diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000000000000000000000000000000000..42f1e597e9581061214a7129e6b887998dd118bb
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+custom: ['https://www.microsoft.com/en-us/research/']
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4aaec0695ebc79962b7115cfbbc7143adc4efae9..6b8e95637915fa324751ff1e2e5d56bb1f8a4eba 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -16,14 +16,14 @@ jobs:
         include: 
         - operating-system: macos-latest
           MVN_COMMAND: mvn -Dmaven.test.skip=true
-          GITHUB_RELEASE_NAME: The Aristotle release
-          TOOLBOX_PRODUCT_ZIP: TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip
+          GITHUB_RELEASE_NAME: The Clarke release
+          TOOLBOX_PRODUCT_ZIP: TLAToolbox-1.8.0-macosx.cocoa.x86_64.zip
 
         - operating-system: ubuntu-latest
           MVN_COMMAND: xvfb-run mvn -Dtest.skip=true -Dmaven.test.failure.ignore=true 
-          GITHUB_RELEASE_NAME: The Aristotle release
-          TOOLBOX_PRODUCT_ZIP: TLAToolbox-1.7.0-linux.gtk.x86_64.zip
-          TOOLBOX_PRODUCT_ZIP_WIN: TLAToolbox-1.7.0-win32.win32.x86_64.zip
+          GITHUB_RELEASE_NAME: The Clarke release
+          TOOLBOX_PRODUCT_ZIP: TLAToolbox-1.8.0-linux.gtk.x86_64.zip
+          TOOLBOX_PRODUCT_ZIP_WIN: TLAToolbox-1.8.0-win32.win32.x86_64.zip
 
     steps:
 
@@ -93,7 +93,7 @@ jobs:
       ## Build TLC and Toolbox (logger reduces verbosity).
       ##
     - name: Build with Maven (Linux)
-      run: ${{ matrix.MVN_COMMAND }} -Pcodesigning -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -fae -B verify --file pom.xml
+      run: ${{ matrix.MVN_COMMAND }} -Pcodesigning -Dtycho.disableP2Mirrors=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -fae -B verify --file pom.xml
 
       ##
       ## Create signed apt repository out of Linux Toolbox zip.
@@ -139,9 +139,9 @@ jobs:
            curl -sS -X DELETE -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/assets/$ID
            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://uploads.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets?name=${{matrix.TOOLBOX_PRODUCT_ZIP}} --upload-file toolbox/org.lamport.tla.toolbox.product.product/target/products/${{matrix.TOOLBOX_PRODUCT_ZIP}}
 
-           ID=$(curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets --header "Content-Type: application/json"  | jq '.[]| select(.name == "TLAToolbox-1.7.0.deb") | .id')
+           ID=$(curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets --header "Content-Type: application/json"  | jq '.[]| select(.name == "TLAToolbox-1.8.0.deb") | .id')
            curl -sS -X DELETE -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/assets/$ID
-           curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://uploads.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0.deb --upload-file toolbox/org.lamport.tla.toolbox.product.product/target/TLAToolbox-1.7.0-linux.gtk.amd64.deb
+           curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://uploads.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.8.0.deb --upload-file toolbox/org.lamport.tla.toolbox.product.product/target/TLAToolbox-1.8.0-linux.gtk.amd64.deb
 
            ID=$(curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets --header "Content-Type: application/json"  | jq '.[]| select(.name == "p2repository.zip") | .id')
            curl -sS -X DELETE -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/assets/$ID
@@ -150,23 +150,26 @@ jobs:
            ## Generate changelog
            cd general/docs/changelogs
            ## Append sha1 sum to changelog (last line of changelog has the table header).
-           echo "$(sha1sum ../../../tlatools/org.lamport.tlatools/dist/tla2tools.jar | cut -f 1 -d " ")|tla2tools.jar"  >> ch1_7_0.md
-           echo "$(sha1sum ../../../toolbox/org.lamport.tla.toolbox.product.product/target/products/${{matrix.TOOLBOX_PRODUCT_ZIP_WIN}} | cut -f 1 -d " ")|${{matrix.TOOLBOX_PRODUCT_ZIP_WIN}}" >> ch1_7_0.md
-           echo "$(sha1sum ../../../toolbox/org.lamport.tla.toolbox.product.product/target/products/${{matrix.TOOLBOX_PRODUCT_ZIP}} | cut -f 1 -d " ")|${{matrix.TOOLBOX_PRODUCT_ZIP}}" >> ch1_7_0.md
-           echo "TBD|macOS" >> ch1_7_0.md
+           echo "$(sha1sum ../../../tlatools/org.lamport.tlatools/dist/tla2tools.jar | cut -f 1 -d " ")|tla2tools.jar"  >> ch1_8_0.md
+           echo "$(sha1sum ../../../toolbox/org.lamport.tla.toolbox.product.product/target/products/${{matrix.TOOLBOX_PRODUCT_ZIP_WIN}} | cut -f 1 -d " ")|${{matrix.TOOLBOX_PRODUCT_ZIP_WIN}}" >> ch1_8_0.md
+           echo "$(sha1sum ../../../toolbox/org.lamport.tla.toolbox.product.product/target/products/${{matrix.TOOLBOX_PRODUCT_ZIP}} | cut -f 1 -d " ")|${{matrix.TOOLBOX_PRODUCT_ZIP}}" >> ch1_8_0.md
+           echo "TBD|macOS" >> ch1_8_0.md
            ## Two above as one-liner without intermediate file.
-           $(jq -n --argjson changelog "$(cat ch1_7_0.md | jq  --raw-input --slurp .)" -f gh-1_7_0.jq > gh-1_7_0.json)
+           $(jq -n --argjson changelog "$(cat ch1_8_0.md | jq  --raw-input --slurp .)" -f gh-1_8_0.jq > gh-1_8_0.json)
            ## Update draft release with latest changelog in case it changed.
            ## https://developer.github.com/v3/repos/releases/#edit-a-release
-           curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE -d @gh-1_7_0.json -X PATCH --header "Content-Type: application/json"
+           curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE -d @gh-1_8_0.json -X PATCH --header "Content-Type: application/json"
 
     - name: Upload assets to INRIA
       if: matrix.operating-system == 'ubuntu-latest'
       run: |
+           ## Thanks Apple for your walled garden! Delete the *unsigned* Toolbox maxOS zip created by the Linux/ubuntu job.  The macOS job below creates and rsyncs a *signed* zip file.
+           rm toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.8.0-macosx.cocoa.x86_64.zip
            ## Upload p2 and apt repository to INRIA machine.
-           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" -av tlatools/org.lamport.tlatools/dist/tla2tools.jar github@upload.tlapl.us:dist/
-           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" -av toolbox/org.lamport.tla.toolbox.product.product/target/products/*.zip github@upload.tlapl.us:products/
-           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" -av toolbox/org.lamport.tla.toolbox.product.product/target/repository/ github@upload.tlapl.us:repository/
+           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" --delete -av tlatools/org.lamport.tlatools/dist/tla2tools.jar github@upload.tlapl.us:dist/
+           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" --delete -av toolbox/org.lamport.tla.toolbox.product.product/target/products/*.zip github@upload.tlapl.us:products/
+           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" --delete -av toolbox/org.lamport.tla.toolbox.product.product/target/repository/ github@upload.tlapl.us:repository/
+           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" --delete -av toolbox/org.lamport.tla.toolbox.doc/html/ github@upload.tlapl.us:doc/
 
     ## 
     ## Update all git tags to make the download urls work, i.e.
@@ -179,8 +182,7 @@ jobs:
       run: |
         git config --local user.email "tlaplus-action@github.com"
         git config --local user.name "TLA+ GitHub Action"
-        git tag -f nightly
-        git tag -f v1.7.0
+        git tag -f v1.8.0
         git push https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git --follow-tags --tags --force
 
       ##
@@ -195,6 +197,17 @@ jobs:
            ## github.xml replaces the Tycho pom.xml with a specific one to publish tla2tools.jar.
            mvn deploy:deploy-file -Dgithub.packages.username=${{ github.actor }} -Dgithub.packages.password=${{ secrets.GITHUB_TOKEN }} -Dfile=dist/tla2tools.jar -Durl=https://maven.pkg.github.com/${{ github.repository }} -DpomFile=github.xml -DrepositoryId=github -f github.xml
 
+      ##
+      ## Trigger build of CommunityModule as integration tests.
+      ##
+    - name: Integration tests with CommunityModules
+      if: matrix.operating-system == 'ubuntu-latest'
+      uses: peter-evans/repository-dispatch@v1
+      with:
+          token: ${{ secrets.COMMUNITYMODULES_ACCESS_TOKEN }}
+          repository: tlaplus/CommunityModules
+          event-type: tlaplus-dispatch
+
       ################################# macOS #################################
 
       ##
@@ -235,10 +248,13 @@ jobs:
            security set-key-partition-list -S apple-tool:,apple: -s -k ${{ secrets.APPLE_CERT_PASSWORD }} tla
            ## Unzip, sign, and zip up the TLA Toolbox.
            unzip toolbox/org.lamport.tla.toolbox.product.product/target/products/${{ matrix.TOOLBOX_PRODUCT_ZIP }}
-           codesign --keychain tla --deep --display --entitlements toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist --options runtime --verbose=4 -h -f -s "Developer ID Application: M K (3PCM4M3RWK)" "TLA+ Toolbox.app"
+           codesign --force --identifier org.lamport.tla.toolbox.product.product --keychain tla --deep --display --entitlements toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist --options runtime --verbose=4 -h -f -s "Developer ID Application: M K (3PCM4M3RWK)" "TLA+ Toolbox.app"
            ditto -ck --sequesterRsrc --keepParent "TLA+ Toolbox.app" ${{ matrix.TOOLBOX_PRODUCT_ZIP }}
+           xcrun altool --notarize-app --primary-bundle-id "org.lamport.tla.toolbox.product.product" --username "${{secrets.APPLE_CODESIGN_DEVELOPER_ID}}" --password "${{secrets.APPLE_CODESIGN_DEVELOPER_PASSWORD}}" --file "${{ matrix.TOOLBOX_PRODUCT_ZIP }}"
            ## Upload signed TLAToolbox zip to Github release.
            DRAFT_RELEASE=$(curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases --header "Content-Type: application/json" | jq '.[]| select(.name=="${{ matrix.GITHUB_RELEASE_NAME }}") | .id')
            ID=$(curl -sS -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets --header "Content-Type: application/json"  | jq '.[]| select(.name == "${{ matrix.TOOLBOX_PRODUCT_ZIP }}") | .id')
            curl -sS -X DELETE -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://api.github.com/repos/${{ github.repository }}/releases/assets/$ID
            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${{secrets.GITHUB_TOKEN}}" https://uploads.github.com/repos/${{ github.repository }}/releases/$DRAFT_RELEASE/assets?name=${{ matrix.TOOLBOX_PRODUCT_ZIP }} --upload-file ${{ matrix.TOOLBOX_PRODUCT_ZIP }}
+           ## Upload p2 and apt repository to INRIA machine.
+           rsync -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa" --delete -av ${{ matrix.TOOLBOX_PRODUCT_ZIP }} github@upload.tlapl.us:products/
diff --git a/.jenkins.groovy b/.jenkins.groovy
index 77b56fdc4e11a67677951f235e006f33985c369b..5ca9fabaf89a34cfed99379c5e607fda966cd66f 100644
--- a/.jenkins.groovy
+++ b/.jenkins.groovy
@@ -46,7 +46,7 @@ for (x in labels) {
 		      }
 	   	      // the macosx zip on the master node to have it signed with the Apple certificate on macosx.  However, only master
 		      // has the lamport certificate to sign the individual toolbox bundles.
-		      stash includes: 'toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip', name: 'toolbox'
+		      stash includes: 'toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip', name: 'toolbox'
                     } else {
 			  withMaven(
 			    // Maven installation declared in the Jenkins "Global Tool Configuration"
@@ -153,7 +153,7 @@ node ('master') {
    }
    
    stage('RenderChangelog') { // Render the github flavord markdown to html
-       sh 'grip --context=tlaplus/tlaplus --export ${WORKSPACE}/general/docs/changelogs/ch1_7_0.md ${WORKSPACE}/general/docs/changelogs/changelog.html'
+       sh 'grip --context=tlaplus/tlaplus --export ${WORKSPACE}/general/docs/changelogs/ch1_7_1.md ${WORKSPACE}/general/docs/changelogs/changelog.html'
    }
 }
 
@@ -162,11 +162,11 @@ node ('macos') {
         sh 'rm -rf *'
         unstash 'toolbox'
         sh 'ls -lah'
-        sh 'unzip toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip'
+        sh 'unzip toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip'
         sh 'codesign -f -s "Developer ID Application: M K (3PCM4M3RWK)" -v "TLA+ Toolbox.app" --deep'
-        sh 'ditto -ck --sequesterRsrc --keepParent "TLA+ Toolbox.app" TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip'
-        sh 'mv TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip toolbox/org.lamport.tla.toolbox.product.product/target/products/'
-        stash includes: 'toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip', name: 'signed'
+        sh 'ditto -ck --sequesterRsrc --keepParent "TLA+ Toolbox.app" TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip'
+        sh 'mv TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip toolbox/org.lamport.tla.toolbox.product.product/target/products/'
+        stash includes: 'toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip', name: 'signed'
     }
 }
 
@@ -188,21 +188,21 @@ node ('master') {
            cd ${WORKSPACE}/general/docs/changelogs
 
            ## Append sha1 sum to changelog (last line of changelog has the table header).
-           echo "$(sha1sum ${WORKSPACE}/tlatools/org.lamport.tlatools/dist/tla2tools.jar | cut -f 1 -d " ")|tla2tools.jar"  >> ch1_7_0.md
-           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-win32.win32.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.0-win32.win32.x86_64.zip" >> ch1_7_0.md
-           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip" >> ch1_7_0.md     
-           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-linux.gtk.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.0-linux.gtk.x86_64.zip" >> ch1_7_0.md
+           echo "$(sha1sum ${WORKSPACE}/tlatools/org.lamport.tlatools/dist/tla2tools.jar | cut -f 1 -d " ")|tla2tools.jar"  >> ch1_7_1.md
+           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-win32.win32.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.1-win32.win32.x86_64.zip" >> ch1_7_1.md
+           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip" >> ch1_7_1.md     
+           echo "$(sha1sum ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-linux.gtk.x86_64.zip | cut -f 1 -d " ")|TLAToolbox-1.7.1-linux.gtk.x86_64.zip" >> ch1_7_1.md
            
            ## Two above as one-liner without intermediate file.
-           $(jq -n --argjson changelog "$(cat ch1_7_0.md | jq  --raw-input --slurp .)" -f gh-1_7_0.jq > gh-1_7_0.json)
+           $(jq -n --argjson changelog "$(cat ch1_7_1.md | jq  --raw-input --slurp .)" -f gh-1_7_1.jq > gh-1_7_1.json)
 
            ## Get id of existing draft release with given name.
-           DRAFT_RELEASE=$(curl -sS -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/tlaplus/tlaplus/releases --header "Content-Type: application/json" | jq '.[]| select(.draft==true and .name=="The Aristotle release") | .id')
+           DRAFT_RELEASE=$(curl -sS -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/tlaplus/tlaplus/releases --header "Content-Type: application/json" | jq '.[]| select(.draft==true and .name=="The Brontinus release") | .id')
            echo $DRAFT_RELEASE
 
            ## Update draft release with latest changelog in case it changed.
            ## https://developer.github.com/v3/repos/releases/#edit-a-release
-           curl -sS -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE -d @gh-1_7_0.json -X PATCH --header "Content-Type: application/json"
+           curl -sS -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE -d @gh-1_7_1.json -X PATCH --header "Content-Type: application/json"
 
            ## Remove old assets otherwise upload below will error.
            ASSETS=$(curl -sS -H "Authorization: token ${GITHUB_TOKEN}" https://api.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets --header "Content-Type: application/json" | jq '.[]| .id')
@@ -216,15 +216,15 @@ node ('master') {
             ## tla2tools.jar
             curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=tla2tools.jar --upload-file ${WORKSPACE}/tlatools/org.lamport.tlatools/dist/tla2tools.jar
             ## macOS
-            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-macosx.cocoa.x86_64.zip
+            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-macosx.cocoa.x86_64.zip
             ## win32
-            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0-win32.win32.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-win32.win32.x86_64.zip
+            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.1-win32.win32.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-win32.win32.x86_64.zip
             ## Linux
-            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0-linux.gtk.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-linux.gtk.x86_64.zip
+            curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.1-linux.gtk.x86_64.zip --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.1-linux.gtk.x86_64.zip
             ## deb
-            #curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0-linux.gtk.amd64.deb --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/repository/TLAToolbox-1.7.0-linux.gtk.amd64.deb
+            #curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.1-linux.gtk.amd64.deb --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/repository/TLAToolbox-1.7.1-linux.gtk.amd64.deb
             ## RPM
-            #curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.0-linux.gtk.amd64.rpm --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLA\\+Toolbox-1.7.0~*.x86_64.rpm
+            #curl -s -X POST -H "Content-Type: application/zip" -H "Authorization: token ${GITHUB_TOKEN}" https://uploads.github.com/repos/tlaplus/tlaplus/releases/$DRAFT_RELEASE/assets?name=TLAToolbox-1.7.1-linux.gtk.amd64.rpm --upload-file ${WORKSPACE}/toolbox/org.lamport.tla.toolbox.product.product/target/products/TLA\\+Toolbox-1.7.1~*.x86_64.rpm
          '''
         }
    }
diff --git a/.travis.yml b/.travis.yml
index a1f330a3fa5cc3f8ae947c11cb1b76e960260ca0..443b37103ea4a353893cfb312a6864b507be660f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
  sudo: required
  dist: trusty
  language: java
- jdk: openjdk11
+ jdk: openjdk14
  install: true
 
  env:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 54e0962c72add72a009547851f11dfc66ab2dff0..5acb58f1e9210e0905085db5956908280cb01ca5 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -31,7 +31,7 @@ The Toolbox's nightly builds are also made available as a [cask](https://github.
 
 ```bash
 $ brew tap homebrew/cask-versions
-$ brew install tlaplus-toolbox-nightly
+$ brew install tla-plus-toolbox-nightly
 ```
 
 Quality Metrics
diff --git a/general/docs/changelogs/ch1_7_1.md b/general/docs/changelogs/ch1_7_1.md
new file mode 100644
index 0000000000000000000000000000000000000000..0929f70ea53579a5194546f97b6040b3a84f23fe
--- /dev/null
+++ b/general/docs/changelogs/ch1_7_1.md
@@ -0,0 +1,55 @@
+### <a href="#latest-tla-files">Click here to jump to the downloads at the bottom of this page!</a>
+
+### Changelog
+The high level changelog is available at http://research.microsoft.com/en-us/um/people/lamport/tla/toolbox.html#release. The [1.7.1 milestone](https://github.com/tlaplus/tlaplus/issues?q=is%3Aissue+milestone%3A1.7.1+is%3Aclosed) lists all completed issues.
+
+### Additional _noteworthy_ changes
+
+#### Tools
+
+##### Feature
+* Improve some of TLC's error messages. e328ae9842620ac028c0ebd2f83033dcb5dbe506
+* Add `TLC!TLCGet("generated")` that equals the number of states generated. fa766305094b698eab3338ed917bd1c8b4542039
+* Prototype: Support multiple TLA+ modules in a single .tla file. 505e073ffe9b9eee8ebe4b375b2dad5f402fa36b
+* Programatically stop simulation with `TLC!TLCSet("exit", TRUE)`. d62b289af42edb2026cda423cc56bb0fe8477b52
+* Prototype: Add an interactive TLA+ REPL. 97afa3c6952e343ee2409366a668ba12afceeef4 ([Screencast](https://asciinema.org/a/3lsDwbmVG0iyZHJ3RbhNycQS0))
+* Drop intermittent stuttering steps from error trace in simulation mode. cfcfafbd964bfef46064fdfeb10c4fd2e4c1839e
+* Return non-zero error codes from SANY on more errors. 10f77cf67f666cda315eaa7337c6cc256d97c8b6
+* [ALIAS](https://github.com/tlaplus/tlaplus/issues/485). f5306c601a2133ebffad6224db93c16663fe5ff5
+* [POSTCONDITION](https://lamport.azurewebsites.net/tla/current-tools.pdf). ced9269895aa6b760fa7d7a35fa61b43eb2a9a0a e9be5d0fa41ba38879b2e92307853a3ad9855542 be394f889f8e22c74e491638c54997d00ee03c88
+* Prototype: Visualize *action* coverage in simulation mode. 3d3259da2d48db44275074cbf6874d477752c59d 3913dd181a5cac755acb1577a6503b6f5d443137
+* Report number of and mean/variance/standard deviation of length of generated traces in simulation mode. d175e317c102b1825e3f9b436e34ed477e5309c9 7a3bcb0302edfc5b2f720002a8561bc5a8552c47
+* Let users set the maximum number of traces to generate in simulation mode. a969d55dbda8cf5750300d82bbdbc37f6df4712f
+
+##### Bugfix
+* TLC shows no error *trace* for violations of `TLC!Assert`, ... (regression in 1.7.0). 19757bdd6aadf7c0e371c40bf591f0d84f12a592
+* State graph visualization in dot format broken for specs with instantiation. a8fc5b1bf6dfb29679848fdd6f15f3c3ada9efa2
+* Simulation mode ignores ASSUMPTIONS. https://github.com/tlaplus/tlaplus/issues/496
+* `TLC!RandomElement` breaks error trace re-construction in simulation mode. e0d64f686dae1aec22728b1acfa40b1a554a7267
+* NoClassDefError when running TLC on Java 1.8. e6bd13ec971c1fa2a165b7068bc0091ab6c510cb
+* Replace custom implementation of (heap) sort in TeX with java.utils.Arrays#sort https://github.com/tlaplus/tlaplus/issues/539 8b52d238eb9f17df98dd795dbb589d9fba4a5822
+
+#### Toolbox
+
+##### Feature
+* Open a Toolbox spec, module, or TLC model in the file manager such as Windows Explorer, Finder, or Nautilus.
+* Proof-of-concept: Remove GraphViz dependency by rendering state graph visualization with embedded browser (macOS & Linux only). 478d8569e84dfb31b982a500947def5c9c813b97
+* Bundle [CommunityModules](https://github.com/tlaplus/CommunityModules) as part of the Toolbox. 3beb7116b97def46fc57fa777c8bb533803ee025
+* Upgrade Eclipse foundation to its 2020-06 release for better HiDPI support. dc67692e17cb75866e7005358255d9849870fc4a
+* Set [`ALIAS`](https://github.com/tlaplus/tlaplus/issues/485) and `POSTCONDITION` in Toolbox's model editor. e8054e8eb913bc87c336882c7c6e1f8565ada1e9 d3cfde5a37d943b95a354d40ad60b8bef07411b2
+* Re-worked PlusCal/TLA+ divergence warning (please manually remove [1.7.0 markers](https://github.com/tlaplus/tlaplus/releases/tag/v1.7.0)). f1cf514c3b334b0968a5ac7fdf14d3e93905b14c e434e139adebf73ed8f9470117031f1ad4b749df 7c61d1a70f03fe4e54142f59487af90745386b74
+
+##### Bugfix
+* Quickly open spec or model in OS file manager. 06280a4f346279a69bb458636d290a027041f006
+* Do not enter `Spec` as next-state relation when restarting model-checking from a state in the error-trace. 7f50021048155a3d085d5d482eede37f09fbf9e5
+* Multiline trace expressions fail to parse in Toolbox. defe0c74915b1c27c6af2fb55c8163f3574c8918
+
+### Contributors
+
+We are grateful for contributions to this release from: [William Schultz](https://github.com/will62794), [Paulo Rafael Feodrippe](https://github.com/pfeodrippe), and [zmatti](https://github.com/zmatti).
+
+<a id="latest-tla-files"/>
+
+### Checksums
+sha1sum|file
+------------ | -------------
diff --git a/general/docs/changelogs/ch1_8_0.md b/general/docs/changelogs/ch1_8_0.md
new file mode 100644
index 0000000000000000000000000000000000000000..b7d2bb7879899259af8ce82551b1799edd2f7382
--- /dev/null
+++ b/general/docs/changelogs/ch1_8_0.md
@@ -0,0 +1,55 @@
+### <a href="#latest-tla-files">Click here to jump to the downloads at the bottom of this page!</a>
+
+### Changelog
+The high level changelog is available at http://research.microsoft.com/en-us/um/people/lamport/tla/toolbox.html#release. The [1.8.0 milestone](https://github.com/tlaplus/tlaplus/issues?q=is%3Aissue+milestone%3A1.7.1+is%3Aclosed) lists all completed issues.
+
+### Additional _noteworthy_ changes
+
+#### Tools
+
+##### Feature
+* Improve some of TLC's error messages. e328ae9842620ac028c0ebd2f83033dcb5dbe506
+* Add `TLC!TLCGet("generated")` that equals the number of states generated. fa766305094b698eab3338ed917bd1c8b4542039
+* Prototype: Support multiple TLA+ modules in a single .tla file. 505e073ffe9b9eee8ebe4b375b2dad5f402fa36b
+* Programatically stop simulation with `TLC!TLCSet("exit", TRUE)`. d62b289af42edb2026cda423cc56bb0fe8477b52
+* Prototype: Add an interactive TLA+ REPL. 97afa3c6952e343ee2409366a668ba12afceeef4 ([Screencast](https://asciinema.org/a/3lsDwbmVG0iyZHJ3RbhNycQS0))
+* Drop intermittent stuttering steps from error trace in simulation mode. cfcfafbd964bfef46064fdfeb10c4fd2e4c1839e
+* Return non-zero error codes from SANY on more errors. 10f77cf67f666cda315eaa7337c6cc256d97c8b6
+* [ALIAS](https://github.com/tlaplus/tlaplus/issues/485). f5306c601a2133ebffad6224db93c16663fe5ff5
+* [POSTCONDITION](https://lamport.azurewebsites.net/tla/current-tools.pdf). ced9269895aa6b760fa7d7a35fa61b43eb2a9a0a e9be5d0fa41ba38879b2e92307853a3ad9855542 be394f889f8e22c74e491638c54997d00ee03c88
+* Prototype: Visualize *action* coverage in simulation mode. 3d3259da2d48db44275074cbf6874d477752c59d 3913dd181a5cac755acb1577a6503b6f5d443137
+* Report number of and mean/variance/standard deviation of length of generated traces in simulation mode. d175e317c102b1825e3f9b436e34ed477e5309c9 7a3bcb0302edfc5b2f720002a8561bc5a8552c47
+* Let users set the maximum number of traces to generate in simulation mode. a969d55dbda8cf5750300d82bbdbc37f6df4712f
+
+##### Bugfix
+* TLC shows no error *trace* for violations of `TLC!Assert`, ... (regression in 1.7.0). 19757bdd6aadf7c0e371c40bf591f0d84f12a592
+* State graph visualization in dot format broken for specs with instantiation. a8fc5b1bf6dfb29679848fdd6f15f3c3ada9efa2
+* Simulation mode ignores ASSUMPTIONS. https://github.com/tlaplus/tlaplus/issues/496
+* `TLC!RandomElement` breaks error trace re-construction in simulation mode. e0d64f686dae1aec22728b1acfa40b1a554a7267
+* NoClassDefError when running TLC on Java 1.8. e6bd13ec971c1fa2a165b7068bc0091ab6c510cb
+* Replace custom implementation of (heap) sort in TeX with java.utils.Arrays#sort https://github.com/tlaplus/tlaplus/issues/539 8b52d238eb9f17df98dd795dbb589d9fba4a5822
+
+#### Toolbox
+
+##### Feature
+* Open a Toolbox spec, module, or TLC model in the file manager such as Windows Explorer, Finder, or Nautilus.
+* Proof-of-concept: Remove GraphViz dependency by rendering state graph visualization with embedded browser (macOS & Linux only). 478d8569e84dfb31b982a500947def5c9c813b97
+* Bundle [CommunityModules](https://github.com/tlaplus/CommunityModules) as part of the Toolbox. 3beb7116b97def46fc57fa777c8bb533803ee025
+* Upgrade Eclipse foundation to its 2020-06 release for better HiDPI support. dc67692e17cb75866e7005358255d9849870fc4a
+* Set [`ALIAS`](https://github.com/tlaplus/tlaplus/issues/485) and `POSTCONDITION` in Toolbox's model editor. e8054e8eb913bc87c336882c7c6e1f8565ada1e9 d3cfde5a37d943b95a354d40ad60b8bef07411b2
+* Re-worked PlusCal/TLA+ divergence warning (please manually remove [1.7.0 markers](https://github.com/tlaplus/tlaplus/releases/tag/v1.7.0)). f1cf514c3b334b0968a5ac7fdf14d3e93905b14c e434e139adebf73ed8f9470117031f1ad4b749df 7c61d1a70f03fe4e54142f59487af90745386b74
+
+##### Bugfix
+* Quickly open spec or model in OS file manager. 06280a4f346279a69bb458636d290a027041f006
+* Do not enter `Spec` as next-state relation when restarting model-checking from a state in the error-trace. 7f50021048155a3d085d5d482eede37f09fbf9e5
+* Multiline trace expressions fail to parse in Toolbox. defe0c74915b1c27c6af2fb55c8163f3574c8918
+
+### Contributors
+
+We are grateful for contributions to this release from: [William Schultz](https://github.com/will62794), [Paulo Rafael Feodrippe](https://github.com/pfeodrippe), and [zmatti](https://github.com/zmatti).
+
+<a id="latest-tla-files"/>
+
+### Checksums
+sha1sum|file
+------------ | -------------
diff --git a/general/docs/changelogs/gh-1_7_1.jq b/general/docs/changelogs/gh-1_7_1.jq
new file mode 100644
index 0000000000000000000000000000000000000000..7b6a22220184f21c6fc4acfed206eb97e25123c5
--- /dev/null
+++ b/general/docs/changelogs/gh-1_7_1.jq
@@ -0,0 +1,7 @@
+{
+  "tag_name": "v1.7.1",
+  "name": "The Brontinus release",
+  "draft": false,
+  "prerelease": true,
+  "body": $changelog
+}
\ No newline at end of file
diff --git a/general/docs/changelogs/gh-1_8_0.jq b/general/docs/changelogs/gh-1_8_0.jq
new file mode 100644
index 0000000000000000000000000000000000000000..6ecff55248495c89b7d3133f4fe57991f6513519
--- /dev/null
+++ b/general/docs/changelogs/gh-1_8_0.jq
@@ -0,0 +1,7 @@
+{
+  "tag_name": "v1.8.0",
+  "name": "The Clarke release",
+  "draft": false,
+  "prerelease": true,
+  "body": $changelog
+}
\ No newline at end of file
diff --git a/general/docs/contributions.md b/general/docs/contributions.md
index 46c2b4203275b8af1aec917b52d799236c8f5a7c..3594ee03e636bbe00bdb88b564fc32a36c7868f5 100644
--- a/general/docs/contributions.md
+++ b/general/docs/contributions.md
@@ -39,6 +39,9 @@ Reasoning about TLA+ sequences currently mainly relies on the lemmas provided in
 
 Most conjectures that users attempt to prove during proof development are in fact not valid. For example, hypotheses needed to prove a step are not provided to the prover, the invariant may not be strong enough etc. When this is the case, the back-end provers time out but do not provide any useful information to the user. The objective of this work is to connect a model generator such as [Nunchaku](https://github.com/nunchaku-inria/nunchaku) to TLAPS that could provide an explanation to the user why the proof obligation is not valid. The main difficulty will be defining a translation from a useful sublanguage of TLA+ set-theoretic expressions to the input language of Nunchaku, which resembles a functional programming language.
 
+#### Warning about unexpanded definitions (difficulty: moderate) (skills: OCaml, TLA+)
+
+A common reason for a proof not to succeed is the failure to tell the prover to expand a definition that needs to be expanded (see section 6.1 of [Proving Safety Properties](https://lamport.azurewebsites.net/tla/proving-safety.pdf) for an example).  In some cases, simple heuristics could indicate that a definition needs to be expanded--for example, if a goal contains a symbol that does not appear in any formula being used to prove it.  The objective is to find and implement such heuristics in a command that the user can invoke to suggest what definitions may need to be expanded.  We believe that this would be an easy way to save users a lot of--especially beginning users.
 
 TLA Toolbox
 -----------
diff --git a/general/ide/README.md b/general/ide/README.md
index 4b131f0cb5b4889ed1f70346cf894f8161acde5a..daf57b27a3b781990d8399963b31ae42ff4590b1 100644
--- a/general/ide/README.md
+++ b/general/ide/README.md
@@ -2,13 +2,13 @@ Eclipse Oomph is a tool to simplify and automate the setup of Eclipse developmen
 
 [A screencast is available that captures the written instructions below.](https://vimeo.com/190224035)
 
-0. Requires a recent - at the time of writing this is 13 - JDK (Java Development Environment) - AdoptOpenJDK is recommended.
+0. Requires a recent - at the time of writing this is 14 - JDK (Java Development Environment) - AdoptOpenJDK is recommended.
 1. Install the Oomph Eclipse installer from [https://wiki.eclipse.org/Eclipse_Installer](https://wiki.eclipse.org/Eclipse_Installer)
 2. Start the downloaded Oomph installer
 3. Switch to "Advanced Mode" by clicking the button in the right upper corner depicted with three horizontal lines
 4. Select "Eclipse Platform" on the Product list (expand "Eclipse.org" node)
-  1. Choose "2019-09" from the Product Version combobox at the bottom ![Choose Platform](https://raw.githubusercontent.com/lemmy/tlaplus/master/general/ide/images/00_PlatformSelection.png)
-     1. You can try to use a more recent version of Eclipse, however if you run into troubles during the installation and set-up, choose "2019-09" instead.
+  1. Choose "2020-03" from the Product Version combobox at the bottom ![Choose Platform](https://raw.githubusercontent.com/lemmy/tlaplus/master/general/ide/images/00_PlatformSelection.png)
+     1. You can try to use a more recent version of Eclipse, however if you run into troubles during the installation and set-up, choose "2020-03" instead.
 5. On the next screen, expand "Github Project" in the tree and select the check-box left to "TLA+" 
   1. Verify that "TLA+" shows up under "Project" at the bottom table and that the "Master" stream is selected ![Chose Project](https://raw.githubusercontent.com/lemmy/tlaplus/master/general/ide/images/01_ProjectSelection.png)
 6. On the next page, select whether to use anonymous Github access (read-only) from the "TLA+ Github Repository" dropdown list ![Chose anonymous access](https://raw.githubusercontent.com/lemmy/tlaplus/master/general/ide/images/02_Variables.png)
diff --git a/general/ide/TLA.setup b/general/ide/TLA.setup
index ab94371f5e86ded713abafa362c359114632652a..3e9f24655f3fcc8acf043fa3ac90b0608e8217c8 100644
--- a/general/ide/TLA.setup
+++ b/general/ide/TLA.setup
@@ -63,8 +63,6 @@
         name="org.eclipse.swtbot.feature.group"/>
     <requirement
         name="org.eclipse.ajdt.feature.group"/>
-    <requirement
-        name="org.eclipse.m2e.feature.feature.group"/>
     <requirement
         name="org.eclipse.pde.feature.group"/>
     <requirement
@@ -80,15 +78,15 @@
     <repository
         url="http://download.eclipse.org/technology/swtbot/releases/latest/"/>
     <repository
-        url="http://download.eclipse.org/tools/ajdt/48/dev/update/"/>
+        url="http://download.eclipse.org/tools/ajdt/410/dev/update/"/>
     <repository
         url="http://download.eclipse.org/technology/m2e/releases/"/>
     <repository
         url="http://download.eclipse.org/e4/snapshots/org.eclipse.e4.tools/latest/"/>
     <repository
-        url="jar:https://dl.bintray.com/abstratt-oss/abstratt-oss/com/abstratt/eclipsegraphviz/com.abstratt.eclipsegraphviz.repository/2.5.201812/com.abstratt.eclipsegraphviz.repository-2.5.201812.zip!/"/>
+        url="jar:https://raw.githubusercontent.com/tlaplus/tlaplus/master/toolbox/org.lamport.tla.toolbox.product.product/cachedTargetPlatform/com.abstratt.eclipsegraphviz.repository-2.5.201812.zip!/"/>
     <repository
-        url="https://download.eclipse.org/rcptt/release/2.4.3/repository/"/>
+        url="https://download.eclipse.org/rcptt/release/2.5.1/repository/"/>
     <repository
         url="http://andrei.gmxhome.de/eclipse/"/>
     <description>Install the tools needed in the IDE to work with the source code for ${scope.project.label}</description>
diff --git a/pom.xml b/pom.xml
index 30f092d39260c9ddb0034d1d8999ab11d9ef4678..159cb3b4daf5dfb58a41be6b3ce4a542d2705869 100644
--- a/pom.xml
+++ b/pom.xml
@@ -78,7 +78,7 @@
 		<module>toolbox/org.lamport.tla.toolbox.product.product</module>
 
 		<!-- Finally run UI tests on fully built product (AUT) -->
-		<module>toolbox/org.lamport.tla.toolbox.product.uitest</module>
+<!--		<module>toolbox/org.lamport.tla.toolbox.product.uitest</module>-->
 	</modules>
 
 	<!-- tycho requires maven >= 3.0 -->
@@ -92,7 +92,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 
 		<!-- https://wiki.eclipse.org/Tycho/Release_Notes/1.4 -->
-		<tycho-version>1.5.0</tycho-version>
+		<tycho-version>1.5.1</tycho-version>
 
 		<!-- no default here -->
 		<tycho.test.vm.argline>-Xmx500m -Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n</tycho.test.vm.argline>
@@ -123,7 +123,7 @@
 		<!-- Align toolbox.version with the version in
 			 org.lamport.tla.toolbox.product.product.product
 			 product.version. -->
-		<toolbox.version>1.7.0</toolbox.version>
+		<toolbox.version>1.8.0</toolbox.version>
 
 		<!-- This is used in org.lamport.tla.toolbox.product.uitest but
 			has been placed at the top level should it need be referenced
@@ -264,6 +264,13 @@
 				<artifactId>target-platform-configuration</artifactId>
 				<version>${tycho-version}</version>
 				<configuration>
+				    <!-- Some dependencies such as jline referenced by org.lamport.tlatools   -->
+				    <!-- cannot be found in the Toolbox's target platform (TLAToolbox.target) -->
+				    <!-- because the dependencies are not made available in p2 repositories.  -->
+				    <!-- However, sometimes OSGi-fied dependencies are made available on      -->
+				    <!-- Maven Central.  'pomDependencies' makes this build look there too.    -->
+				    <!-- https://wiki.eclipse.org/Tycho/Target_Platform#.22POM_dependencies_consider.22 -->
+                    <pomDependencies>consider</pomDependencies>
 					<!-- recommended: use p2-based target platform resolver -->
 					<resolver>p2</resolver>
 					<ignoreTychoRepositories>true</ignoreTychoRepositories>
diff --git a/tlatools/org.lamport.tlatools/.classpath b/tlatools/org.lamport.tlatools/.classpath
index af7fe93b8e8c46dccdd67e1df4ebf45458fdefc9..50e6030f3ed596552c385c73dc65be29c8ad1643 100644
--- a/tlatools/org.lamport.tlatools/.classpath
+++ b/tlatools/org.lamport.tlatools/.classpath
@@ -17,6 +17,8 @@
 	<classpathentry kind="src" path="test-verify"/>
 	<classpathentry kind="src" path="test-benchmark"/>
 	<classpathentry kind="lib" path="lib/javax.mail/mailapi-1.6.3.jar"/>
+	<classpathentry kind="lib" path="lib/jline/jline-reader-3.14.1.jar"/>
+	<classpathentry kind="lib" path="lib/jline/jline-terminal-3.14.1.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"/>
diff --git a/tlatools/org.lamport.tlatools/META-INF/MANIFEST.MF b/tlatools/org.lamport.tlatools/META-INF/MANIFEST.MF
index 74de04aeca0d4d8ec85c1c5faec1483f3941b356..3031adae80eb6b9f843c714cdf827e220f3868d1 100644
--- a/tlatools/org.lamport.tlatools/META-INF/MANIFEST.MF
+++ b/tlatools/org.lamport.tlatools/META-INF/MANIFEST.MF
@@ -32,7 +32,6 @@ Export-Package: pcal,
  tlc2.tool.impl,
  tlc2.tool.liveness,
  tlc2.tool.management,
- tlc2.tool.other,
  tlc2.tool.queue,
  tlc2.util,
  tlc2.value,
@@ -44,6 +43,8 @@ Eclipse-BundleShape: dir
 Require-Bundle: org.aspectj.runtime;bundle-version="1.6.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,
- org.easymock;bundle-version="2.4.0";resolution:=optional
+ org.easymock;bundle-version="2.4.0";resolution:=optional,
+ org.jline.reader;bundle-version="3.14.1";resolution:=optional,
+ org.jline.terminal;bundle-version="3.14.1";resolution:=optional
 Bundle-ClassPath: .
 Automatic-Module-Name: org.lamport.tlatools
diff --git a/tlatools/org.lamport.tlatools/customBuild.xml b/tlatools/org.lamport.tlatools/customBuild.xml
index 88a386dd0d204a2c4827416405d524f933acd138..d6a9266623d2e6b1e65b37556302244c7c034255 100644
--- a/tlatools/org.lamport.tlatools/customBuild.xml
+++ b/tlatools/org.lamport.tlatools/customBuild.xml
@@ -137,6 +137,8 @@
 			<classpath refid="project.classpath" />
 			<classpath>
 				<pathelement location="lib/javax.mail/mailapi-1.6.3.jar" />
+				<pathelement location="lib/jline/jline-terminal-3.14.1.jar" />
+				<pathelement location="lib/jline/jline-reader-3.14.1.jar" />
 			</classpath>
 		</javac>
 
@@ -249,7 +251,30 @@
 			</patternset>
 		</unzip>
 		<touch file="target/classes/META-INF/javamail.default.address.map"/>
-		
+
+		<unzip src="lib/jline/jline-terminal-3.14.1.jar" dest="${class.dir}">
+			<patternset>
+				<include name="**/*.*"/>
+			</patternset>
+		</unzip>
+
+		<unzip src="lib/jline/jline-reader-3.14.1.jar" dest="${class.dir}">
+			<patternset>
+				<include name="**/*.*"/>
+			</patternset>
+		</unzip>
+
+		<!-- Include a few LICENSE files -->
+		<copy todir="${class.dir}">
+			<fileset dir="lib/jline">
+				<include name="jline-LICENSE.txt" />
+			</fileset>
+			<fileset dir="src/org/apache/commons/math3/">
+				<include name="CommonsMath-NOTICE.txt" />
+				<include name="CommonsMath-LICENSE.txt" />
+			</fileset>
+		</copy>
+
 
 		<!-- create a JAR file for the users -->
 		<mkdir dir="${dist.dir}" />
@@ -275,6 +300,12 @@
 					src/tla2sany/parser/Token.09-09-07,
 					src/tla2sany/parser/TokenMgrError.09-09-07"/>
 			<fileset dir="${doc.dir}" includes="License.txt"/>
+			<fileset dir="${test.class.dir}">
+				<include name="**/tlc2/tool/CommonTestCase*.class" />
+				<include name="**/tlc2/tool/liveness/ModelCheckerTestCase*.class" />
+				<include name="**/tlc2/TestMPRecorder*.class" />
+				<include name="**/util/IsolatedTestCaseRunner*.class" />
+			</fileset>
 			<manifest>
 				<attribute name="Built-By" value="${user.name}" />
 				<attribute name="Build-Tag" value="${env.BUILD_TAG}" />
@@ -285,7 +316,7 @@
 				<!-- 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" />
+				<attribute name="Class-Path" value="CommunityModules-deps.jar CommunityModules.jar" />
 				<!-- Git revision -->
 				<attribute name="X-Git-Branch" value="${git.branch}" />
 				<attribute name="X-Git-Tag" value="${git.tag}" />
@@ -304,7 +335,7 @@
 	</target>
 
 	<!-- Compiles the TLA+ tools *test* code -->
-	<target name="compile-test" unless="test.skip">
+	<target name="compile-test">
 		<!-- compile unit tests -->
 		<mkdir dir="${test.class.dir}" />
 		<javac includeantruntime="false" srcdir="${test.dir}" destdir="${test.class.dir}" debug="true" verbose="false" source="1.8" target="1.8">
@@ -355,6 +386,8 @@
 				<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="lib/jline/jline-terminal-3.14.1.jar" />
+				<pathelement location="lib/jline/jline-reader-3.14.1.jar" />
 				<pathelement location="test-model/UserModuleOverrideFromJar.jar" />
 				<pathelement path="${class.dir}" />
 				<pathelement path="${test.class.dir}" />
@@ -388,6 +421,8 @@
 					<exclude name="**/CommonTestCase.java" />
 					<exclude name="**/ModelCheckerTestCase.java" />
 					<exclude name="**/PCalModelCheckerTestCase.java" />
+					<exclude name="**/TraceExpressionSpecTest.java" />
+					<exclude name="**/TraceExpressionSpecSafetyTest.java" />
 					<exclude name="**/SuiteTestCase.java" />
 					<exclude name="**/SuiteETestCase.java" />
 					<exclude name="**/DistributedTLCTestCase.java" />
@@ -479,6 +514,8 @@
 				<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="lib/jline/jline-terminal-3.14.1.jar" />
+				<pathelement location="lib/jline/jline-reader-3.14.1.jar" />
 				<pathelement location="test-model/UserModuleOverrideFromJar.jar" />
 				<pathelement path="${class.dir}" />
 				<pathelement path="${test.class.dir}" />
@@ -549,6 +586,8 @@
 						<exclude name="**/CommonTestCase.java" />
 						<exclude name="**/ModelCheckerTestCase.java" />
 						<exclude name="**/PCalModelCheckerTestCase.java" />
+						<exclude name="**/TraceExpressionSpecTest.java" />
+						<exclude name="**/TraceExpressionSpecSafetyTest.java" />
 						<exclude name="**/SuiteTestCase.java" />
 						<exclude name="**/SuiteETestCase.java" />
 						<exclude name="**/DistributedTLCTestCase.java" />
@@ -576,6 +615,10 @@
 						<exclude name="**/DetlefsTest.java" />
 						<exclude name="**/StarkMutexTest.java" />
 						<exclude name="**/SimpleMultiProcTest.java" />
+						<!-- The test checks if Tool/FastTool methods are correctly inlined during (long) 
+						     model-checking. It fails with this target but passes with test.  I assume
+						     this is because test-dist records coverage, which, perhaps, interferes with inlining. -->
+						<exclude name="**/InliningTest.java" />
 					</fileset>
 				</batchtest>
 				<batchtest fork="yes" todir="${test.reports}">
diff --git a/tlatools/org.lamport.tlatools/lib/jline/jline-LICENSE.txt b/tlatools/org.lamport.tlatools/lib/jline/jline-LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7e11b67fba770f5177946862f1a798577b3c2977
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/lib/jline/jline-LICENSE.txt
@@ -0,0 +1,35 @@
+Copyright (c) 2002-2018, the original author or authors.
+All rights reserved.
+
+https://opensource.org/licenses/BSD-3-Clause
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/tlatools/org.lamport.tlatools/lib/jline/jline-reader-3.14.1.jar b/tlatools/org.lamport.tlatools/lib/jline/jline-reader-3.14.1.jar
new file mode 100644
index 0000000000000000000000000000000000000000..890a4facf9da24942c79d41230f0675545a3d59f
Binary files /dev/null and b/tlatools/org.lamport.tlatools/lib/jline/jline-reader-3.14.1.jar differ
diff --git a/tlatools/org.lamport.tlatools/lib/jline/jline-terminal-3.14.1.jar b/tlatools/org.lamport.tlatools/lib/jline/jline-terminal-3.14.1.jar
new file mode 100644
index 0000000000000000000000000000000000000000..1efbd6a08ef03e240c5a62b093cd500843236644
Binary files /dev/null and b/tlatools/org.lamport.tlatools/lib/jline/jline-terminal-3.14.1.jar differ
diff --git a/tlatools/org.lamport.tlatools/ossrh.xml b/tlatools/org.lamport.tlatools/ossrh.xml
index 1b495f1fb9f0afe1e76fa90716b58dc1c0f97263..f6f7a40e38d059bc777c43a5f0ff1beddb4fc3c4 100644
--- a/tlatools/org.lamport.tlatools/ossrh.xml
+++ b/tlatools/org.lamport.tlatools/ossrh.xml
@@ -17,7 +17,7 @@
 	<groupId>org.lamport</groupId>
 	<artifactId>tla2tools</artifactId>
 	<name>TLA+ Tools</name>
-	<version>1.7.0-SNAPSHOT</version>
+	<version>1.7.1-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>
 
diff --git a/tlatools/org.lamport.tlatools/pom.xml b/tlatools/org.lamport.tlatools/pom.xml
index f2c1f1eaddacb99f31077d6a5243ea87aa269821..409de90af6469cea2adc79a701da5877835186db 100644
--- a/tlatools/org.lamport.tlatools/pom.xml
+++ b/tlatools/org.lamport.tlatools/pom.xml
@@ -202,6 +202,23 @@
             <artifactId>aspectjrt</artifactId>
             <version>1.9.1</version>
         </dependency>
+        <!-- REPL.java imports classes from org.jline.  The Toolbox (being an OSGi based Rich Client Platform) -->
+        <!-- does not see the jline jars references in customBuild.xml or .classpath.  It has to find jline    -->
+        <!-- elsewhere such as in the target platform (TLAToolbox.target) or on Maven Central.  For jline      -->
+        <!-- there exists no p2 repository that we could add to our target platform.  However, an OSGi-fied    -->
+        <!-- jline exists on Maven Central.                                                                    -->
+		<!-- https://mvnrepository.com/artifact/org.jline/jline-terminal -->
+		<dependency>
+		    <groupId>org.jline</groupId>
+		    <artifactId>jline-terminal</artifactId>
+		    <version>3.14.1</version>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.jline/jline-reader -->
+		<dependency>
+		    <groupId>org.jline</groupId>
+		    <artifactId>jline-reader</artifactId>
+		    <version>3.14.1</version>
+		</dependency>
       </dependencies>
 
       <!-- August 2014 - TL
diff --git a/tlatools/org.lamport.tlatools/spec/generate-te-spec-by-default.md b/tlatools/org.lamport.tlatools/spec/generate-te-spec-by-default.md
new file mode 100644
index 0000000000000000000000000000000000000000..9cbf53ef4b1ae09aeb68767275e31325928edded
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/spec/generate-te-spec-by-default.md
@@ -0,0 +1,64 @@
+# Generating Trace Expression Specs by Default
+
+## Overview
+
+This document describes & motivates a feature to have TLC generate a trace expression (TE) spec by default, instead of by request via command line parameter.
+
+## Motivation
+
+Trace exploration is an important and much-used feature of the TLA+ Toolbox.
+When TLC model-checks a spec and finds an invariant violation, it produces a sequence of states from the initial state to the state violating the invariant.
+Invariant violation is usually undesirable and stems from either a failure of system design or of the writing of a formal spec describing that design; thus it is useful to provide the user tools to assist in debugging why the invariant was violated.
+Trace exploration is foremost among these, where the user can define additional expressions to be evaluated in each state or transition of the error trace.
+This is a nice way to quickly iterate and explore system behavior at greater depth without having to re-run the entire model.
+
+Since trace exploration is such a useful feature, all TLA+ users should have access - not just those who use the toolbox.
+The TLA+ Visual Studio Code extension has seen uptake and lacks support for trace exploration.
+Adding trace exploration support to the VS Code extension is the ultimate motivator for the work outlined in this spec.
+
+## Design
+
+VS Code development revolves around a user-customizable text editor and high-quality command line tools.
+Thus bringing a high-quality trace exploration experience to VS Code really reduces to bringing that experience to the TLC command line interface.
+The idea is as follows: when TLC finds an invariant violation, it generates a minimal TLA+ spec which - if itself model-checked by TLC - produces the exact same error trace as leads to the original invariant violation.
+Trace exploration is facilitated by alias expressions - a transformation function applied to each state, added to the generated TLA+ spec by the user.
+The user can rename variables, hide variables, or add new variables evaluating to an expression of their choice.
+When TLC is run on the generated spec it will output an error trace transformed by the alias expression.
+All of this work was complete at the start of this project, triggered by providing the `-generateSpecTE` flag to TLC on startup.
+
+It was decided that TLC should generate these TE specs by default, so users would still be able to run trace exploration if they forgot the `-generateSpecTE` flag.
+However, there was a problem: the existing method of generating TE specs parsed the actual command line output of TLC, rather than reading the error trace from internal data structures.
+This required TLC be run in "tool" mode, where its output was extensively annotated to ease consumption by automated tools; for example:
+
+```
+@!@!@STARTMSG 2262:0 @!@!@
+TLC2 Version 2.15 of Day Month 20?? (rev: ${git.shortRevision})
+@!@!@ENDMSG 2262 @!@!@
+@!@!@STARTMSG 2401:3 @!@!@
+Please run the Java VM, which executes TLC with a throughput optimized garbage 
+collector, by passing the "-XX:+UseParallelGC" property.
+@!@!@ENDMSG 2401 @!@!@
+```
+
+Running TLC in this mode all the time would be unacceptable.
+Furthermore, the text-output-based generation method ran afoul of one its most closely-associated features: alias expressions!
+If a user had an alias expression transforming output in their *original* spec, the TE spec was generated from that transformed error trace output and thus nearly guaranteed to be invalid.
+Thus the TE spec generation process needed to be rewritten to read from internal TLC data structures, rather than the final command line output.
+This was the bulk of development work required for this phase of the project.
+
+## Implementation
+
+The work to generate TE specs from internal TLC data structures is now complete.
+TLC behaves as follows:
+
+ * The `-generateSpecTE` flag is ignored; a no-op
+ * Users can use the `-noGenerateSpecTE` flag to skip TE spec generation
+ * Users can use the `-teSpecOutDir` flag to specify TE spec output directory
+
+The TE spec is generated by default whenever users run TLC, and TLC uses regular human-readable non-tool output.
+
+These changes were implemented by hooking into the `tlc2.output.MP` class, through which all TLC output flows.
+An object subscribed to the `MP` class can filter through messages, each associated with an error code.
+A handful of error codes are associated with error traces, and a huge amount of detail is present about each state in the trace at this level of the code.
+This data was extracted and fed into the TE spec generation code, short-circuiting the original loop.
+
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-LICENSE.txt b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d97b49ab0f0f1e6d2c991ac0496be35d8bc36ad8
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-LICENSE.txt
@@ -0,0 +1,457 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
+
+
+Apache Commons Math includes the following code provided to the ASF under the
+Apache License 2.0:
+
+ - The inverse error function implementation in the Erf class is based on CUDA
+   code developed by Mike Giles, Oxford-Man Institute of Quantitative Finance,
+   and published in GPU Computing Gems, volume 2, 2010 (grant received on
+   March 23th 2013)
+ - The LinearConstraint, LinearObjectiveFunction, LinearOptimizer,
+   RelationShip, SimplexSolver and SimplexTableau classes in package
+   org.apache.commons.math3.optimization.linear include software developed by
+   Benjamin McCann (http://www.benmccann.com) and distributed with
+   the following copyright: Copyright 2009 Google Inc. (grant received on
+   March 16th 2009)
+ - The class "org.apache.commons.math3.exception.util.LocalizedFormatsTest" which
+   is an adapted version of "OrekitMessagesTest" test class for the Orekit library
+ - The "org.apache.commons.math3.analysis.interpolation.HermiteInterpolator"
+   has been imported from the Orekit space flight dynamics library.
+
+===============================================================================
+ 
+
+
+APACHE COMMONS MATH DERIVATIVE WORKS: 
+
+The Apache commons-math library includes a number of subcomponents
+whose implementation is derived from original sources written
+in C or Fortran.  License terms of the original sources
+are reproduced below.
+
+===============================================================================
+For the lmder, lmpar and qrsolv Fortran routine from minpack and translated in
+the LevenbergMarquardtOptimizer class in package
+org.apache.commons.math3.optimization.general 
+Original source copyright and license statement:
+
+Minpack Copyright Notice (1999) University of Chicago.  All rights reserved
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above
+copyright notice, this list of conditions and the following
+disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials
+provided with the distribution.
+
+3. The end-user documentation included with the
+redistribution, if any, must include the following
+acknowledgment:
+
+   "This product includes software developed by the
+   University of Chicago, as Operator of Argonne National
+   Laboratory.
+
+Alternately, this acknowledgment may appear in the software
+itself, if and wherever such third-party acknowledgments
+normally appear.
+
+4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS"
+WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE
+UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND
+THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE
+OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY
+OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR
+USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF
+THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4)
+DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION
+UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL
+BE CORRECTED.
+
+5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT
+HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF
+ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT,
+INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF
+ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF
+PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER
+SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT
+(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE,
+EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
+POSSIBILITY OF SUCH LOSS OR DAMAGES.
+===============================================================================
+
+Copyright and license statement for the odex Fortran routine developed by
+E. Hairer and G. Wanner and translated in GraggBulirschStoerIntegrator class
+in package org.apache.commons.math3.ode.nonstiff:
+
+
+Copyright (c) 2004, Ernst Hairer
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are 
+met:
+
+- Redistributions of source code must retain the above copyright 
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright 
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
+Copyright and license statement for the original Mersenne twister C
+routines translated in MersenneTwister class in package 
+org.apache.commons.math3.random:
+
+   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+   All rights reserved.                          
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+     1. Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+     2. Redistributions in binary form must reproduce the above copyright
+        notice, this list of conditions and the following disclaimer in the
+        documentation and/or other materials provided with the distribution.
+
+     3. The names of its contributors may not be used to endorse or promote 
+        products derived from this software without specific prior written 
+        permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+===============================================================================
+
+The initial code for shuffling an array (originally in class
+"org.apache.commons.math3.random.RandomDataGenerator", now replaced by
+a method in class "org.apache.commons.math3.util.MathArrays") was
+inspired from the algorithm description provided in
+"Algorithms", by Ian Craw and John Pulham (University of Aberdeen 1999).
+The textbook (containing a proof that the shuffle is uniformly random) is
+available here:
+  http://citeseerx.ist.psu.edu/viewdoc/download;?doi=10.1.1.173.1898&rep=rep1&type=pdf
+
+===============================================================================
+License statement for the direction numbers in the resource files for Sobol sequences.
+
+-----------------------------------------------------------------------------
+Licence pertaining to sobol.cc and the accompanying sets of direction numbers
+
+-----------------------------------------------------------------------------
+Copyright (c) 2008, Frances Y. Kuo and Stephen Joe
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the copyright holders nor the names of the
+      University of New South Wales and the University of Waikato
+      and its contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
+The initial commit of package "org.apache.commons.math3.ml.neuralnet" is
+an adapted version of code developed in the context of the Data Processing
+and Analysis Consortium (DPAC) of the "Gaia" project of the European Space
+Agency (ESA).
+===============================================================================
+
+The initial commit of the class "org.apache.commons.math3.special.BesselJ" is
+an adapted version of code translated from the netlib Fortran program, rjbesl
+http://www.netlib.org/specfun/rjbesl by R.J. Cody at Argonne National
+Laboratory (USA).  There is no license or copyright statement included with the
+original Fortran sources.
+===============================================================================
+
+
+The BracketFinder (package org.apache.commons.math3.optimization.univariate)
+and PowellOptimizer (package org.apache.commons.math3.optimization.general)
+classes are based on the Python code in module "optimize.py" (version 0.5)
+developed by Travis E. Oliphant for the SciPy library (http://www.scipy.org/)
+Copyright © 2003-2009 SciPy Developers.
+
+SciPy license
+Copyright © 2001, 2002 Enthought, Inc.
+All rights reserved.
+
+Copyright © 2003-2013 SciPy Developers.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the name of Enthought nor the names of the SciPy Developers may
+      be used to endorse or promote products derived from this software without
+      specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+===============================================================================
+
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-NOTICE.txt b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-NOTICE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..be158fe280392097ab15cc631cae38c08bc95b68
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/CommonsMath-NOTICE.txt
@@ -0,0 +1,9 @@
+Apache Commons Math
+Copyright 2001-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed for Orekit by
+CS Systèmes d'Information (http://www.c-s.fr/)
+Copyright 2010-2012 CS Systèmes d'Information
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/MathIllegalArgumentException.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/MathIllegalArgumentException.java
new file mode 100644
index 0000000000000000000000000000000000000000..89012f0c8f4ebd2a876ca05f456cc290eb52e015
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/MathIllegalArgumentException.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.commons.math3.exception;
+
+import org.apache.commons.math3.exception.util.Localizable;
+import org.apache.commons.math3.exception.util.ExceptionContext;
+import org.apache.commons.math3.exception.util.ExceptionContextProvider;
+
+/**
+ * Base class for all preconditions violation exceptions.
+ * In most cases, this class should not be instantiated directly: it should
+ * serve as a base class to create all the exceptions that have the semantics
+ * of the standard {@link IllegalArgumentException}.
+ *
+ * @since 2.2
+ */
+public class MathIllegalArgumentException extends IllegalArgumentException
+    implements ExceptionContextProvider {
+    /** Serializable version Id. */
+    private static final long serialVersionUID = -6024911025449780478L;
+    /** Context. */
+    private final ExceptionContext context;
+
+    /**
+     * @param pattern Message pattern explaining the cause of the error.
+     * @param args Arguments.
+     */
+    public MathIllegalArgumentException(Localizable pattern,
+                                        Object ... args) {
+        context = new ExceptionContext(this);
+        context.addMessage(pattern, args);
+    }
+
+    /** {@inheritDoc} */
+    public ExceptionContext getContext() {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getMessage() {
+        return context.getMessage();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getLocalizedMessage() {
+        return context.getLocalizedMessage();
+    }
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ArgUtils.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ArgUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..74214cc17e6d1e4e282f8804e25e93ae16c56e02
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ArgUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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 org.apache.commons.math3.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Utility class for transforming the list of arguments passed to
+ * constructors of exceptions.
+ *
+ */
+public class ArgUtils {
+    /**
+     * Class contains only static methods.
+     */
+    private ArgUtils() {}
+
+    /**
+     * Transform a multidimensional array into a one-dimensional list.
+     *
+     * @param array Array (possibly multidimensional).
+     * @return a list of all the {@code Object} instances contained in
+     * {@code array}.
+     */
+    public static Object[] flatten(Object[] array) {
+        final List<Object> list = new ArrayList<Object>();
+        if (array != null) {
+            for (Object o : array) {
+                if (o instanceof Object[]) {
+                    for (Object oR : flatten((Object[]) o)) {
+                        list.add(oR);
+                    }
+                } else {
+                    list.add(o);
+                }
+            }
+        }
+        return list.toArray();
+    }
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContext.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a78bd38f450af43097676a3438db04530a7a74b
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContext.java
@@ -0,0 +1,334 @@
+/*
+ * 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 org.apache.commons.math3.exception.util;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Map;
+import java.io.IOException;
+import java.io.Serializable;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.util.HashMap;
+import java.text.MessageFormat;
+import java.util.Locale;
+
+/**
+ * Class that contains the actual implementation of the functionality mandated
+ * by the {@link ExceptionContext} interface.
+ * All Commons Math exceptions delegate the interface's methods to this class.
+ *
+ * @since 3.0
+ */
+public class ExceptionContext implements Serializable {
+    /** Serializable version Id. */
+    private static final long serialVersionUID = -6024911025449780478L;
+    /**
+     * The throwable to which this context refers to.
+     */
+    private Throwable throwable;
+    /**
+     * Various informations that enrich the informative message.
+     */
+    private List<Localizable> msgPatterns;
+    /**
+     * Various informations that enrich the informative message.
+     * The arguments will replace the corresponding place-holders in
+     * {@link #msgPatterns}.
+     */
+    private List<Object[]> msgArguments;
+    /**
+     * Arbitrary context information.
+     */
+    private Map<String, Object> context;
+
+    /** Simple constructor.
+     * @param throwable the exception this context refers too
+     */
+    public ExceptionContext(final Throwable throwable) {
+        this.throwable = throwable;
+        msgPatterns    = new ArrayList<Localizable>();
+        msgArguments   = new ArrayList<Object[]>();
+        context        = new HashMap<String, Object>();
+    }
+
+    /** Get a reference to the exception to which the context relates.
+     * @return a reference to the exception to which the context relates
+     */
+    public Throwable getThrowable() {
+        return throwable;
+    }
+
+    /**
+     * Adds a message.
+     *
+     * @param pattern Message pattern.
+     * @param arguments Values for replacing the placeholders in the message
+     * pattern.
+     */
+    public void addMessage(Localizable pattern,
+                           Object ... arguments) {
+        msgPatterns.add(pattern);
+        msgArguments.add(ArgUtils.flatten(arguments));
+    }
+
+    /**
+     * Sets the context (key, value) pair.
+     * Keys are assumed to be unique within an instance. If the same key is
+     * assigned a new value, the previous one will be lost.
+     *
+     * @param key Context key (not null).
+     * @param value Context value.
+     */
+    public void setValue(String key, Object value) {
+        context.put(key, value);
+    }
+
+    /**
+     * Gets the value associated to the given context key.
+     *
+     * @param key Context key.
+     * @return the context value or {@code null} if the key does not exist.
+     */
+    public Object getValue(String key) {
+        return context.get(key);
+    }
+
+    /**
+     * Gets all the keys stored in the exception
+     *
+     * @return the set of keys.
+     */
+    public Set<String> getKeys() {
+        return context.keySet();
+    }
+
+    /**
+     * Gets the default message.
+     *
+     * @return the message.
+     */
+    public String getMessage() {
+        return getMessage(Locale.US);
+    }
+
+    /**
+     * Gets the message in the default locale.
+     *
+     * @return the localized message.
+     */
+    public String getLocalizedMessage() {
+        return getMessage(Locale.getDefault());
+    }
+
+    /**
+     * Gets the message in a specified locale.
+     *
+     * @param locale Locale in which the message should be translated.
+     * @return the localized message.
+     */
+    public String getMessage(final Locale locale) {
+        return buildMessage(locale, ": ");
+    }
+
+    /**
+     * Gets the message in a specified locale.
+     *
+     * @param locale Locale in which the message should be translated.
+     * @param separator Separator inserted between the message parts.
+     * @return the localized message.
+     */
+    public String getMessage(final Locale locale,
+                             final String separator) {
+        return buildMessage(locale, separator);
+    }
+
+    /**
+     * Builds a message string.
+     *
+     * @param locale Locale in which the message should be translated.
+     * @param separator Message separator.
+     * @return a localized message string.
+     */
+    private String buildMessage(Locale locale,
+                                String separator) {
+        final StringBuilder sb = new StringBuilder();
+        int count = 0;
+        final int len = msgPatterns.size();
+        for (int i = 0; i < len; i++) {
+            final Localizable pat = msgPatterns.get(i);
+            final Object[] args = msgArguments.get(i);
+            final MessageFormat fmt = new MessageFormat(pat.getLocalizedString(locale),
+                                                        locale);
+            sb.append(fmt.format(args));
+            if (++count < len) {
+                // Add a separator if there are other messages.
+                sb.append(separator);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Serialize this object to the given stream.
+     *
+     * @param out Stream.
+     * @throws IOException This should never happen.
+     */
+    private void writeObject(ObjectOutputStream out)
+        throws IOException {
+        out.writeObject(throwable);
+        serializeMessages(out);
+        serializeContext(out);
+    }
+    /**
+     * Deserialize this object from the given stream.
+     *
+     * @param in Stream.
+     * @throws IOException This should never happen.
+     * @throws ClassNotFoundException This should never happen.
+     */
+    private void readObject(ObjectInputStream in)
+        throws IOException,
+               ClassNotFoundException {
+        throwable = (Throwable) in.readObject();
+        deSerializeMessages(in);
+        deSerializeContext(in);
+    }
+
+    /**
+     * Serialize  {@link #msgPatterns} and {@link #msgArguments}.
+     *
+     * @param out Stream.
+     * @throws IOException This should never happen.
+     */
+    private void serializeMessages(ObjectOutputStream out)
+        throws IOException {
+        // Step 1.
+        final int len = msgPatterns.size();
+        out.writeInt(len);
+        // Step 2.
+        for (int i = 0; i < len; i++) {
+            final Localizable pat = msgPatterns.get(i);
+            // Step 3.
+            out.writeObject(pat);
+            final Object[] args = msgArguments.get(i);
+            final int aLen = args.length;
+            // Step 4.
+            out.writeInt(aLen);
+            for (int j = 0; j < aLen; j++) {
+                if (args[j] instanceof Serializable) {
+                    // Step 5a.
+                    out.writeObject(args[j]);
+                } else {
+                    // Step 5b.
+                    out.writeObject(nonSerializableReplacement(args[j]));
+                }
+            }
+        }
+    }
+
+    /**
+     * Deserialize {@link #msgPatterns} and {@link #msgArguments}.
+     *
+     * @param in Stream.
+     * @throws IOException This should never happen.
+     * @throws ClassNotFoundException This should never happen.
+     */
+    private void deSerializeMessages(ObjectInputStream in)
+        throws IOException,
+               ClassNotFoundException {
+        // Step 1.
+        final int len = in.readInt();
+        msgPatterns = new ArrayList<Localizable>(len);
+        msgArguments = new ArrayList<Object[]>(len);
+        // Step 2.
+        for (int i = 0; i < len; i++) {
+            // Step 3.
+            final Localizable pat = (Localizable) in.readObject();
+            msgPatterns.add(pat);
+            // Step 4.
+            final int aLen = in.readInt();
+            final Object[] args = new Object[aLen];
+            for (int j = 0; j < aLen; j++) {
+                // Step 5.
+                args[j] = in.readObject();
+            }
+            msgArguments.add(args);
+        }
+    }
+
+    /**
+     * Serialize {@link #context}.
+     *
+     * @param out Stream.
+     * @throws IOException This should never happen.
+     */
+    private void serializeContext(ObjectOutputStream out)
+        throws IOException {
+        // Step 1.
+        final int len = context.size();
+        out.writeInt(len);
+        for (Map.Entry<String, Object> entry : context.entrySet()) {
+            // Step 2.
+            out.writeObject(entry.getKey());
+            final Object value = entry.getValue();
+            if (value instanceof Serializable) {
+                // Step 3a.
+                out.writeObject(value);
+            } else {
+                // Step 3b.
+                out.writeObject(nonSerializableReplacement(value));
+            }
+        }
+    }
+
+    /**
+     * Deserialize {@link #context}.
+     *
+     * @param in Stream.
+     * @throws IOException This should never happen.
+     * @throws ClassNotFoundException This should never happen.
+     */
+    private void deSerializeContext(ObjectInputStream in)
+        throws IOException,
+               ClassNotFoundException {
+        // Step 1.
+        final int len = in.readInt();
+        context = new HashMap<String, Object>();
+        for (int i = 0; i < len; i++) {
+            // Step 2.
+            final String key = (String) in.readObject();
+            // Step 3.
+            final Object value = in.readObject();
+            context.put(key, value);
+        }
+    }
+
+    /**
+     * Replaces a non-serializable object with an error message string.
+     *
+     * @param obj Object that does not implement the {@code Serializable}
+     * interface.
+     * @return a string that mentions which class could not be serialized.
+     */
+    private String nonSerializableReplacement(Object obj) {
+        return "[Object could not be serialized: " + obj.getClass().getName() + "]";
+    }
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContextProvider.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContextProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..913f66ad5a074dbc07fac78c7a2caa71a5d01e0e
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/ExceptionContextProvider.java
@@ -0,0 +1,33 @@
+/*
+ * 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 org.apache.commons.math3.exception.util;
+
+/**
+ * Interface for accessing the context data structure stored in Commons Math
+ * exceptions.
+ *
+ */
+public interface ExceptionContextProvider {
+    /**
+     * Gets a reference to the "rich context" data structure that allows to
+     * customize error messages and store key, value pairs in exceptions.
+     *
+     * @return a reference to the exception context.
+     */
+    ExceptionContext getContext();
+
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/Localizable.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/Localizable.java
new file mode 100644
index 0000000000000000000000000000000000000000..9758bc24b6d174db50e86583b7004987eb5dc9fe
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/Localizable.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.apache.commons.math3.exception.util;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+/**
+ * Interface for localizable strings.
+ *
+ * @since 2.2
+ */
+public interface Localizable extends Serializable {
+    /**
+     * Gets the source (non-localized) string.
+     *
+     * @return the source string.
+     */
+    String getSourceString();
+
+    /**
+     * Gets the localized string.
+     *
+     * @param locale locale into which to get the string.
+     * @return the localized string or the source string if no
+     * localized version is available.
+     */
+    String getLocalizedString(Locale locale);
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/LocalizedFormats.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/LocalizedFormats.java
new file mode 100644
index 0000000000000000000000000000000000000000..8be9639ec3611306f14d5145bfb8f148203fea5f
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/exception/util/LocalizedFormats.java
@@ -0,0 +1,414 @@
+/*
+ * 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 org.apache.commons.math3.exception.util;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Enumeration for localized messages formats used in exceptions messages.
+ * <p>
+ * The constants in this enumeration represent the available
+ * formats as localized strings. These formats are intended to be
+ * localized using simple properties files, using the constant
+ * name as the key and the property value as the message format.
+ * The source English format is provided in the constants themselves
+ * to serve both as a reminder for developers to understand the parameters
+ * needed by each format, as a basis for translators to create
+ * localized properties files, and as a default format if some
+ * translation is missing.
+ * </p>
+ * @since 2.2
+ */
+public enum LocalizedFormats implements Localizable {
+
+    // CHECKSTYLE: stop MultipleVariableDeclarations
+    // CHECKSTYLE: stop JavadocVariable
+
+    ARGUMENT_OUTSIDE_DOMAIN("Argument {0} outside domain [{1} ; {2}]"),
+    ARRAY_SIZE_EXCEEDS_MAX_VARIABLES("array size cannot be greater than {0}"),
+    ARRAY_SIZES_SHOULD_HAVE_DIFFERENCE_1("array sizes should have difference 1 ({0} != {1} + 1)"),
+    ARRAY_SUMS_TO_ZERO("array sums to zero"),
+    ASSYMETRIC_EIGEN_NOT_SUPPORTED("eigen decomposition of assymetric matrices not supported yet"),
+    AT_LEAST_ONE_COLUMN("matrix must have at least one column"),
+    AT_LEAST_ONE_ROW("matrix must have at least one row"),
+    BANDWIDTH("bandwidth ({0})"),
+    BESSEL_FUNCTION_BAD_ARGUMENT("Bessel function of order {0} cannot be computed for x = {1}"),
+    BESSEL_FUNCTION_FAILED_CONVERGENCE("Bessel function of order {0} failed to converge for x = {1}"),
+    BINOMIAL_INVALID_PARAMETERS_ORDER("must have n >= k for binomial coefficient (n, k), got k = {0}, n = {1}"),
+    BINOMIAL_NEGATIVE_PARAMETER("must have n >= 0 for binomial coefficient (n, k), got n = {0}"),
+    CANNOT_CLEAR_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS("statistics constructed from external moments cannot be cleared"),
+    CANNOT_COMPUTE_0TH_ROOT_OF_UNITY("cannot compute 0-th root of unity, indefinite result"),
+    CANNOT_COMPUTE_BETA_DENSITY_AT_0_FOR_SOME_ALPHA("cannot compute beta density at 0 when alpha = {0,number}"),
+    CANNOT_COMPUTE_BETA_DENSITY_AT_1_FOR_SOME_BETA("cannot compute beta density at 1 when beta = %.3g"),
+    CANNOT_COMPUTE_NTH_ROOT_FOR_NEGATIVE_N("cannot compute nth root for null or negative n: {0}"),
+    CANNOT_DISCARD_NEGATIVE_NUMBER_OF_ELEMENTS("cannot discard a negative number of elements ({0})"),
+    CANNOT_FORMAT_INSTANCE_AS_3D_VECTOR("cannot format a {0} instance as a 3D vector"),
+    CANNOT_FORMAT_INSTANCE_AS_COMPLEX("cannot format a {0} instance as a complex number"),
+    CANNOT_FORMAT_INSTANCE_AS_REAL_VECTOR("cannot format a {0} instance as a real vector"),
+    CANNOT_FORMAT_OBJECT_TO_FRACTION("cannot format given object as a fraction number"),
+    CANNOT_INCREMENT_STATISTIC_CONSTRUCTED_FROM_EXTERNAL_MOMENTS("statistics constructed from external moments cannot be incremented"),
+    CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR("cannot normalize a zero norm vector"),
+    CANNOT_RETRIEVE_AT_NEGATIVE_INDEX("elements cannot be retrieved from a negative array index {0}"),
+    CANNOT_SET_AT_NEGATIVE_INDEX("cannot set an element at a negative index {0}"),
+    CANNOT_SUBSTITUTE_ELEMENT_FROM_EMPTY_ARRAY("cannot substitute an element from an empty array"),
+    CANNOT_TRANSFORM_TO_DOUBLE("Conversion Exception in Transformation: {0}"),
+    CARDAN_ANGLES_SINGULARITY("Cardan angles singularity"),
+    CLASS_DOESNT_IMPLEMENT_COMPARABLE("class ({0}) does not implement Comparable"),
+    CLOSE_VERTICES("too close vertices near point ({0}, {1}, {2})"),
+    CLOSEST_ORTHOGONAL_MATRIX_HAS_NEGATIVE_DETERMINANT("the closest orthogonal matrix has a negative determinant {0}"),
+    COLUMN_INDEX_OUT_OF_RANGE("column index {0} out of allowed range [{1}, {2}]"),
+    COLUMN_INDEX("column index ({0})"), /* keep */
+    CONSTRAINT("constraint"), /* keep */
+    CONTINUED_FRACTION_INFINITY_DIVERGENCE("Continued fraction convergents diverged to +/- infinity for value {0}"),
+    CONTINUED_FRACTION_NAN_DIVERGENCE("Continued fraction diverged to NaN for value {0}"),
+    CONTRACTION_CRITERIA_SMALLER_THAN_EXPANSION_FACTOR("contraction criteria ({0}) smaller than the expansion factor ({1}).  This would lead to a never ending loop of expansion and contraction as a newly expanded internal storage array would immediately satisfy the criteria for contraction."),
+    CONTRACTION_CRITERIA_SMALLER_THAN_ONE("contraction criteria smaller than one ({0}).  This would lead to a never ending loop of expansion and contraction as an internal storage array length equal to the number of elements would satisfy the contraction criteria."),
+    CONVERGENCE_FAILED("convergence failed"), /* keep */
+    CROSSING_BOUNDARY_LOOPS("some outline boundary loops cross each other"),
+    CROSSOVER_RATE("crossover rate ({0})"),
+    CUMULATIVE_PROBABILITY_RETURNED_NAN("Cumulative probability function returned NaN for argument {0} p = {1}"),
+    DIFFERENT_ROWS_LENGTHS("some rows have length {0} while others have length {1}"),
+    DIFFERENT_ORIG_AND_PERMUTED_DATA("original and permuted data must contain the same elements"),
+    DIGEST_NOT_INITIALIZED("digest not initialized"),
+    DIMENSIONS_MISMATCH_2x2("got {0}x{1} but expected {2}x{3}"), /* keep */
+    DIMENSIONS_MISMATCH_SIMPLE("{0} != {1}"), /* keep */
+    DIMENSIONS_MISMATCH("dimensions mismatch"), /* keep */
+    DISCRETE_CUMULATIVE_PROBABILITY_RETURNED_NAN("Discrete cumulative probability function returned NaN for argument {0}"),
+    DISTRIBUTION_NOT_LOADED("distribution not loaded"),
+    DUPLICATED_ABSCISSA_DIVISION_BY_ZERO("duplicated abscissa {0} causes division by zero"),
+    EDGE_CONNECTED_TO_ONE_FACET("edge joining points ({0}, {1}, {2}) and ({3}, {4}, {5}) is connected to one facet only"),
+    ELITISM_RATE("elitism rate ({0})"),
+    EMPTY_CLUSTER_IN_K_MEANS("empty cluster in k-means"),
+    EMPTY_INTERPOLATION_SAMPLE("sample for interpolation is empty"),
+    EMPTY_POLYNOMIALS_COEFFICIENTS_ARRAY("empty polynomials coefficients array"), /* keep */
+    EMPTY_SELECTED_COLUMN_INDEX_ARRAY("empty selected column index array"),
+    EMPTY_SELECTED_ROW_INDEX_ARRAY("empty selected row index array"),
+    EMPTY_STRING_FOR_IMAGINARY_CHARACTER("empty string for imaginary character"),
+    ENDPOINTS_NOT_AN_INTERVAL("endpoints do not specify an interval: [{0}, {1}]"),
+    EQUAL_VERTICES_IN_SIMPLEX("equal vertices {0} and {1} in simplex configuration"),
+    EULER_ANGLES_SINGULARITY("Euler angles singularity"),
+    EVALUATION("evaluation"), /* keep */
+    EXPANSION_FACTOR_SMALLER_THAN_ONE("expansion factor smaller than one ({0})"),
+    FACET_ORIENTATION_MISMATCH("facets orientation mismatch around edge joining points ({0}, {1}, {2}) and ({3}, {4}, {5})"),
+    FACTORIAL_NEGATIVE_PARAMETER("must have n >= 0 for n!, got n = {0}"),
+    FAILED_BRACKETING("number of iterations={4}, maximum iterations={5}, initial={6}, lower bound={7}, upper bound={8}, final a value={0}, final b value={1}, f(a)={2}, f(b)={3}"),
+    FAILED_FRACTION_CONVERSION("Unable to convert {0} to fraction after {1} iterations"),
+    FIRST_COLUMNS_NOT_INITIALIZED_YET("first {0} columns are not initialized yet"),
+    FIRST_ELEMENT_NOT_ZERO("first element is not 0: {0}"),
+    FIRST_ROWS_NOT_INITIALIZED_YET("first {0} rows are not initialized yet"),
+    FRACTION_CONVERSION_OVERFLOW("Overflow trying to convert {0} to fraction ({1}/{2})"),
+    FUNCTION_NOT_DIFFERENTIABLE("function is not differentiable"),
+    FUNCTION_NOT_POLYNOMIAL("function is not polynomial"),
+    GCD_OVERFLOW_32_BITS("overflow: gcd({0}, {1}) is 2^31"),
+    GCD_OVERFLOW_64_BITS("overflow: gcd({0}, {1}) is 2^63"),
+    HOLE_BETWEEN_MODELS_TIME_RANGES("{0} wide hole between models time ranges"),
+    ILL_CONDITIONED_OPERATOR("condition number {1} is too high "),
+    INCONSISTENT_STATE_AT_2_PI_WRAPPING("inconsistent state at 2\u03c0 wrapping"),
+    INDEX_LARGER_THAN_MAX("the index specified: {0} is larger than the current maximal index {1}"),
+    INDEX_NOT_POSITIVE("index ({0}) is not positive"),
+    INDEX_OUT_OF_RANGE("index {0} out of allowed range [{1}, {2}]"),
+    INDEX("index ({0})"), /* keep */
+    NOT_FINITE_NUMBER("{0} is not a finite number"), /* keep */
+    INFINITE_BOUND("interval bounds must be finite"),
+    ARRAY_ELEMENT("value {0} at index {1}"), /* keep */
+    INFINITE_ARRAY_ELEMENT("Array contains an infinite element, {0} at index {1}"),
+    INFINITE_VALUE_CONVERSION("cannot convert infinite value"),
+    INITIAL_CAPACITY_NOT_POSITIVE("initial capacity ({0}) is not positive"),
+    INITIAL_COLUMN_AFTER_FINAL_COLUMN("initial column {1} after final column {0}"),
+    INITIAL_ROW_AFTER_FINAL_ROW("initial row {1} after final row {0}"),
+    @Deprecated
+    INPUT_DATA_FROM_UNSUPPORTED_DATASOURCE("input data comes from unsupported datasource: {0}, supported sources: {1}, {2}"),
+    INSTANCES_NOT_COMPARABLE_TO_EXISTING_VALUES("instance of class {0} not comparable to existing values"),
+    INSUFFICIENT_DATA("insufficient data"),
+    INSUFFICIENT_DATA_FOR_T_STATISTIC("insufficient data for t statistic, needs at least 2, got {0}"),
+    INSUFFICIENT_DIMENSION("insufficient dimension {0}, must be at least {1}"),
+    DIMENSION("dimension ({0})"), /* keep */
+    INSUFFICIENT_OBSERVED_POINTS_IN_SAMPLE("sample contains {0} observed points, at least {1} are required"),
+    INSUFFICIENT_ROWS_AND_COLUMNS("insufficient data: only {0} rows and {1} columns."),
+    INTEGRATION_METHOD_NEEDS_AT_LEAST_TWO_PREVIOUS_POINTS("multistep method needs at least {0} previous steps, got {1}"),
+    INTERNAL_ERROR("internal error, please fill a bug report at {0}"),
+    INVALID_BINARY_DIGIT("invalid binary digit: {0}"),
+    INVALID_BINARY_CHROMOSOME("binary mutation works on BinaryChromosome only"),
+    INVALID_BRACKETING_PARAMETERS("invalid bracketing parameters:  lower bound={0},  initial={1}, upper bound={2}"),
+    INVALID_FIXED_LENGTH_CHROMOSOME("one-point crossover only works with fixed-length chromosomes"),
+    INVALID_IMPLEMENTATION("required functionality is missing in {0}"),
+    INVALID_INTERVAL_INITIAL_VALUE_PARAMETERS("invalid interval, initial value parameters:  lower={0}, initial={1}, upper={2}"),
+    INVALID_ITERATIONS_LIMITS("invalid iteration limits: min={0}, max={1}"),
+    INVALID_MAX_ITERATIONS("bad value for maximum iterations number: {0}"),
+    NOT_ENOUGH_DATA_REGRESSION("the number of observations is not sufficient to conduct regression"),
+    INVALID_REGRESSION_ARRAY("input data array length = {0} does not match the number of observations = {1} and the number of regressors = {2}"),
+    INVALID_REGRESSION_OBSERVATION("length of regressor array = {0} does not match the number of variables = {1} in the model"),
+    INVALID_ROUNDING_METHOD("invalid rounding method {0}, valid methods: {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}), {11} ({12}), {13} ({14}), {15} ({16})"),
+    ITERATOR_EXHAUSTED("iterator exhausted"),
+    ITERATIONS("iterations"), /* keep */
+    LCM_OVERFLOW_32_BITS("overflow: lcm({0}, {1}) is 2^31"),
+    LCM_OVERFLOW_64_BITS("overflow: lcm({0}, {1}) is 2^63"),
+    LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE("list of chromosomes bigger than maxPopulationSize"),
+    LOESS_EXPECTS_AT_LEAST_ONE_POINT("Loess expects at least 1 point"),
+    LOWER_BOUND_NOT_BELOW_UPPER_BOUND("lower bound ({0}) must be strictly less than upper bound ({1})"), /* keep */
+    LOWER_ENDPOINT_ABOVE_UPPER_ENDPOINT("lower endpoint ({0}) must be less than or equal to upper endpoint ({1})"),
+    MAP_MODIFIED_WHILE_ITERATING("map has been modified while iterating"),
+    MULTISTEP_STARTER_STOPPED_EARLY("multistep integrator starter stopped early, maybe too large step size"),
+    EVALUATIONS("evaluations"), /* keep */
+    MAX_COUNT_EXCEEDED("maximal count ({0}) exceeded"), /* keep */
+    MAX_ITERATIONS_EXCEEDED("maximal number of iterations ({0}) exceeded"),
+    MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION("minimal step size ({1,number,0.00E00}) reached, integration needs {0,number,0.00E00}"),
+    MISMATCHED_LOESS_ABSCISSA_ORDINATE_ARRAYS("Loess expects the abscissa and ordinate arrays to be of the same size, but got {0} abscissae and {1} ordinatae"),
+    MUTATION_RATE("mutation rate ({0})"),
+    NAN_ELEMENT_AT_INDEX("element {0} is NaN"),
+    NAN_VALUE_CONVERSION("cannot convert NaN value"),
+    NEGATIVE_BRIGHTNESS_EXPONENT("brightness exponent should be positive or null, but got {0}"),
+    NEGATIVE_COMPLEX_MODULE("negative complex module {0}"),
+    NEGATIVE_ELEMENT_AT_2D_INDEX("element ({0}, {1}) is negative: {2}"),
+    NEGATIVE_ELEMENT_AT_INDEX("element {0} is negative: {1}"),
+    NEGATIVE_NUMBER_OF_SUCCESSES("number of successes must be non-negative ({0})"),
+    NUMBER_OF_SUCCESSES("number of successes ({0})"), /* keep */
+    NEGATIVE_NUMBER_OF_TRIALS("number of trials must be non-negative ({0})"),
+    NUMBER_OF_INTERPOLATION_POINTS("number of interpolation points ({0})"), /* keep */
+    NUMBER_OF_TRIALS("number of trials ({0})"),
+    NOT_CONVEX("vertices do not form a convex hull in CCW winding"),
+    NOT_CONVEX_HYPERPLANES("hyperplanes do not define a convex region"),
+    ROBUSTNESS_ITERATIONS("number of robustness iterations ({0})"),
+    START_POSITION("start position ({0})"), /* keep */
+    NON_CONVERGENT_CONTINUED_FRACTION("Continued fraction convergents failed to converge (in less than {0} iterations) for value {1}"),
+    NON_INVERTIBLE_TRANSFORM("non-invertible affine transform collapses some lines into single points"),
+    NON_POSITIVE_MICROSPHERE_ELEMENTS("number of microsphere elements must be positive, but got {0}"),
+    NON_POSITIVE_POLYNOMIAL_DEGREE("polynomial degree must be positive: degree={0}"),
+    NON_REAL_FINITE_ABSCISSA("all abscissae must be finite real numbers, but {0}-th is {1}"),
+    NON_REAL_FINITE_ORDINATE("all ordinatae must be finite real numbers, but {0}-th is {1}"),
+    NON_REAL_FINITE_WEIGHT("all weights must be finite real numbers, but {0}-th is {1}"),
+    NON_SQUARE_MATRIX("non square ({0}x{1}) matrix"),
+    NORM("Norm ({0})"), /* keep */
+    NORMALIZE_INFINITE("Cannot normalize to an infinite value"),
+    NORMALIZE_NAN("Cannot normalize to NaN"),
+    NOT_ADDITION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not addition compatible"),
+    NOT_DECREASING_NUMBER_OF_POINTS("points {0} and {1} are not decreasing ({2} < {3})"),
+    NOT_DECREASING_SEQUENCE("points {3} and {2} are not decreasing ({1} < {0})"), /* keep */
+    NOT_ENOUGH_DATA_FOR_NUMBER_OF_PREDICTORS("not enough data ({0} rows) for this many predictors ({1} predictors)"),
+    NOT_ENOUGH_POINTS_IN_SPLINE_PARTITION("spline partition must have at least {0} points, got {1}"),
+    NOT_INCREASING_NUMBER_OF_POINTS("points {0} and {1} are not increasing ({2} > {3})"),
+    NOT_INCREASING_SEQUENCE("points {3} and {2} are not increasing ({1} > {0})"), /* keep */
+    NOT_MULTIPLICATION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not multiplication compatible"),
+    NOT_POSITIVE_DEFINITE_MATRIX("not positive definite matrix"), /* keep */
+    NON_POSITIVE_DEFINITE_MATRIX("not positive definite matrix: diagonal element at ({1},{1}) is smaller than {2} ({0})"),
+    NON_POSITIVE_DEFINITE_OPERATOR("non positive definite linear operator"), /* keep */
+    NON_SELF_ADJOINT_OPERATOR("non self-adjoint linear operator"), /* keep */
+    NON_SQUARE_OPERATOR("non square ({0}x{1}) linear operator"), /* keep */
+    DEGREES_OF_FREEDOM("degrees of freedom ({0})"), /* keep */
+    NOT_POSITIVE_DEGREES_OF_FREEDOM("degrees of freedom must be positive ({0})"),
+    NOT_POSITIVE_ELEMENT_AT_INDEX("element {0} is not positive: {1}"),
+    NOT_POSITIVE_EXPONENT("invalid exponent {0} (must be positive)"),
+    NUMBER_OF_ELEMENTS_SHOULD_BE_POSITIVE("number of elements should be positive ({0})"),
+    BASE("base ({0})"), /* keep */
+    EXPONENT("exponent ({0})"), /* keep */
+    NOT_POSITIVE_LENGTH("length must be positive ({0})"),
+    LENGTH("length ({0})"), /* keep */
+    NOT_POSITIVE_MEAN("mean must be positive ({0})"),
+    MEAN("mean ({0})"), /* keep */
+    NOT_POSITIVE_NUMBER_OF_SAMPLES("number of sample is not positive: {0}"),
+    NUMBER_OF_SAMPLES("number of samples ({0})"), /* keep */
+    NOT_POSITIVE_PERMUTATION("permutation k ({0}) must be positive"),
+    PERMUTATION_SIZE("permutation size ({0}"), /* keep */
+    NOT_POSITIVE_POISSON_MEAN("the Poisson mean must be positive ({0})"),
+    NOT_POSITIVE_POPULATION_SIZE("population size must be positive ({0})"),
+    POPULATION_SIZE("population size ({0})"), /* keep */
+    NOT_POSITIVE_ROW_DIMENSION("invalid row dimension: {0} (must be positive)"),
+    NOT_POSITIVE_SAMPLE_SIZE("sample size must be positive ({0})"),
+    NOT_POSITIVE_SCALE("scale must be positive ({0})"),
+    SCALE("scale ({0})"), /* keep */
+    NOT_POSITIVE_SHAPE("shape must be positive ({0})"),
+    SHAPE("shape ({0})"), /* keep */
+    NOT_POSITIVE_STANDARD_DEVIATION("standard deviation must be positive ({0})"),
+    STANDARD_DEVIATION("standard deviation ({0})"), /* keep */
+    NOT_POSITIVE_UPPER_BOUND("upper bound must be positive ({0})"),
+    NOT_POSITIVE_WINDOW_SIZE("window size must be positive ({0})"),
+    NOT_POWER_OF_TWO("{0} is not a power of 2"),
+    NOT_POWER_OF_TWO_CONSIDER_PADDING("{0} is not a power of 2, consider padding for fix"),
+    NOT_POWER_OF_TWO_PLUS_ONE("{0} is not a power of 2 plus one"),
+    NOT_STRICTLY_DECREASING_NUMBER_OF_POINTS("points {0} and {1} are not strictly decreasing ({2} <= {3})"),
+    NOT_STRICTLY_DECREASING_SEQUENCE("points {3} and {2} are not strictly decreasing ({1} <= {0})"), /* keep */
+    NOT_STRICTLY_INCREASING_KNOT_VALUES("knot values must be strictly increasing"),
+    NOT_STRICTLY_INCREASING_NUMBER_OF_POINTS("points {0} and {1} are not strictly increasing ({2} >= {3})"),
+    NOT_STRICTLY_INCREASING_SEQUENCE("points {3} and {2} are not strictly increasing ({1} >= {0})"), /* keep */
+    NOT_SUBTRACTION_COMPATIBLE_MATRICES("{0}x{1} and {2}x{3} matrices are not subtraction compatible"),
+    NOT_SUPPORTED_IN_DIMENSION_N("method not supported in dimension {0}"),
+    NOT_SYMMETRIC_MATRIX("not symmetric matrix"),
+    NON_SYMMETRIC_MATRIX("non symmetric matrix: the difference between entries at ({0},{1}) and ({1},{0}) is larger than {2}"), /* keep */
+    NO_BIN_SELECTED("no bin selected"),
+    NO_CONVERGENCE_WITH_ANY_START_POINT("none of the {0} start points lead to convergence"), /* keep */
+    NO_DATA("no data"), /* keep */
+    NO_DEGREES_OF_FREEDOM("no degrees of freedom ({0} measurements, {1} parameters)"),
+    NO_DENSITY_FOR_THIS_DISTRIBUTION("This distribution does not have a density function implemented"),
+    NO_FEASIBLE_SOLUTION("no feasible solution"),
+    NO_OPTIMUM_COMPUTED_YET("no optimum computed yet"), /* keep */
+    NO_REGRESSORS("Regression model must include at least one regressor"),
+    NO_RESULT_AVAILABLE("no result available"),
+    NO_SUCH_MATRIX_ENTRY("no entry at indices ({0}, {1}) in a {2}x{3} matrix"),
+    NAN_NOT_ALLOWED("NaN is not allowed"),
+    NULL_NOT_ALLOWED("null is not allowed"), /* keep */
+    ARRAY_ZERO_LENGTH_OR_NULL_NOT_ALLOWED("a null or zero length array not allowed"),
+    COVARIANCE_MATRIX("covariance matrix"), /* keep */
+    DENOMINATOR("denominator"), /* keep */
+    DENOMINATOR_FORMAT("denominator format"), /* keep */
+    FRACTION("fraction"), /* keep */
+    FUNCTION("function"), /* keep */
+    IMAGINARY_FORMAT("imaginary format"), /* keep */
+    INPUT_ARRAY("input array"), /* keep */
+    NUMERATOR("numerator"), /* keep */
+    NUMERATOR_FORMAT("numerator format"), /* keep */
+    OBJECT_TRANSFORMATION("conversion exception in transformation"), /* keep */
+    REAL_FORMAT("real format"), /* keep */
+    WHOLE_FORMAT("whole format"), /* keep */
+    NUMBER_TOO_LARGE("{0} is larger than the maximum ({1})"), /* keep */
+    NUMBER_TOO_SMALL("{0} is smaller than the minimum ({1})"), /* keep */
+    NUMBER_TOO_LARGE_BOUND_EXCLUDED("{0} is larger than, or equal to, the maximum ({1})"), /* keep */
+    NUMBER_TOO_SMALL_BOUND_EXCLUDED("{0} is smaller than, or equal to, the minimum ({1})"), /* keep */
+    NUMBER_OF_SUCCESS_LARGER_THAN_POPULATION_SIZE("number of successes ({0}) must be less than or equal to population size ({1})"),
+    NUMERATOR_OVERFLOW_AFTER_MULTIPLY("overflow, numerator too large after multiply: {0}"),
+    N_POINTS_GAUSS_LEGENDRE_INTEGRATOR_NOT_SUPPORTED("{0} points Legendre-Gauss integrator not supported, number of points must be in the {1}-{2} range"),
+    OBSERVED_COUNTS_ALL_ZERO("observed counts are all 0 in observed array {0}"),
+    OBSERVED_COUNTS_BOTTH_ZERO_FOR_ENTRY("observed counts are both zero for entry {0}"),
+    BOBYQA_BOUND_DIFFERENCE_CONDITION("the difference between the upper and lower bound must be larger than twice the initial trust region radius ({0})"),
+    OUT_OF_BOUNDS_QUANTILE_VALUE("out of bounds quantile value: {0}, must be in (0, 100]"),
+    OUT_OF_BOUNDS_CONFIDENCE_LEVEL("out of bounds confidence level {0}, must be between {1} and {2}"),
+    OUT_OF_BOUND_SIGNIFICANCE_LEVEL("out of bounds significance level {0}, must be between {1} and {2}"),
+    SIGNIFICANCE_LEVEL("significance level ({0})"), /* keep */
+    OUT_OF_ORDER_ABSCISSA_ARRAY("the abscissae array must be sorted in a strictly increasing order, but the {0}-th element is {1} whereas {2}-th is {3}"),
+    OUT_OF_PLANE("point ({0}, {1}, {2}) is out of plane"),
+    OUT_OF_RANGE_ROOT_OF_UNITY_INDEX("out of range root of unity index {0} (must be in [{1};{2}])"),
+    OUT_OF_RANGE("out of range"), /* keep */
+    OUT_OF_RANGE_SIMPLE("{0} out of [{1}, {2}] range"), /* keep */
+    OUT_OF_RANGE_LEFT("{0} out of ({1}, {2}] range"),
+    OUT_OF_RANGE_RIGHT("{0} out of [{1}, {2}) range"),
+    OUTLINE_BOUNDARY_LOOP_OPEN("an outline boundary loop is open"),
+    OVERFLOW("overflow"), /* keep */
+    OVERFLOW_IN_FRACTION("overflow in fraction {0}/{1}, cannot negate"),
+    OVERFLOW_IN_ADDITION("overflow in addition: {0} + {1}"),
+    OVERFLOW_IN_SUBTRACTION("overflow in subtraction: {0} - {1}"),
+    OVERFLOW_IN_MULTIPLICATION("overflow in multiplication: {0} * {1}"),
+    PERCENTILE_IMPLEMENTATION_CANNOT_ACCESS_METHOD("cannot access {0} method in percentile implementation {1}"),
+    PERCENTILE_IMPLEMENTATION_UNSUPPORTED_METHOD("percentile implementation {0} does not support {1}"),
+    PERMUTATION_EXCEEDS_N("permutation size ({0}) exceeds permuation domain ({1})"), /* keep */
+    POLYNOMIAL("polynomial"), /* keep */
+    POLYNOMIAL_INTERPOLANTS_MISMATCH_SEGMENTS("number of polynomial interpolants must match the number of segments ({0} != {1} - 1)"),
+    POPULATION_LIMIT_NOT_POSITIVE("population limit has to be positive"),
+    POWER_NEGATIVE_PARAMETERS("cannot raise an integral value to a negative power ({0}^{1})"),
+    PROPAGATION_DIRECTION_MISMATCH("propagation direction mismatch"),
+    RANDOMKEY_MUTATION_WRONG_CLASS("RandomKeyMutation works only with RandomKeys, not {0}"),
+    ROOTS_OF_UNITY_NOT_COMPUTED_YET("roots of unity have not been computed yet"),
+    ROTATION_MATRIX_DIMENSIONS("a {0}x{1} matrix cannot be a rotation matrix"),
+    ROW_INDEX_OUT_OF_RANGE("row index {0} out of allowed range [{1}, {2}]"),
+    ROW_INDEX("row index ({0})"), /* keep */
+    SAME_SIGN_AT_ENDPOINTS("function values at endpoints do not have different signs, endpoints: [{0}, {1}], values: [{2}, {3}]"),
+    SAMPLE_SIZE_EXCEEDS_COLLECTION_SIZE("sample size ({0}) exceeds collection size ({1})"), /* keep */
+    SAMPLE_SIZE_LARGER_THAN_POPULATION_SIZE("sample size ({0}) must be less than or equal to population size ({1})"),
+    SIMPLEX_NEED_ONE_POINT("simplex must contain at least one point"),
+    SIMPLE_MESSAGE("{0}"),
+    SINGULAR_MATRIX("matrix is singular"), /* keep */
+    SINGULAR_OPERATOR("operator is singular"),
+    SUBARRAY_ENDS_AFTER_ARRAY_END("subarray ends after array end"),
+    TOO_LARGE_CUTOFF_SINGULAR_VALUE("cutoff singular value is {0}, should be at most {1}"),
+    TOO_LARGE_TOURNAMENT_ARITY("tournament arity ({0}) cannot be bigger than population size ({1})"),
+    TOO_MANY_ELEMENTS_TO_DISCARD_FROM_ARRAY("cannot discard {0} elements from a {1} elements array"),
+    TOO_MANY_REGRESSORS("too many regressors ({0}) specified, only {1} in the model"),
+    TOO_SMALL_COST_RELATIVE_TOLERANCE("cost relative tolerance is too small ({0}), no further reduction in the sum of squares is possible"),
+    TOO_SMALL_INTEGRATION_INTERVAL("too small integration interval: length = {0}"),
+    TOO_SMALL_ORTHOGONALITY_TOLERANCE("orthogonality tolerance is too small ({0}), solution is orthogonal to the jacobian"),
+    TOO_SMALL_PARAMETERS_RELATIVE_TOLERANCE("parameters relative tolerance is too small ({0}), no further improvement in the approximate solution is possible"),
+    TRUST_REGION_STEP_FAILED("trust region step has failed to reduce Q"),
+    TWO_OR_MORE_CATEGORIES_REQUIRED("two or more categories required, got {0}"),
+    TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED("two or more values required in each category, one has {0}"),
+    UNABLE_TO_BRACKET_OPTIMUM_IN_LINE_SEARCH("unable to bracket optimum in line search"),
+    UNABLE_TO_COMPUTE_COVARIANCE_SINGULAR_PROBLEM("unable to compute covariances: singular problem"),
+    UNABLE_TO_FIRST_GUESS_HARMONIC_COEFFICIENTS("unable to first guess the harmonic coefficients"),
+    UNABLE_TO_ORTHOGONOLIZE_MATRIX("unable to orthogonalize matrix in {0} iterations"),
+    UNABLE_TO_PERFORM_QR_DECOMPOSITION_ON_JACOBIAN("unable to perform Q.R decomposition on the {0}x{1} jacobian matrix"),
+    UNABLE_TO_SOLVE_SINGULAR_PROBLEM("unable to solve: singular problem"),
+    UNBOUNDED_SOLUTION("unbounded solution"),
+    UNKNOWN_MODE("unknown mode {0}, known modes: {1} ({2}), {3} ({4}), {5} ({6}), {7} ({8}), {9} ({10}) and {11} ({12})"),
+    UNKNOWN_PARAMETER("unknown parameter {0}"),
+    UNMATCHED_ODE_IN_EXPANDED_SET("ode does not match the main ode set in the extended set"),
+    CANNOT_PARSE_AS_TYPE("string \"{0}\" unparseable (from position {1}) as an object of type {2}"), /* keep */
+    CANNOT_PARSE("string \"{0}\" unparseable (from position {1})"), /* keep */
+    UNPARSEABLE_3D_VECTOR("unparseable 3D vector: \"{0}\""),
+    UNPARSEABLE_COMPLEX_NUMBER("unparseable complex number: \"{0}\""),
+    UNPARSEABLE_REAL_VECTOR("unparseable real vector: \"{0}\""),
+    UNSUPPORTED_EXPANSION_MODE("unsupported expansion mode {0}, supported modes are {1} ({2}) and {3} ({4})"),
+    UNSUPPORTED_OPERATION("unsupported operation"), /* keep */
+    ARITHMETIC_EXCEPTION("arithmetic exception"), /* keep */
+    ILLEGAL_STATE("illegal state"), /* keep */
+    USER_EXCEPTION("exception generated in user code"), /* keep */
+    URL_CONTAINS_NO_DATA("URL {0} contains no data"),
+    VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC("{0} values have been added before statistic is configured"),
+    VECTOR_LENGTH_MISMATCH("vector length mismatch: got {0} but expected {1}"),
+    VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT("vector must have at least one element"),
+    WEIGHT_AT_LEAST_ONE_NON_ZERO("weigth array must contain at least one non-zero value"),
+    WRONG_BLOCK_LENGTH("wrong array shape (block length = {0}, expected {1})"),
+    WRONG_NUMBER_OF_POINTS("{0} points are required, got only {1}"),
+    NUMBER_OF_POINTS("number of points ({0})"), /* keep */
+    ZERO_DENOMINATOR("denominator must be different from 0"), /* keep */
+    ZERO_DENOMINATOR_IN_FRACTION("zero denominator in fraction {0}/{1}"),
+    ZERO_FRACTION_TO_DIVIDE_BY("the fraction to divide by must not be zero: {0}/{1}"),
+    ZERO_NORM("zero norm"),
+    ZERO_NORM_FOR_ROTATION_AXIS("zero norm for rotation axis"),
+    ZERO_NORM_FOR_ROTATION_DEFINING_VECTOR("zero norm for rotation defining vector"),
+    ZERO_NOT_ALLOWED("zero not allowed here");
+
+    // CHECKSTYLE: resume JavadocVariable
+    // CHECKSTYLE: resume MultipleVariableDeclarations
+
+
+    /** Source English format. */
+    private final String sourceFormat;
+
+    /** Simple constructor.
+     * @param sourceFormat source English format to use when no
+     * localized version is available
+     */
+    LocalizedFormats(final String sourceFormat) {
+        this.sourceFormat = sourceFormat;
+    }
+
+    /** {@inheritDoc} */
+    public String getSourceString() {
+        return sourceFormat;
+    }
+
+    /** {@inheritDoc} */
+    public String getLocalizedString(final Locale locale) {
+        try {
+            final String path = LocalizedFormats.class.getName().replaceAll("\\.", "/");
+            ResourceBundle bundle =
+                    ResourceBundle.getBundle("assets/" + path, locale);
+            if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) {
+                // the value of the resource is the translated format
+                return bundle.getString(toString());
+            }
+
+        } catch (MissingResourceException mre) { // NOPMD
+            // do nothing here
+        }
+
+        // either the locale is not supported or the resource is unknown
+        // don't translate and fall back to using the source format
+        return sourceFormat;
+
+    }
+
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/Primes.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/Primes.java
new file mode 100644
index 0000000000000000000000000000000000000000..12cd317bd6dca3df63f8589fe3b6e4828357b4cb
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/Primes.java
@@ -0,0 +1,129 @@
+/*
+ * 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 org.apache.commons.math3.primes;
+
+import org.apache.commons.math3.exception.MathIllegalArgumentException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+
+import java.util.List;
+
+
+/**
+ * Methods related to prime numbers in the range of <code>int</code>:
+ * <ul>
+ * <li>primality test</li>
+ * <li>prime number generation</li>
+ * <li>factorization</li>
+ * </ul>
+ *
+ * @since 3.2
+ */
+public class Primes {
+
+    /**
+     * Hide utility class.
+     */
+    private Primes() {
+    }
+
+    /**
+     * Primality test: tells if the argument is a (provable) prime or not.
+     * <p>
+     * It uses the Miller-Rabin probabilistic test in such a way that a result is guaranteed:
+     * it uses the firsts prime numbers as successive base (see Handbook of applied cryptography
+     * by Menezes, table 4.1).
+     *
+     * @param n number to test.
+     * @return true if n is prime. (All numbers &lt; 2 return false).
+     */
+    public static boolean isPrime(int n) {
+        if (n < 2) {
+            return false;
+        }
+
+        for (int p : SmallPrimes.PRIMES) {
+            if (0 == (n % p)) {
+                return n == p;
+            }
+        }
+        return SmallPrimes.millerRabinPrimeTest(n);
+    }
+
+    /**
+     * Return the smallest prime greater than or equal to n.
+     *
+     * @param n a positive number.
+     * @return the smallest prime greater than or equal to n.
+     * @throws MathIllegalArgumentException if n &lt; 0.
+     */
+    public static int nextPrime(int n) {
+        if (n < 0) {
+            throw new MathIllegalArgumentException(LocalizedFormats.NUMBER_TOO_SMALL, n, 0);
+        }
+        if (n == 2) {
+            return 2;
+        }
+        n |= 1;//make sure n is odd
+        if (n == 1) {
+            return 2;
+        }
+
+        if (isPrime(n)) {
+            return n;
+        }
+
+        // prepare entry in the +2, +4 loop:
+        // n should not be a multiple of 3
+        final int rem = n % 3;
+        if (0 == rem) { // if n % 3 == 0
+            n += 2; // n % 3 == 2
+        } else if (1 == rem) { // if n % 3 == 1
+            // if (isPrime(n)) return n;
+            n += 4; // n % 3 == 2
+        }
+        while (true) { // this loop skips all multiple of 3
+            if (isPrime(n)) {
+                return n;
+            }
+            n += 2; // n % 3 == 1
+            if (isPrime(n)) {
+                return n;
+            }
+            n += 4; // n % 3 == 2
+        }
+    }
+
+    /**
+     * Prime factors decomposition
+     *
+     * @param n number to factorize: must be &ge; 2
+     * @return list of prime factors of n
+     * @throws MathIllegalArgumentException if n &lt; 2.
+     */
+    public static List<Integer> primeFactors(int n) {
+
+        if (n < 2) {
+            throw new MathIllegalArgumentException(LocalizedFormats.NUMBER_TOO_SMALL, n, 2);
+        }
+        // slower than trial div unless we do an awful lot of computation
+        // (then it finally gets JIT-compiled efficiently
+        // List<Integer> out = PollardRho.primeFactors(n);
+        return SmallPrimes.trialDivision(n);
+
+    }
+
+}
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/SmallPrimes.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/SmallPrimes.java
new file mode 100644
index 0000000000000000000000000000000000000000..c889f10dc8dec0989e914697b68ed95a2d0b4896
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/primes/SmallPrimes.java
@@ -0,0 +1,188 @@
+/*
+ * 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 org.apache.commons.math3.primes;
+
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * Utility methods to work on primes within the <code>int</code> range.
+ * @since 3.2
+ */
+class SmallPrimes {
+
+    /**
+     * The first 512 prime numbers.
+     * <p>
+     * It contains all primes smaller or equal to the cubic square of Integer.MAX_VALUE.
+     * As a result, <code>int</code> numbers which are not reduced by those primes are guaranteed
+     * to be either prime or semi prime.
+     */
+    public static final int[] PRIMES = {2,
+            3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
+            79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179,
+            181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
+            293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
+            421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547,
+            557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661,
+            673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
+            821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
+            953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087,
+            1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229,
+            1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381,
+            1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523,
+            1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663,
+            1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823,
+            1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993,
+            1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131,
+            2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293,
+            2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437,
+            2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621,
+            2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749,
+            2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909,
+            2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083,
+            3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259,
+            3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433,
+            3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581,
+            3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671};
+
+    /** The last number in PRIMES. */
+    public static final int PRIMES_LAST = PRIMES[PRIMES.length - 1];
+
+    /**
+     * Hide utility class.
+     */
+    private SmallPrimes() {
+    }
+
+    /**
+     * Extract small factors.
+     * @param n the number to factor, must be &gt; 0.
+     * @param factors the list where to add the factors.
+     * @return the part of n which remains to be factored, it is either a prime or a semi-prime
+     */
+    public static int smallTrialDivision(int n, final List<Integer> factors) {
+        for (int p : PRIMES) {
+            while (0 == n % p) {
+                n /= p;
+                factors.add(p);
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Extract factors in the range <code>PRIME_LAST+2</code> to <code>maxFactors</code>.
+     * @param n the number to factorize, must be >= PRIME_LAST+2 and must not contain any factor below PRIME_LAST+2
+     * @param maxFactor the upper bound of trial division: if it is reached, the method gives up and returns n.
+     * @param factors the list where to add the factors.
+     * @return  n or 1 if factorization is completed.
+     */
+    public static int boundedTrialDivision(int n, int maxFactor, List<Integer> factors) {
+        int f = PRIMES_LAST + 2;
+        // no check is done about n >= f
+        while (f <= maxFactor) {
+            if (0 == n % f) {
+                n /= f;
+                factors.add(f);
+                break;
+            }
+            f += 4;
+            if (0 == n % f) {
+                n /= f;
+                factors.add(f);
+                break;
+            }
+            f += 2;
+        }
+        if (n != 1) {
+            factors.add(n);
+        }
+        return n;
+    }
+
+    /**
+     * Factorization by trial division.
+     * @param n the number to factor
+     * @return the list of prime factors of n
+     */
+    public static List<Integer> trialDivision(int n){
+        final List<Integer> factors = new ArrayList<Integer>(32);
+        n = smallTrialDivision(n, factors);
+        if (1 == n) {
+            return factors;
+        }
+        // here we are sure that n is either a prime or a semi prime
+        final int bound = (int) FastMath.sqrt(n);
+        boundedTrialDivision(n, bound, factors);
+        return factors;
+    }
+
+    /**
+     * Miller-Rabin probabilistic primality test for int type, used in such a way that a result is always guaranteed.
+     * <p>
+     * It uses the prime numbers as successive base therefore it is guaranteed to be always correct.
+     * (see Handbook of applied cryptography by Menezes, table 4.1)
+     *
+     * @param n number to test: an odd integer &ge; 3
+     * @return true if n is prime. false if n is definitely composite.
+     */
+    public static boolean millerRabinPrimeTest(final int n) {
+        final int nMinus1 = n - 1;
+        final int s = Integer.numberOfTrailingZeros(nMinus1);
+        final int r = nMinus1 >> s;
+        //r must be odd, it is not checked here
+        int t = 1;
+        if (n >= 2047) {
+            t = 2;
+        }
+        if (n >= 1373653) {
+            t = 3;
+        }
+        if (n >= 25326001) {
+            t = 4;
+        } // works up to 3.2 billion, int range stops at 2.7 so we are safe :-)
+        BigInteger br = BigInteger.valueOf(r);
+        BigInteger bn = BigInteger.valueOf(n);
+
+        for (int i = 0; i < t; i++) {
+            BigInteger a = BigInteger.valueOf(SmallPrimes.PRIMES[i]);
+            BigInteger bPow = a.modPow(br, bn);
+            int y = bPow.intValue();
+            if ((1 != y) && (y != nMinus1)) {
+                int j = 1;
+                while ((j <= s - 1) && (nMinus1 != y)) {
+                    long square = ((long) y) * y;
+                    y = (int) (square % n);
+                    if (1 == y) {
+                        return false;
+                    } // definitely composite
+                    j++;
+                }
+                if (nMinus1 != y) {
+                    return false;
+                } // definitely composite
+            }
+        }
+        return true; // definitely prime
+    }
+}
+
diff --git a/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/util/FastMath.java b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/util/FastMath.java
new file mode 100644
index 0000000000000000000000000000000000000000..3d0e4354c5dfb8f49137cc7bb3af1f402efd1025
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/org/apache/commons/math3/util/FastMath.java
@@ -0,0 +1,11 @@
+package org.apache.commons.math3.util;
+
+public class FastMath {
+
+	public static double sqrt(int d) {
+		// The original implementation of FastMath.java in Apache Commons Math delegates
+		// to Math.sqrt too. However, its FastMath has many more methods in that we drag
+		// in many dependencies. We don't want nor need those dependencies.
+		return Math.sqrt(d);
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/pcal/PcalParams.java b/tlatools/org.lamport.tlatools/src/pcal/PcalParams.java
index b83937d9a73257838721061f012af9d527efbfb4..22de7b25fba05f028e88a78dc3270dbfa8322ffe 100644
--- a/tlatools/org.lamport.tlatools/src/pcal/PcalParams.java
+++ b/tlatools/org.lamport.tlatools/src/pcal/PcalParams.java
@@ -191,17 +191,10 @@ public final class PcalParams
     ***********************************************************************/
     public static final String BeginXlation1 = "BEGIN" ;
     public static final String BeginXlation2 = "TRANSLATION" ;
-    public static final String BeginXlation3 = "- the hash of the PCal code:" ;
 
     public static final String EndXlation1 = "END" ;
     public static final String EndXlation2 = "TRANSLATION" ;
-    public static final String EndXlation3 = "- the hash of the generated TLA code (remove "
-												+ "to silence divergence warnings):" ;
     
-    // Checksum marker keywords - introduced as part of https://github.com/tlaplus/tlaplus/issues/296
-    public static final String PCAL_CHECKSUM_KEYWORD = "PCal-";
-    public static final String TRANSLATED_PCAL_CHECKSUM_KEYWORD = "TLA-";
-
   /*************************************************************************
   * The string identifying the end of the automatically generated part of  *
   * the .cfg file and the beginning of the user-added part.                *
diff --git a/tlatools/org.lamport.tlatools/src/pcal/Translator.java b/tlatools/org.lamport.tlatools/src/pcal/Translator.java
index ade7335dd052d84c2e0fbaa306203dd795aa1082..b9a880530b8e338c588c4a907e31a4a090bc9102 100644
--- a/tlatools/org.lamport.tlatools/src/pcal/Translator.java
+++ b/tlatools/org.lamport.tlatools/src/pcal/Translator.java
@@ -62,14 +62,14 @@ public class Translator
      * @param args
      * @return 
      */
-	public boolean translate() {
+	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);
+		final List<String> out = trans.performTranslation(in, cb);
 		if (out != null) {
 			final StringBuilder buf = new StringBuilder();
 			final String lineSeparator = System.getProperty("line.separator");
diff --git a/tlatools/org.lamport.tlatools/src/pcal/ValidationCallBack.java b/tlatools/org.lamport.tlatools/src/pcal/ValidationCallBack.java
new file mode 100644
index 0000000000000000000000000000000000000000..91c8558039998c133aa0a367d0179c3926c997e1
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/src/pcal/Validator.java b/tlatools/org.lamport.tlatools/src/pcal/Validator.java
index b42f0d0b9f0337521f53203dadf4ff9e824181c2..2c3660e8c2c3b9d51a510891393fd702b17f0549 100644
--- a/tlatools/org.lamport.tlatools/src/pcal/Validator.java
+++ b/tlatools/org.lamport.tlatools/src/pcal/Validator.java
@@ -1,19 +1,24 @@
 package pcal;
 
 import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+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;
 
 /**
@@ -21,6 +26,7 @@ import util.TLAConstants;
  * blocks.
  */
 public class Validator {
+
 	public enum ValidationResult {
 		/** No PlusCal algorithm exists in the specification */
 		NO_PLUSCAL_EXISTS,
@@ -28,11 +34,11 @@ public class Validator {
 		/** No translation exists - BUT THERE IS PLUSCAL IN THE SPECIFICATION! */
 		NO_TRANSLATION_EXISTS,
 		
-		/** PlusCal and a translation block exist, but there are no checksums calculated. */
-		NO_CHECKSUMS_EXIST,
+		NO_TLA_CHECKSUMS_EXIST,
+		NO_PCAL_CHECKSUMS_EXIST,
 		
-		/** One or both Checksum in the spec do not match the checksums calculated for what was found in the spec. */
-		DIVERGENCE_EXISTS,
+		TLA_DIVERGENCE_EXISTS,
+		PCAL_DIVERGENCE_EXISTS,
 		
 		/** A Java error was encountered while attempting to validate. */
 		ERROR_ENCOUNTERED,
@@ -41,72 +47,69 @@ public class Validator {
 		NO_DIVERGENCE;
 	}
 	
+	static final String PCAL_CHECKSUM = "pcalchecksum";
+	static final String TLA_CHECKSUM = "tlachecksum";
 	
-	static protected boolean PRE_TRIM_VALIDATION_CONTENT = true;
+	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 PCAL_CHECKSUM_PATTERN = Pattern.compile(PcalParams.PCAL_CHECKSUM_KEYWORD + "[0-9a-f]+$");
-	static final Pattern TRANSLATED_PCAL_CHECKSUM_PATTERN
-									= Pattern.compile(PcalParams.TRANSLATED_PCAL_CHECKSUM_KEYWORD + "[0-9a-f]+$");
+	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_PREFIX_PATTERN = Pattern.compile(TLAConstants.MODULE_OPENING_PREFIX_REGEX);
 	private static final Pattern MODULE_CLOSING_PATTERN = Pattern.compile(TLAConstants.MODULE_CLOSING_REGEX);
-	
-	/*
-	 * This regex is appropriate for only pre-MODULE-lines; the PlusCal specification states that the options line
-	 *  	may exist before the module, after the module, or within the module in a comment line or block. For our
-	 *  	purposes, we only care if it exists before the module and therefore the following regex.
-	 */
-	private static final Pattern PLUSCAL_OPTIONS = Pattern.compile("^[\\s]*PlusCal[\\s]+options",
-																   Pattern.CASE_INSENSITIVE);
 
-	/**
-	 * This method is a convenience wrapped around {@link #validate(List)}.
-	 * 
-	 * @param inputStream an stream to the entire specification to be validated; this stream is not closed.
-	 * @return the result of the validation, as enumerated by the inner enum of this class.
-	 */
-	public static ValidationResult validate(final InputStream inputStream)
+	public static Set<ValidationResult> validate(ParseUnit parseUnit, InputStream inputStream)
 			throws IOException {
-        final String specContents;
-    	final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    	final byte[] buffer = new byte[4096];
-    	final BufferedInputStream bis = (inputStream instanceof BufferedInputStream)
-    											? (BufferedInputStream)inputStream
-    											: new BufferedInputStream(inputStream);
-    	
-        int bytesRead;
-        while ((bytesRead = bis.read(buffer)) != -1) {
-        	baos.write(buffer, 0, bytesRead);
-        }
-        
-    	specContents = new String(baos.toByteArray(), "UTF-8");
-
-		final String[] lines = specContents.split("\\r?\\n");
-		int startLine = -1;
-		if (PRE_TRIM_VALIDATION_CONTENT) {
-			for (int i = 0; i < lines.length; i++) {
-				final Matcher m = MODULE_PREFIX_PATTERN.matcher(lines[i]);
-				if (m.find()) {
-					startLine = i;
-					break;
-				} else {
-					final Matcher m2 = PLUSCAL_OPTIONS.matcher(lines[i]);
-					if (m2.find()) {
-						startLine = i;
-						break;
-					}
+		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 (startLine < 1) {
-			return validate(Arrays.asList(lines));
-		} else {
-			final int reducedLength = lines.length - startLine;
-			final String[] reducedLines = new String[reducedLength];
-
-			System.arraycopy(lines, startLine, reducedLines, 0, reducedLength);
-			return validate(Arrays.asList(reducedLines));
+		
+		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);
 	}
 	
 	/**
@@ -116,7 +119,7 @@ public class Validator {
 	 * @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 ValidationResult validate(final List<String> specificationText) {
+	private static Set<ValidationResult>  validate(final List<String> specificationText) {
         final Vector<String> deTabbedSpecification = trans.removeTabs(specificationText);
 		
         final IntPair searchLoc = new IntPair(0, 0);
@@ -187,104 +190,108 @@ public class Validator {
             	} 
             }
         }
-		if (!foundBegin) {
-			return ValidationResult.NO_PLUSCAL_EXISTS;
-		}
+	       
+		final Set<ValidationResult> res = new HashSet<>();
 
+		
+		// TLA+ translation
 		final int translationLine = trans.findTokenPair(deTabbedSpecification, 0,
 														PcalParams.BeginXlation1, PcalParams.BeginXlation2);
-        final String pcalMD5;
-        final String translatedMD5;
 		if (translationLine == -1) {
-            return ValidationResult.NO_TRANSLATION_EXISTS;
-		} else {
-			final int endTranslationLine = trans.findTokenPair(deTabbedSpecification, translationLine + 1,
-															   PcalParams.EndXlation1, PcalParams.EndXlation2);
-			if (endTranslationLine == -1) {
-                return ValidationResult.NO_TRANSLATION_EXISTS;
-            }
+			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;
+		}
 
-			final String beginLine = deTabbedSpecification.get(translationLine);
-        	Matcher m = Validator.PCAL_CHECKSUM_PATTERN.matcher(beginLine);
-        	if (m.find()) {
-        		pcalMD5 = beginLine.substring(m.start() + PcalParams.PCAL_CHECKSUM_KEYWORD.length());
-        	} else {
-        		return ValidationResult.NO_CHECKSUMS_EXIST;
-        	}
-        	final String endLine = deTabbedSpecification.get(endTranslationLine);
-        	m = Validator.TRANSLATED_PCAL_CHECKSUM_PATTERN.matcher(endLine);
-        	if (m.find()) {
-        		translatedMD5 = endLine.substring(m.start() + PcalParams.TRANSLATED_PCAL_CHECKSUM_KEYWORD.length());
+		// PlusCal algorithm
+		if (!foundBegin) {
+			res.add(ValidationResult.NO_PLUSCAL_EXISTS);
+		} else {
 
-            	final Vector<String> translation = new Vector<>(specificationText.subList((translationLine + 1),
-            																			   endTranslationLine));
-            	final String calculatedMD5 = calculateMD5(translation);
-            	if (!translatedMD5.equals(calculatedMD5)) {
-            		return ValidationResult.DIVERGENCE_EXISTS;
-            	}
-        	} else {
-        		translatedMD5 = null;
-        	}
-        }
-        
-		try {
-			ParseAlgorithm.uncomment(deTabbedSpecification, algLine, algCol);
-		} catch (ParseAlgorithmException e) {
-            PcalDebug.reportError(e);
-            return ValidationResult.ERROR_ENCOUNTERED;
-        }
+	    	// 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) {
 
-		// This seems like crazy poor design - we're already passing around algLine and algCol, but if we don't make
-		//	this arbitrary object, throw it into a global public static setting, and also assign values to it there,
-		//	then the ParseAlgorithm won't pick up the values..
-        final TLAtoPCalMapping mapping = new TLAtoPCalMapping() ;
-        mapping.algColumn = algCol;
-        mapping.algLine = algLine;
-        PcalParams.tlaPcalMapping = mapping;
-		
-		final PcalCharReader reader = new PcalCharReader(deTabbedSpecification, algLine, algCol,
-				specificationText.size(), 0);
-		final AST ast;
-		try {
-			ast = ParseAlgorithm.getAlgorithm(reader, foundFairBegin);
-		} catch (ParseAlgorithmException e) {
-			PcalDebug.reportError(e);
-			return ValidationResult.ERROR_ENCOUNTERED;
+	    			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);
+	    	}
 		}
-        
-        final String calculatedMD5 = Validator.calculateMD5(ast.toString());
-    	if (!pcalMD5.equals(calculatedMD5)) {
-    		return ValidationResult.DIVERGENCE_EXISTS;
-    	}
-
-		return ValidationResult.NO_DIVERGENCE;
+		return res.isEmpty() ? setOf(ValidationResult.NO_DIVERGENCE) : res;
 	}
     
-    static String calculateMD5(final Vector<String> lines) {
+    public static String checksum(final Vector<String> lines) {
     	final StringBuilder sb = new StringBuilder();
     	for (final String str : lines) {
     		sb.append(str);
     	}
     	
-    	return calculateMD5(sb.toString());
+    	return checksum(sb.toString());
     }
     
-    static String calculateMD5(final String string) {
-    	try {
-        	final MessageDigest digest = MessageDigest.getInstance("MD5");
-        	final byte[] hash = digest.digest(string.getBytes(StandardCharsets.UTF_8));
-        	final StringBuffer hexString = new StringBuffer();
-			for (int i = 0; i < hash.length; i++) {
-				final String hex = Integer.toHexString(0xff & hash[i]);
-				if (hex.length() == 1) {
-					hexString.append('0');
-				}
-				hexString.append(hex);
-			}
-            return hexString.toString();
-    	} catch (final NoSuchAlgorithmException e) {
-    		PcalDebug.reportError("Unable to calculate MD5: " + e.getMessage());
-    		return null;
-    	}
+    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/org.lamport.tlatools/src/pcal/trans.java b/tlatools/org.lamport.tlatools/src/pcal/trans.java
index 57fe07a9ce91a59c4606deb5d9ffe1899f6b17fe..428dbcda55863f27945be154b49e9ac8b6d99dad 100644
--- a/tlatools/org.lamport.tlatools/src/pcal/trans.java
+++ b/tlatools/org.lamport.tlatools/src/pcal/trans.java
@@ -13,6 +13,7 @@ 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;
@@ -290,9 +291,9 @@ class trans {
     static final int STATUS_EXIT_WITH_ERRORS = -1;
     
     private static final String PCAL_TRANSLATION_COMMENT_LINE_PREFIX
-    		= "\\* " + PcalParams.BeginXlation1 + " " + PcalParams.BeginXlation2 + " " + PcalParams.BeginXlation3;
+    		= "\\* " + PcalParams.BeginXlation1 + " " + PcalParams.BeginXlation2;
     private static final String TLA_TRANSLATION_COMMENT_LINE_PREFIX
-    		= "\\* " + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2 + " " + PcalParams.EndXlation3;
+    		= "\\* " + PcalParams.EndXlation1 + " " + PcalParams.EndXlation2;
     
     
     /**
@@ -570,6 +571,10 @@ class trans {
     // 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.
@@ -679,9 +684,10 @@ class trans {
     	final ArrayList<String> output = new ArrayList<>(specificationText);
 
         translationLine = findTokenPair(untabInputVec, 0, PcalParams.BeginXlation1, PcalParams.BeginXlation2);
+        int endTranslationLine = -1;
         if (translationLine != -1)
         {
-            int endTranslationLine = findTokenPair(untabInputVec, translationLine + 1,
+            endTranslationLine = findTokenPair(untabInputVec, translationLine + 1,
             									   PcalParams.EndXlation1, PcalParams.EndXlation2);
             if (endTranslationLine == -1)
             {
@@ -689,12 +695,13 @@ class trans {
                 return null;
             }
 
-            endTranslationLine--;
-            while (translationLine < endTranslationLine)
+            
+            int etl = endTranslationLine - 1;
+            while (translationLine < etl)
             {
-            	output.remove(endTranslationLine);
-                untabInputVec.remove(endTranslationLine);
-                endTranslationLine--;
+            	output.remove(etl);
+                untabInputVec.remove(etl);
+                etl--;
             }
         }
 
@@ -744,7 +751,7 @@ class trans {
          */
         mapping.algColumn = algCol;
         mapping.algLine = algLine;
-        
+
         if (translationLine == -1) 
         {
            /****************************************************************
@@ -836,7 +843,8 @@ class trans {
                 return null ;
             } ;
             
-			output.add((ecLine + 1), (PCAL_TRANSLATION_COMMENT_LINE_PREFIX + " "));
+			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));
@@ -845,26 +853,18 @@ class trans {
 //System.out.println(ecLine + ", " + ecCol);
 //Debug.printVector(inputVec, "foo");
         }
-        else {
-        	// if it has an existing checksum suffix then get rid of it
-        	final String originalBeginLine = output.remove(translationLine);
-        	Matcher m = Validator.PCAL_CHECKSUM_PATTERN.matcher(originalBeginLine);
-        	String outputLine;
-        	if (m.find()) {
-        		outputLine = PCAL_TRANSLATION_COMMENT_LINE_PREFIX + " ";
-        	} else {
-        		outputLine = originalBeginLine + " ";
-        	}
-			output.add(translationLine, outputLine);
-			
-        	final String originalEndLine = output.remove(translationLine + 1);
-        	m = Validator.TRANSLATED_PCAL_CHECKSUM_PATTERN.matcher(originalEndLine);
-        	if (m.find()) {
-        		outputLine = TLA_TRANSLATION_COMMENT_LINE_PREFIX + " ";
-        	} else {
-        		outputLine = originalEndLine + " ";
-        	}
-			output.add((translationLine + 1), outputLine);
+        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;
+				}
+			}
         }
         
         /*
@@ -922,7 +922,6 @@ class trans {
         }
         PcalDebug.reportInfo("Parsing completed.");
         
-        final String pcalMD5 = Validator.calculateMD5(ast.toString());
 // tla-pcal debugging
 //System.out.println("Translation Output:");
 //System.out.println(ast.toString());
@@ -995,13 +994,34 @@ class trans {
                 return null ; // added for testing
             }
         }
-        
-        final String beginLine = output.remove(mapping.tlaStartLine - 1);
-        output.add((mapping.tlaStartLine - 1), (beginLine + PcalParams.PCAL_CHECKSUM_KEYWORD + pcalMD5));
-        
-        final String translationMD5 = Validator.calculateMD5(translation);
-        final String endLine = output.remove(mapping.tlaStartLine);
-		output.add(mapping.tlaStartLine, (endLine + PcalParams.TRANSLATED_PCAL_CHECKSUM_KEYWORD + translationMD5));
+
+		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.                                  *
@@ -1924,6 +1944,14 @@ class trans {
                 }
                 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);
         }
 
diff --git a/tlatools/org.lamport.tlatools/src/tla2sany/drivers/SANY.java b/tlatools/org.lamport.tlatools/src/tla2sany/drivers/SANY.java
index ac724a869dacc2b4d97fc2093bc32ba83ef7a7b9..8d2d49ced5cfb88083e6fef0de5c2325ffff78be 100644
--- a/tlatools/org.lamport.tlatools/src/tla2sany/drivers/SANY.java
+++ b/tlatools/org.lamport.tlatools/src/tla2sany/drivers/SANY.java
@@ -47,14 +47,14 @@ public class SANY {
 //    ***********************************************************************/
 //    "last modified on Tue  10 February 2011 at 11:49:54 PST by lamport";
 
-  private static String modDate = "24 February 2014";
+  private static String modDate = "08 July 2020";
 //                lastModified.substring(21, lastModified.indexOf(" at"));
     /***********************************************************************
     * The modification date.                                               *
     ***********************************************************************/
 
   public static String version = 
-    "Version 2.1 created " + modDate ; 
+    "Version 2.2 created " + modDate ; 
 
   private static boolean doParsing          = true;  
     // true <=> parsing should be done
@@ -77,6 +77,10 @@ public class SANY {
     // true <=> statistics about builtin operator usage 
     //          should be reported
 
+  private static boolean doStrictErrorCodes   = false;
+    // true <=> error level should be reported as the tools'
+    //          return value
+
   /**
    * The SANY.frontEndMain method Processes an entire TLA+ spec
    * that starts in the file named in "filename", including all files
@@ -143,7 +147,11 @@ public class SANY {
       syserr.println(e.toString());
       throw new FrontEndException(e);
     }
-    return 0;
+    if (doStrictErrorCodes) {
+      return spec.errorLevel;
+    } else {
+      return 0;
+    }
   }
 
   /** 
@@ -197,6 +205,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 +218,7 @@ public class SANY {
       try 
       {
           // Actual parsing method called from inside loadSpec()
-          if (!spec.loadSpec(spec.getFileName(), spec.parseErrors, true)) 
+          if (!spec.loadSpec(spec.getFileName(), spec.parseErrors, validatePCalTranslation)) 
           {
               // dead code SZ 02. Aug 2009
               /*
@@ -410,6 +422,8 @@ public class SANY {
            doDebugging        = !doDebugging;
       else if (args[i].equals("-STAT") || args[i].equals("-stat")) 
            doStats      = !doStats;      
+      else if (args[i].toLowerCase().equals("-error-codes"))
+           doStrictErrorCodes = true;
       else {
         ToolIO.out.println("Illegal switch: " + args[i]);
         System.exit(-1);
diff --git a/tlatools/org.lamport.tlatools/src/tla2sany/explorer/DotExplorerVisitor.java b/tlatools/org.lamport.tlatools/src/tla2sany/explorer/DotExplorerVisitor.java
index dfd828dc4732f5ed78ed3630fe8fb22e58543041..2c840df880e9357d85c8f10ba46eb9ebe424db40 100644
--- a/tlatools/org.lamport.tlatools/src/tla2sany/explorer/DotExplorerVisitor.java
+++ b/tlatools/org.lamport.tlatools/src/tla2sany/explorer/DotExplorerVisitor.java
@@ -104,6 +104,7 @@ public class DotExplorerVisitor extends ExplorerVisitor {
 				final String loc = sn.getLocation().toString();
 				this.writer.append("\n");
 				this.writer.append(loc.replace("of module", "\n"));
+				this.writer.append("\n" + sn.getClass().getSimpleName());
 			}
 			this.writer.append("\"]");
 			this.writer.append(";\n");
diff --git a/tlatools/org.lamport.tlatools/src/tla2sany/modanalyzer/SpecObj.java b/tlatools/org.lamport.tlatools/src/tla2sany/modanalyzer/SpecObj.java
index 544993ce298daeaf2e1cdb0c1b15818feb07532a..e319996a8c137a3c63ea759126b0b99c0bb5a9b6 100644
--- a/tlatools/org.lamport.tlatools/src/tla2sany/modanalyzer/SpecObj.java
+++ b/tlatools/org.lamport.tlatools/src/tla2sany/modanalyzer/SpecObj.java
@@ -11,6 +11,7 @@ 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;
@@ -19,6 +20,7 @@ import tla2sany.st.TreeNode;
 import tla2sany.utilities.Vector;
 import util.FileUtil;
 import util.FilenameToStream;
+import util.MonolithSpecExtractor;
 import util.NamedInputStream;
 import util.ToolIO;
 
@@ -287,6 +289,19 @@ public class SpecObj
             // SZ 23.02.2009: split the name resolution from the stream retrieval
             // NamedInputStream nis = this.ntfis.toNIStream(name);
             NamedInputStream nis = FileUtil.createNamedInputStream(name, this.resolver);
+            
+			if (nis == null && rootParseUnit != null && rootParseUnit.getNis() != null
+					&& rootParseUnit.getNis().sourceFile() != null) {
+				// Fall back and try loading the module from a monolithic spec (one big .tla
+				// file consisting of multiple TLA+ modules and TLC configs) that is what is the
+				// rootModule here (compare tlc2.tool.impl.ModelConfig.parse()).
+            	try {
+					final File monolithSpec = rootParseUnit.getNis().sourceFile();
+					nis = MonolithSpecExtractor.module(monolithSpec, name);
+            	} catch (IOException e) {
+            		nis = null;
+            	}
+            }
 
             if (nis != null)
             {
@@ -1006,30 +1021,44 @@ public class SpecObj
     	final File f = parseUnit.getNis().sourceFile();
     	
     	try (final FileInputStream fis = new FileInputStream(f)) {
-    		final Validator.ValidationResult result = Validator.validate(fis);
+    		final Set<ValidationResult> results = Validator.validate(parseUnit, fis);
     		
-            switch (result) {
-            	case NO_PLUSCAL_EXISTS:
-            	case NO_DIVERGENCE:
-            	case NO_CHECKSUMS_EXIST:
-            		break;
-            	case ERROR_ENCOUNTERED:
-					ToolIO.err.println("A Java problem was encountered attempting to validate the specification for "
-							+ parseUnit.getName());
-
-        			break;
-            	case NO_TRANSLATION_EXISTS:
-					ToolIO.out.println("PlusCal was found in the specification for " + parseUnit.getName()
-							+ " but no TLA+ translation block for it could be found.");
-
-            		break;
-            	case DIVERGENCE_EXISTS:
-					ToolIO.out.println("!! WARNING: Either the PlusCal or its TLA+ translation has changed in the "
-							+ "specification for " + parseUnit.getName()
-							+ " since the last time translation was performed.");
-            		
-            		break;
-            }
+    		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());
@@ -1053,4 +1082,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/org.lamport.tlatools/src/tla2sany/semantic/AtNode.java b/tlatools/org.lamport.tlatools/src/tla2sany/semantic/AtNode.java
index 2a673d779722fed014cb8314f131435962e58774..1d7f522ee9954272d71e926b0e7fa85b58efbc14 100644
--- a/tlatools/org.lamport.tlatools/src/tla2sany/semantic/AtNode.java
+++ b/tlatools/org.lamport.tlatools/src/tla2sany/semantic/AtNode.java
@@ -153,8 +153,10 @@ public class AtNode extends ExprNode {
    */
   @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()
 
 
diff --git a/tlatools/org.lamport.tlatools/src/tla2tex/LaTeXOutput.java b/tlatools/org.lamport.tlatools/src/tla2tex/LaTeXOutput.java
index 051caaa6f6baebf5b49ca2d98061bc36f74d86dc..001b9aaa128ee9b3741d23611d87cc955d091637 100644
--- a/tlatools/org.lamport.tlatools/src/tla2tex/LaTeXOutput.java
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/src/tla2tex/PosAndCol.java b/tlatools/org.lamport.tlatools/src/tla2tex/PosAndCol.java
index 828613fb0a932b67875a25e1a9470522c352113a..d33ab345896177cef279f8edbb55c5126b86682e 100644
--- a/tlatools/org.lamport.tlatools/src/tla2tex/PosAndCol.java
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/src/tlc2/REPL.java b/tlatools/org.lamport.tlatools/src/tlc2/REPL.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3f42bd2832843761897f0747563d93655594737
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/REPL.java
@@ -0,0 +1,177 @@
+package tlc2;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.UserInterruptException;
+import org.jline.reader.impl.DefaultParser;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+
+import tla2sany.semantic.ModuleNode;
+import tla2sany.semantic.OpDefNode;
+import tlc2.output.EC;
+import tlc2.output.MP;
+import tlc2.tool.EvalException;
+import tlc2.tool.TLCState;
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Context;
+import tlc2.value.impl.Value;
+import util.Assert;
+import util.SimpleFilenameToStream;
+import util.TLAConstants;
+import util.ToolIO;
+
+
+/**
+ * A TLA+ REPL which provides an interactive mode of evaluating expressions and specifications.
+ */
+public class REPL {
+
+    // The spec file to use in the REPL context, if any.
+    private File specFile = null;
+
+    // The naming prefix of the temporary directory.
+    static final String TEMP_DIR_PREFIX = "tlarepl";
+
+    // The name of the spec used for evaluating expressions.
+    final String REPL_SPEC_NAME = "tlarepl";
+
+    // A temporary directory to place auxiliary files needed for REPL evaluation.
+    Path replTempDir;
+
+    public REPL(Path tempDir) {
+        replTempDir = tempDir;
+    }
+
+    public void setSpecFile(final File pSpecFile) {
+        specFile = pSpecFile;
+    }
+
+    /**
+     * Evaluate the given string input as a TLA+ expression.
+     *
+     * @return the pretty printed result of the evaluation or an empty string if there was an error.
+     */
+    public String processInput(String evalExpr) {
+
+        // The modules we will extend in the REPL environment.
+        String moduleExtends = "Reals,Sequences,Bags,FiniteSets,TLC,Randomization";
+        if (specFile != null) {
+            String mainModuleName = specFile.getName().replaceFirst(TLAConstants.Files.TLA_EXTENSION + "$", "");
+            moduleExtends += ("," + mainModuleName);
+        }
+
+        File tempFile, configFile;
+        try {
+
+            // We want to place the spec files used by REPL evaluation into the temporary directory.
+            tempFile = new File(replTempDir.toString(), REPL_SPEC_NAME + TLAConstants.Files.TLA_EXTENSION);
+            configFile = new File(replTempDir.toString(), REPL_SPEC_NAME + TLAConstants.Files.CONFIG_EXTENSION);
+
+            // Create the config file.
+            BufferedWriter cfgWriter = new BufferedWriter(new FileWriter(configFile.getAbsolutePath(), false));
+            cfgWriter.append("INIT replinit");
+            cfgWriter.newLine();
+            cfgWriter.append("NEXT replnext");
+            cfgWriter.newLine();
+            cfgWriter.close();
+
+            // Create the spec file lines.
+            ArrayList<String> lines = new ArrayList<String>();
+            String replValueVarName = "replvalue";
+            lines.add("---- MODULE tlarepl ----");
+            lines.add("EXTENDS " + moduleExtends);
+            lines.add("VARIABLE replvar");
+            // Dummy Init and Next predicates.
+            lines.add("replinit == replvar = 0");
+            lines.add("replnext == replvar' = 0");
+            // The expression to evaluate.
+            lines.add(replValueVarName + " == " + evalExpr);
+            lines.add("====");
+
+            // Write out the spec file.
+            BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile.getAbsolutePath(), false));
+            for (String line : lines) {
+                writer.append(line);
+                writer.newLine();
+            }
+            writer.close();
+
+            // Avoid sending log messages to stdout and reset the messages recording.
+            ToolIO.setMode(ToolIO.TOOL);
+            ToolIO.reset();
+
+            try {
+                // We placed the REPL spec files into a temporary directory, so, we add this temp directory
+                // path to the filename resolver used by the Tool.
+                SimpleFilenameToStream resolver = new SimpleFilenameToStream(replTempDir.toAbsolutePath().toString());
+                Tool tool = new FastTool(REPL_SPEC_NAME, REPL_SPEC_NAME, resolver);
+                ModuleNode module = tool.getSpecProcessor().getRootModule();
+                OpDefNode valueNode = module.getOpDef(replValueVarName);
+                Value exprVal = (Value) tool.eval(valueNode.getBody(), Context.Empty, TLCState.Empty);
+                return exprVal.toString();
+            } catch (Assert.TLCRuntimeException | EvalException exc) {
+                // TODO: Improve error messages with more specific detail.
+                System.out.println("Error evaluating expression: '" + evalExpr + "'");
+            }
+
+        } catch (IOException pe) {
+            pe.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * Runs the main REPL loop continuously until there is a fatal error or a user interrupt.
+     */
+    public void runREPL() throws IOException {
+        // For TLA+ we don't want to treat backslashes as escape chars e.g. for LaTeX like operators.
+        DefaultParser parser = new DefaultParser();
+        parser.setEscapeChars(null);
+        Terminal terminal = TerminalBuilder.builder().build();
+        LineReader reader = LineReaderBuilder.builder().parser(parser).terminal(terminal).build();
+
+        // Run the loop.
+        String prompt = "(tla+) ";
+        String expr;
+        while (true) {
+            try {
+                expr = reader.readLine(prompt);
+                String res = processInput(expr);
+                if (res.equals("")) {
+                    continue;
+                }
+                System.out.println(res);
+            } catch (UserInterruptException e) {
+                return;
+            } catch (EndOfFileException e) {
+                e.printStackTrace();
+                return;
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        try {
+            Path tempDir = Files.createTempDirectory(TEMP_DIR_PREFIX);
+            REPL repl = new REPL(tempDir);
+            // TODO: Allow external spec file to be loaded into REPL context.
+            System.out.println("Welcome to the TLA+ REPL!");
+            MP.printMessage(EC.TLC_VERSION, TLCGlobals.versionOfTLC);
+            System.out.println("Enter a constant-level TLA+ expression.");
+            repl.runREPL();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/TLC.java b/tlatools/org.lamport.tlatools/src/tlc2/TLC.java
index 2e3e38adaa51e981410592df69d498b5eae0e59b..53e0b2623c83a889eed8ed63bb1d6671666f913e 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/TLC.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/TLC.java
@@ -5,19 +5,13 @@
 
 package tlc2;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
 import java.io.File;
-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.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
@@ -26,27 +20,21 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import model.InJarFilenameToStream;
 import model.ModelInJar;
-import tlc2.input.MCOutputPipeConsumer;
-import tlc2.input.MCParser;
-import tlc2.input.MCParserResults;
 import tlc2.output.EC;
+import tlc2.output.ErrorTraceMessagePrinterRecorder;
 import tlc2.output.MP;
 import tlc2.output.Messages;
-import tlc2.output.TLAMonolithCreator;
-import tlc2.output.TeeOutputStream;
 import tlc2.tool.DFIDModelChecker;
+import tlc2.tool.ITool;
 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;
@@ -80,9 +68,15 @@ import util.UsageGenerator;
  * @author Simon Zambrovski
  */
 public class TLC {
+	/**
+	 * Whether the TLA+ spec is encoded in a .jar file, not a TLA+ text file.
+	 */
     private static boolean MODEL_PART_OF_JAR = false;
     
-    private enum RunMode {
+    /**
+     * Possible TLC run modes: either model checking or simulation.
+     */
+    public enum RunMode {
     	MODEL_CHECK, SIMULATE;
     }
     
@@ -90,16 +84,41 @@ public class TLC {
     // SZ Feb 20, 2009: the class has been 
     // transformed from static to dynamic
 
+    /**
+     * Whether to run in model checking or simulation mode.
+     * Defaults to model checking.
+     */
     private RunMode runMode;
+    /**
+     * Whether to clean up the states directory.
+     */
     private boolean cleanup;
+    /**
+     * Whether to check for deadlock.
+     */
     private boolean deadlock;
 
+    /**
+     * Whether a seed for the random number generator was provided.
+     */
     private boolean noSeed;
+    /**
+     * The seed for the random number generator.
+     */
     private long seed;
+    /**
+     * Adjustment for random number generator seed.
+     */
     private long aril;
     
+    /**
+     * TLC processing start time.
+     */
 	private long startTime;
 
+	/**
+	 * Name of main TLA+ specification file.
+	 */
     private String mainFile;
     private String configFile;
 	private String metadir;
@@ -115,29 +134,55 @@ public class TLC {
 	 */
     private IStateWriter stateWriter = new NoopStateWriter();
 
+    /**
+     * Name of checkpoint from which TLC should recover.
+     */
     private String fromChkpt;
 
+    /**
+     * Fingerprint set function index to use.
+     * By default one is picked at random.
+     */
     private int fpIndex;
     /**
      * The number of traces/behaviors to generate in simulation mode
      */
     private static long traceNum = Long.MAX_VALUE;
+
+    /**
+     * Name of the file to which to write state traces.
+     */
     private String traceFile = null;
+    /**
+     * Maximum state trace depth. Set to 100 by default.
+     */
     private int traceDepth;
     private FilenameToStream resolver;
 
-    // flag if the welcome message is already printed
+    /**
+	 * Whether welcome message has already been printed.
+	 */
     private boolean welcomePrinted;
     
+    /**
+     * Fingerprint set configuration.
+     */
     private FPSetConfiguration fpSetConfiguration;
     
-    private File temporaryMCOutputLogFile;
-    private FileOutputStream temporaryMCOutputStream;
-    private File specTETLAFile;
-    private MCParserResults mcParserResults;
-    private MCOutputPipeConsumer mcOutputConsumer;
-    private boolean avoidMonolithSpecTECreation;
-    private final AtomicBoolean waitingOnGenerationCompletion;
+    /**
+     * Interface to retrieve model properties.
+     */
+    private volatile ITool tool;
+
+    /**
+     * Records errors as TLC runs.
+     */
+    private final ErrorTraceMessagePrinterRecorder recorder = new ErrorTraceMessagePrinterRecorder();
+    
+    /**
+     * Trace exploration spec generator.
+     */
+    private TraceExplorationSpec teSpec;
     
     /**
      * Initialization
@@ -162,9 +207,6 @@ public class TLC {
         traceDepth = 100;
 
         fpSetConfiguration = new FPSetConfiguration();
-        
-        avoidMonolithSpecTECreation = false;
-        waitingOnGenerationCompletion = new AtomicBoolean(false);
 	}
 
     /*
@@ -322,7 +364,7 @@ public class TLC {
 		return true;
 	}
 
-	public static void setTraceNum(int aTraceNum) {
+	public static void setTraceNum(long aTraceNum) {
 		traceNum = aTraceNum;
 	}
 
@@ -342,6 +384,9 @@ public class TLC {
 	    boolean actionLabels = false;
 		boolean snapshot = false;
 		
+		boolean generateTESpec = true;
+		Path teSpecOutDir = null;
+		
         // SZ Feb 20, 2009: extracted this method to separate the 
         // parameter handling from the actual processing
         int index = 0;
@@ -362,7 +407,7 @@ public class TLC {
 					index++; // consume simulate args
 					for (String arg : simArgs) {
 						if (arg.startsWith("num=")) {
-							traceNum = Integer.parseInt(arg.replace("num=", ""));
+							traceNum = Long.parseLong(arg.replace("num=", ""));
 						} else if (arg.startsWith("file=")) {
 							traceFile = arg.replace("file=", "");
 						}
@@ -412,104 +457,30 @@ public class TLC {
                 TLCGlobals.tool = true;
             } 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.set(true);
-							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.set(false);
-							synchronized(this) {
-								notifyAll();
-							}
-						} 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("-noGenerateSpecTE"))
+            {
+            	index++;
+            	generateTESpec = false;
+            } else if (args[index].equals("-teSpecOutDir"))
+            {
+            	index++;
+            	if (index < args.length) {
+            		String path = args[index];
+            		try {
+						teSpecOutDir = Paths.get(path);
+            		} catch (InvalidPathException e) {
+            			printErrorMsg("Error: invalid path for -teSpecOutDir option: " + path);
+            			return false;
+            		}
+
+            		index++;
+            	} else {
+            		printErrorMsg("Error: expected a path for -teSpecOutDir option.");
+            		return false;
+            	}
             } else if (args[index].equals("-help") || args[index].equals("-h"))
             {
                 printUsage();
@@ -747,7 +718,7 @@ public class TLC {
                 {
                     try
                     {
-                        int num = args[index].strip().toLowerCase().equals("auto")
+                        int num = args[index].trim().toLowerCase().equals("auto")
                                 ? Runtime.getRuntime().availableProcessors()
                                 : Integer.parseInt(args[index]);
                         if (num < 1)
@@ -783,7 +754,7 @@ public class TLC {
                         index++;
                     } catch (Exception e)
                     {
-                        printErrorMsg("Errorexpect a nonnegative integer for -dfid option. " + "But encountered "
+                        printErrorMsg("Error: expect a nonnegative integer for -dfid option. " + "But encountered "
                                 + args[index]);
                         return false;
                     }
@@ -912,6 +883,17 @@ public class TLC {
                 
         startTime = System.currentTimeMillis();
 
+		generateTESpec =
+				generateTESpec
+				&& !TLCGlobals.continuation
+				&& !TLCGlobals.tool
+				&& !TraceExplorationSpec.isTESpecFile(mainFile);
+                
+		if (generateTESpec) {
+			final Path specDir = teSpecOutDir == null ? getTlaFileParentDir(mainFile) : teSpecOutDir;
+			this.teSpec = new TraceExplorationSpec(specDir, new Date(startTime), this.recorder);
+		}
+		
 		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.
@@ -1013,6 +995,7 @@ public class TLC {
      */
     public int process()
     {
+    	MP.setRecorder(this.recorder);
         // UniqueString.initialize();
         
         // a JMX wrapper that exposes runtime statistics 
@@ -1042,12 +1025,14 @@ public class TLC {
                 } else
                 {
                     rng.setSeed(seed, aril);
+    				RandomEnumerableValues.setSeed(seed);
                 }
 				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;
+                tool = simulator.getTool();
                 result = simulator.simulate();
 			} else { // RunMode.MODEL_CHECK
 				if (noSeed) {
@@ -1063,7 +1048,7 @@ public class TLC {
 				printStartupBanner(isBFS() ? EC.TLC_MODE_MC : EC.TLC_MODE_MC_DFS, getModelCheckingRuntime(fpIndex, fpSetConfiguration));
 				
             	// model checking
-                final FastTool tool = new FastTool(mainFile, configFile, resolver);
+                tool = new FastTool(mainFile, configFile, resolver);
                 deadlock = deadlock && tool.getModelConfig().getCheckDeadlock();
                 if (isBFS())
                 {
@@ -1078,22 +1063,8 @@ public class TLC {
 					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];
-                }
             }
-            return result;
+			return result;
         } catch (Throwable e)
         {
             if (e instanceof StackOverflowError)
@@ -1129,16 +1100,20 @@ public class TLC {
 			// readable runtime (days, hours, minutes, ...).
 			final long runtime = System.currentTimeMillis() - startTime;
 			MP.printMessage(EC.TLC_FINISHED,
-					TLCGlobals.tool ? Long.toString(runtime) + "ms" : convertRuntimeToHumanReadable(runtime));
+					// If TLC runs without -tool output it might still be useful to
+					// report overall runtime in a machine-readable format (milliseconds)
+					// instead of in a human-readable one.
+					TLCGlobals.tool || Boolean.getBoolean(TLC.class.getName() + ".asMilliSeconds")
+							? Long.toString(runtime) + "ms"
+							: convertRuntimeToHumanReadable(runtime));
+
+			// Generate trace exploration spec if error occurred.
+			if (teSpec != null) {
+				teSpec.generate(this.tool);
+			}
+
+			MP.unsubscribeRecorder(this.recorder);
 			MP.flush();
-			
-	        while (waitingOnGenerationCompletion.get()) {
-	        	synchronized (this) {
-	        		try {
-	        			wait();
-	        		} catch (final InterruptedException ie) { }
-	        	}
-	        }
         }
     }
     
@@ -1260,6 +1235,14 @@ public class TLC {
         this.resolver = resolver;
         ToolIO.setDefaultResolver(resolver);
     }
+    
+    public FilenameToStream getResolver() {
+        return this.resolver;
+    }
+    
+    public void setStateWriter(IStateWriter sw) {
+    	this.stateWriter = sw;
+    }
 
     /**
      * Print out an error message, with usage hint
@@ -1313,6 +1296,22 @@ public class TLC {
 		new ExecutionStatisticsCollector().collect(udc);
 	}
 	
+	/**
+	 * Gets the parent directory of the TLA file.
+	 * @param tlaFilePath Path to a TLA file.
+	 * @return Path to the parent directory of the TLA file.
+	 */
+	private static Path getTlaFileParentDir(String tlaFilePath) {
+		if (null == tlaFilePath) {
+			return Paths.get(".");
+		}
+		
+		try {
+			Path tlaDirPath = Paths.get(tlaFilePath).getParent();
+			return null == tlaDirPath ? Paths.get(".") : tlaDirPath;
+		} catch (InvalidPathException e) { return Paths.get("."); }
+	}
+	
 	private static String mode2String(final int mode) {
 		switch (mode) {
 		case EC.TLC_MODE_MC:
@@ -1325,33 +1324,6 @@ public class TLC {
 			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;
-	}
     
     /**
      * 
@@ -1405,16 +1377,16 @@ public class TLC {
 														"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("-noGenerateSpecTE",
+														"Whether to skip generating a trace exploration (TE) spec in\n"
+															+ "the event of TLC finding a state or behavior that does\n"
+															+ "not satisfy the invariants; TLC's default behavior is to\n"
+															+ "generate this spec.", true));
+		sharedArguments.add(new UsageGenerator.Argument("-teSpecOutDir", "some-dir-name",
+														"Directory to which to output the TE spec if TLC generates\n"
+															+ "an error trace. Can be a relative (to root spec dir)\n"
+															+ "or absolute path. By default the TE spec is output\n"
+															+ "to the same directory as the main spec.", true));
     	sharedArguments.add(new UsageGenerator.Argument("-gzip",
 														"control if gzip is applied to value input/output streams;\n"
 															+ "defaults to 'off'", true));
@@ -1505,6 +1477,10 @@ public class TLC {
     public String getMainFile() {
         return mainFile;
     }
+    
+    public long getStartTime() {
+    	return startTime;
+    }
 
 	public String getModelName() {
 		return System.getProperty(MailSender.MODEL_NAME, this.mainFile);
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpec.java b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpec.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f198650715461e0ec59a7c500b30b608fc0b386
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpec.java
@@ -0,0 +1,255 @@
+package tlc2;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+import tlc2.input.MCParser;
+import tlc2.input.MCParserResults;
+import tlc2.model.MCError;
+import tlc2.output.EC;
+import tlc2.output.ErrorTraceMessagePrinterRecorder;
+import tlc2.output.MP;
+import tlc2.tool.ITool;
+import tlc2.tool.TLCState;
+import tlc2.tool.impl.ModelConfig;
+import tlc2.tool.impl.SpecProcessor;
+import util.TLAConstants;
+
+/**
+ * Logic for generating a trace exploration (TE) spec.
+ */
+public class TraceExplorationSpec {
+	
+	/**
+	 * Timestamp to include in TE spec module name.
+	 */
+	private final Date timestamp;
+	
+	/**
+	 * Resolves TE spec files & provides output streams to them.
+	 */
+	private IStreamProvider streamProvider;
+	
+	/**
+	 * Records TLC output as it runs, capturing the error trace if one is found.
+	 */
+	private final ErrorTraceMessagePrinterRecorder recorder;
+	
+	/**
+	 * Initializes a new instance of the {@link TraceExplorationSpec} class.
+	 * @param outputDirectory Directory to which to output the TE spec.
+	 * @param timestamp Timestamp to include in TE spec filename.
+	 * @param recorder Recorder to record TLC as it runs; assumed to already be subscribed.
+	 */
+	public TraceExplorationSpec(
+			Path outputDirectory,
+			Date timestamp,
+			ErrorTraceMessagePrinterRecorder recorder) {
+		this.timestamp = timestamp;
+		this.streamProvider = new FileStreamProvider(outputDirectory);
+		this.recorder = recorder;
+	}
+	
+	/**
+	 * Generates the TE spec and writes it to TLA and CFG files.
+	 * @param specInfo Information about the original spec.
+	 * @return Either TE spec details or an error message.
+	 */
+	public Optional<TraceExplorationSpecGenerationReport> generate(ITool specInfo) {
+		return this.recorder.getMCErrorTrace().map(errorTrace -> {
+			ModelConfig cfg = specInfo.getModelConfig();
+			SpecProcessor spec = specInfo.getSpecProcessor();
+			String ogModuleName = specInfo.getRootName();
+			List<String> variables = Arrays.asList(TLCState.Empty.getVarsAsStrings());
+			MCParserResults parserResults = MCParser.generateResultsFromProcessorAndConfig(spec, cfg);
+			List<String> constants = parserResults.getModelConfig().getRawConstants();
+			return this.generate(ogModuleName, constants, variables, errorTrace);
+		});
+	}
+
+	/**
+	 * Generates the TE spec and writes it to TLA and CFG files.
+	 * @param ogModuleName Name of the original spec.
+	 * @param constants Constants from the original spec; to be put into cfg file.
+	 * 	example value: { "CONSTANT X <- XVal", "CONSTANT Y <- YVAL" }
+	 * @param variables Variables from the original spec.
+	 * 	example value: { "x", "y" }
+	 * @param errorTrace The error trace.
+	 */
+	public 	TraceExplorationSpecGenerationReport generate(
+			String ogModuleName,
+			List<String> constants,
+			List<String> variables,
+			MCError errorTrace) {
+		String teSpecModuleName = deriveTESpecModuleName(ogModuleName, this.timestamp);
+		try (
+				OutputStream tlaStream = this.streamProvider.getTlaStream(teSpecModuleName);
+				OutputStream cfgStream = this.streamProvider.getCfgStream(teSpecModuleName);
+		) {
+			TraceExplorer.writeSpecTEStreams(
+					teSpecModuleName,
+					ogModuleName,
+					constants,
+					variables,
+					errorTrace,
+					tlaStream,
+					cfgStream);
+			TraceExplorationSpecGenerationReport report = new TraceExplorationSpecGenerationReport(
+					errorTrace,
+					this.streamProvider.getTlaPath(teSpecModuleName),
+					this.streamProvider.getCfgPath(teSpecModuleName));
+			MP.printMessage(EC.TLC_TE_SPEC_GENERATION_COMPLETE, report.teSpecTlaPath.toString());
+			return report;
+		} catch (SecurityException | IOException e) {
+			MP.printMessage(EC.TLC_TE_SPEC_GENERATION_ERROR, e.getMessage());
+		}
+		return null;
+	}
+	
+	public static String teModuleId(Date timestamp) {
+		final long secondsSinceEpoch = timestamp.getTime() / 1_000L;
+		return Long.toString(secondsSinceEpoch);
+	}
+	
+	/**
+	 * Derives the TE spec module name.
+	 * @param ogModuleName Original module name.
+	 * @return The TE spec module name.
+	 */
+	public static String deriveTESpecModuleName(String ogModuleName, Date timestamp) {
+		// millis to seconds
+		return String.format(
+			"%s_%s_%s",
+			ogModuleName,
+			TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME,
+			teModuleId(timestamp));
+	}
+	
+	/**
+	 * Determines whether the given spec is a TE spec.
+	 * @param tlaFilePath Path to the TLA file.
+	 * @return Whether the given spec is a TE spec.
+	 */
+	public static boolean isTESpecFile(String tlaFilePath) {
+		if (null == tlaFilePath) {
+			return false;
+		}
+		
+		try {
+			// TODO: branch based on something better than the filename such as the module
+			// name that we choose above.
+			String filename = Paths.get(tlaFilePath).getFileName().toString();
+			// see tlc2.TraceExplorationSpec.deriveTESpecModuleName(String)
+			return filename
+					.matches("^.*_" + TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + "_\\d{10}(.tla)?$");
+		} catch (InvalidPathException e) { return false; }
+	}
+	
+	/**
+	 * Interface for creating streams to which to write the TE spec.
+	 */
+	public interface IStreamProvider {
+		
+		/**
+		 * Returns the path to the TLA file.
+		 * Path not guaranteed to exist.
+		 * @param moduleName Name of the TE spec module.
+		 * @return The path to the TLA file.
+		 */
+		public Path getTlaPath(String moduleName);
+		
+		/**
+		 * Creates an output stream to which to write the TE spec.
+		 * Caller is responsible for managing stream lifecycle.
+		 * @param moduleName Name of the TE spec module.
+		 * @return A new output stream to which to write the TE spec.
+		 * @throws FileNotFoundException Thrown if filepath is inaccessible.
+		 * @throws SecurityException Thrown if lacking perms to write file.
+		 */
+		public OutputStream getTlaStream(String moduleName) throws FileNotFoundException, SecurityException;
+		
+		/**
+		 * Returns the path to the CFG file.
+		 * Path not guaranteed to exist.
+		 * @param moduleName Name of the TE spec module.
+		 * @return The path to the CFG file.
+		 */
+		public Path getCfgPath(String moduleName);
+		
+		/**
+		 * Creates an output stream to which to write the TE spec's CFG file.
+		 * Caller is responsible for managing stream lifecycle.
+		 * @param moduleName Name of the TE spec module.
+		 * @return A new output stream to which to write the CFG file.
+		 * @throws FileNotFoundException Thrown if filepath is inaccessible.
+		 * @throws SecurityException Thrown if lacking perms to write file.
+		 */
+		public OutputStream getCfgStream(String moduleName) throws FileNotFoundException, SecurityException;
+	}
+	
+	/**
+	 * Provides streams to actual files on disk.
+	 */
+	public class FileStreamProvider implements IStreamProvider {
+		
+		/**
+		 * Directory to which to output the files.
+		 */
+		private Path outputDirectory;
+		
+		/**
+		 * Initializes a new instance of {@link FileStreamProvider}
+		 * @param outputDirectory Output directory for TLA & CFG files.
+		 */
+		public FileStreamProvider(Path outputDirectory) {
+			this.outputDirectory = outputDirectory;
+		}
+		
+		@Override
+		public Path getTlaPath(String moduleName) {
+			return this.outputDirectory.resolve(moduleName + TLAConstants.Files.TLA_EXTENSION);
+		}
+		
+		@Override
+		public OutputStream getTlaStream(String moduleName) throws FileNotFoundException, SecurityException {
+			this.ensureDirectoryExists();
+			final File tlaFile = this.getTlaPath(moduleName).toFile();
+			return new FileOutputStream(tlaFile);
+		}
+		
+		@Override
+		public Path getCfgPath(String moduleName) {
+			return this.outputDirectory.resolve(moduleName + TLAConstants.Files.CONFIG_EXTENSION);
+		}
+		
+		@Override
+		public OutputStream getCfgStream(String moduleName) throws FileNotFoundException, SecurityException {
+			this.ensureDirectoryExists();
+			final File cfgFile = this.getCfgPath(moduleName).toFile();
+			return new FileOutputStream(cfgFile);
+		}
+		
+		/**
+		 * Recursively creates directories until the desired path is present.
+		 * @throws SecurityException Access issue when creating directories.
+		 */
+		private void ensureDirectoryExists() throws SecurityException {
+			for (int i = 1; i <= this.outputDirectory.getNameCount(); i++) {
+				Path subPath = this.outputDirectory.subpath(0, i);
+				if (!subPath.toFile().exists()) {
+					subPath.toFile().mkdir();
+				}
+			}
+		}
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpecGenerationReport.java b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpecGenerationReport.java
new file mode 100644
index 0000000000000000000000000000000000000000..b02cfffed0638d38cee995a583f898ae38478827
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorationSpecGenerationReport.java
@@ -0,0 +1,38 @@
+package tlc2;
+
+import java.nio.file.Path;
+
+import tlc2.model.MCError;
+
+/**
+ * Information about the generated TE spec.
+ */
+public class TraceExplorationSpecGenerationReport {
+	
+	/**
+	 * The error trace reproduced by the TE spec.
+	 */
+	public MCError errorTrace;
+	
+	/**
+	 * Path to the TE spec TLA file.
+	 */
+	public Path teSpecTlaPath;
+	
+	/**
+	 * Path to the TE spec CFG file.
+	 */
+	public Path teSpecCfgPath;
+	
+	/**
+	 * Initializes a new instance of {@link GenerationReport}
+	 * @param errorTrace The error trace.
+	 * @param teSpecTlaPath The TLA path.
+	 * @param teSpecCfgPath The CFG path.
+	 */
+	public TraceExplorationSpecGenerationReport(MCError errorTrace, Path teSpecTlaPath, Path teSpecCfgPath) {
+		this.errorTrace = errorTrace;
+		this.teSpecTlaPath = teSpecTlaPath;
+		this.teSpecCfgPath = teSpecCfgPath;
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorer.java b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorer.java
index bed61c179df1d628aee88f2ad0e0e4573146eb7a..c4804a5e17204da8deb672627ec5806fe39f8c55 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorer.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/TraceExplorer.java
@@ -2,13 +2,17 @@ package tlc2;
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -23,11 +27,7 @@ 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 tlc2.tool.TLCState;
 import util.TLAConstants;
 import util.ToolIO;
 import util.UsageGenerator;
@@ -76,83 +76,156 @@ public class TraceExplorer {
     
     
     /**
-	 * @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
+	 */
+	public static File[] writeSpecTEFiles(
+			final File outputDirectory,
+			final String osn,
+			final String[] vars,
+			final MCParserResults results,
+			final MCError error)
+					throws FileNotFoundException,
+						SecurityException,
+						IOException {
+
+		final File specTETLA = new File(
+				outputDirectory,
+				TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.Files.TLA_EXTENSION);
+		final File specTECFG = new File(
+				outputDirectory,
+				TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.Files.CONFIG_EXTENSION);
+		
+		List<String> constants = results.getModelConfig().getRawConstants();
+		List<String> variables = Arrays.asList(vars);
+
+		try (
+				FileOutputStream specTETLAOutStream = new FileOutputStream(specTETLA);
+				FileOutputStream specTECFGOutStream = new FileOutputStream(specTECFG)
+		) {
+			writeSpecTEStreams(
+					TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME,
+					osn,
+					constants,
+					variables,
+					error,
+					specTETLAOutStream,
+					specTECFGOutStream);
+		}
+
+		return new File[] { specTETLA, specTECFG };
+	}
+	
+	/**
+	 * 
+	 * @param originalSpecName
+	 * @param originalSpecVariables
+	 * @param specInfo
+	 * @param error
+	 * @param specTETLAOutStream
+	 * @param specTECFGOutStream
 	 * @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.");
+	public static void writeSpecTEStreams(
+			final String teSpecModuleName,
+			final String originalSpecName,
+			final List<String> constants,
+			final List<String> variables,
+			final MCError error,
+			final OutputStream specTETLAOutStream,
+			final OutputStream specTECFGOutStream) throws IOException {
+
+		final SpecTraceExpressionWriter writer = new SpecTraceExpressionWriter();
+
+		/**
+		 * Write content of config file (SpecTE).
+		 * <p>
+		 * Contrary to the Toolbox's TraceExplorerDelegate, which reads constants,
+		 * overrides, ... from the TLC model, this implementation copies all CONSTANT
+		 * and CONSTANTS verbatim from the existing config file with which TLC ran. The
+		 * definition that appear in the .tla file (e.g. MC.tla) don't have to be copied
+		 * because SpecTE extends MC.
+		 * (see TraceExplorerDelegate#writeModelInfo)
+		 */
+		writer.addConstants(constants);
+
+		/**
+		 * Write SpecTE.
+		 */
+		final Set<String> extendedModules = new HashSet<>();
+		extendedModules.add(TLAConstants.BuiltInModules.TLC);
+		extendedModules.add(TLAConstants.BuiltInModules.TRACE_EXPRESSIONS);
+
+		writer.addPrimer(teSpecModuleName, originalSpecName, extendedModules);
 		
-		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.");
+		writer.addTraceExpressionInstance(
+				String.format("%s_%s", originalSpecName, TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME));
+
+		final List<MCState> trace = error.getStates();
+		
+		final String traceFunctionId = writer.addTraceFunctionInstance();
+		
+		writer.addProperties(trace);
+
+		// Write Init and Next with vars instead of extracting the vars from trace to
+		// always write a syntactically correct behavior spec even if trace = <<>>.
+		writer.addInitNextTraceFunction(trace, variables, SPEC_TE_INIT_ID, SPEC_TE_NEXT_ID);
+				
+		writer.addFooter();
+		
+		/**
+		 * Write definition of trace expression into new module.
+		 */
+		writer.append(TLAConstants.CR);
+		
+		final SpecTraceExpressionWriter te = new SpecTraceExpressionWriter();
+		te.append(TLAConstants.CR);
+		te.addPrimer(String.format("%s_%s", originalSpecName, TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME),
+				originalSpecName, extendedModules);
+		te.addTraceExpressionStub(TLAConstants.TraceExplore.SPEC_TE_TRACE_EXPRESSION, variables);
+		te.addFooter();
+		writer.append(TLAConstants.CR + te.toString() + TLAConstants.CR + TLAConstants.CR);
 		
-		return new File[] { tlaCopier.getDestinationFile(), cfgCopier.getDestinationFile() };
+		/**
+		 * Write commented definition of trace def override into new module.
+		 * A user simply has to provide the serialized trace and uncomment the module.
+		 */
+		writer.append(TLAConstants.CR);
+		writer.append("Parsing and semantic processing can take forever if the trace below is long.")
+				.append(TLAConstants.CR);
+		writer.append(" In this case, it is advised to deserialize the trace from a binary file.")
+				.append(TLAConstants.CR);
+		writer.append(" To create the file, replace your spec's invariant F with:").append(TLAConstants.CR);
+		writer.append("  Inv == IF F THEN TRUE ELSE ~IOSerialize(Trace, \"file.bin\", TRUE)").append(TLAConstants.CR);
+		writer.append(" (IOUtils and TLCExt modules from https://modules.tlapl.us/)");
+		
+		final Set<String> extendedModulesWithIOUtils = new HashSet<>(extendedModules);
+		extendedModulesWithIOUtils.add("IOUtils");
+		
+		final SpecTraceExpressionWriter w = new SpecTraceExpressionWriter();
+		w.append(TLAConstants.CR);
+		w.addPrimer(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + "TraceDef", originalSpecName,
+				extendedModulesWithIOUtils);
+		w.append(traceFunctionId).append(TLAConstants.DEFINES).append("IODeserialize(\"file.bin\", TRUE)\n\n");
+		w.addFooter();
+		// Users can uncomment the module if they wish to read the serialized trace.
+		writer.append(TLAConstants.CR + w.getComment() + TLAConstants.CR + TLAConstants.CR);
+		
+		/**
+		 * Write definition of trace def into new module.
+		 */
+		writer.addPrimer(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + "TraceDef", originalSpecName, extendedModules);
+
+		writer.addTraceFunction(trace, traceFunctionId);
+		
+		writer.addAliasToCfg(TLAConstants.TraceExplore.SPEC_TE_TTRACE_EXPRESSION);
+		
+        /**
+         * Write to streams.
+         */
+		writer.writeStreams(specTETLAOutStream, specTECFGOutStream);
     }
     
     
@@ -396,12 +469,12 @@ public class TraceExplorer {
     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.TRACE_EXPRESSION_MODULE_NAME + ", or "
 												+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file.");
     	}
     	
 		final boolean specifiedModuleIsSpecTE
-					= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+					= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME);
 		final boolean needGenerateSpecTE = RunMode.GENERATE_SPEC_TE.equals(runMode) 
 											|| (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
     	if (needGenerateSpecTE) {
@@ -417,10 +490,10 @@ public class TraceExplorer {
     			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.";
+    							+ TLAConstants.TraceExplore.TRACE_EXPRESSION_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.TRACE_EXPRESSION_MODULE_NAME + " nor "
 								+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, "
 								+ "no trace expressions will be evaluated.";
     			}
@@ -462,14 +535,14 @@ public class TraceExplorer {
 				specGenerationOriginalSpecName = consumer.getSpecName();
 				
 				final boolean specifiedModuleIsSpecTE
-							= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+							= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.TRACE_EXPRESSION_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.TRACE_EXPRESSION_MODULE_NAME + ", or "
 														+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " file.");
 				}
 
@@ -498,7 +571,7 @@ public class TraceExplorer {
 		
 		MP.printMessage(EC.GENERAL, "TraceExplorer is expecting input on stdin...");
 
-		pipeConsumer.consumeOutput(false);
+		pipeConsumer.consumeOutput();
 		
 		if (pipeConsumer.outputHadNoToolMessages()) {
 			MP.printMessage(EC.GENERAL, "The output had no tool messages; was TLC not run with"
@@ -510,7 +583,7 @@ public class TraceExplorer {
 		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);
+				= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME);
 		final boolean needGenerateSpecTE
 				= RunMode.GENERATE_SPEC_TE.equals(runMode)
 								|| (!specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
@@ -519,10 +592,10 @@ public class TraceExplorer {
     			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.";
+    							+ TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + " will be produced.";
     			} else {
     				msg = "The output contained no error-state messages, no "
-								+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + " nor "
+								+ TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + " nor "
 								+ TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME + " will be produced, and, so, "
 								+ "no trace expressions will be evaluated.";
     			}
@@ -588,7 +661,7 @@ public class TraceExplorer {
 		final String configContent = writer.getConfigBuffer().toString();
 		writer.writeFiles(tlaFile, null);
 
-		final CFGCopier cfgCopier = new CFGCopier(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME,
+		final CFGCopier cfgCopier = new CFGCopier(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME,
 				TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME, specGenerationSourceDirectory, configContent);
 		cfgCopier.copy();
 
@@ -606,7 +679,7 @@ public class TraceExplorer {
 
     private boolean performPreFlightFileChecks() {
 		final boolean specifiedModuleIsSpecTE
-				= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+				= specGenerationOriginalSpecName.equals(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME);
 		final boolean outputShouldExist = !expectedOutputFromStdIn 
 											|| (specifiedModuleIsSpecTE && RunMode.TRACE_EXPLORATION.equals(runMode));
 
@@ -648,7 +721,7 @@ public class TraceExplorer {
 			if (!overwriteGeneratedFiles) {
 				if (!specifiedModuleIsSpecTE) {
 					final File specTETLA = new File(specGenerationSourceDirectory,
-							(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION));
+							(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + TLAConstants.Files.TLA_EXTENSION));
 
 					if (specTETLA.exists()) {
 						printErrorMessage("specified source directory already contains " + specTETLA.getName()
@@ -677,9 +750,12 @@ public class TraceExplorer {
 		return true;
     }
     
-    private void writeSpecTEFiles(final MCParserResults results, final MCError error) throws IOException {
-    	writeSpecTEFiles(specGenerationSourceDirectory, specGenerationOriginalSpecName, results, error);
-    }
+	private void writeSpecTEFiles(final MCParserResults results, final MCError error) throws IOException {
+		writeSpecTEFiles(specGenerationSourceDirectory, specGenerationOriginalSpecName,
+				// Not sure TLCState.Empty is correctly initialized at this point, but I don't
+				// want to spend more time on it (screw you future Markus).
+				TLCState.Empty.getVarsAsStrings(), results, error);
+	}
     
     private void printErrorMessage(final String message) {
     	MP.printError(EC.GENERAL, message);
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java
index c422a7d21ed886162846044594764c4605aa5614..66b81f15d0b3bca1a8f977a28adca79a705172be 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/TraceExpressionExplorerSpecWriter.java
@@ -90,7 +90,7 @@ public class TraceExpressionExplorerSpecWriter extends AbstractSpecWriter {
 		}
 		
 		addPrimer(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME,
-				  TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME);
+				  TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME);
 		
 		declareExpressionVariables();
 		createInitNextWithExpressions();
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/input/AbstractMCOutputConsumer.java b/tlatools/org.lamport.tlatools/src/tlc2/input/AbstractMCOutputConsumer.java
index 439629cd36cb5c21b2c46f28e261ce03b8e6af87..97311ccf2ecbe3e3ff53bee412ac7008b2acd5d6 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/input/AbstractMCOutputConsumer.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/input/AbstractMCOutputConsumer.java
@@ -54,7 +54,7 @@ abstract class AbstractMCOutputConsumer {
 		
 		MCOutputMessage message = parseChunk(reader);
 		if ((message == null) || (message.getType() != MP.ERROR)) {
-			throw new IOException("Expected a useless error message like "
+			throw new IOException("Expected an error message like "
 									+ "'The behavior up to this point is...' but didn't find one after"
 									+ "[" + currentError.getMessage() + "]");
 		}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/input/MCOutputPipeConsumer.java b/tlatools/org.lamport.tlatools/src/tlc2/input/MCOutputPipeConsumer.java
index 911d62febe47d6a8cd51e2c906b548ddf76cdf58..fa70804ee2457d6148d16186d44b72adbfa0f9bb 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/input/MCOutputPipeConsumer.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/input/MCOutputPipeConsumer.java
@@ -53,21 +53,13 @@ public class MCOutputPipeConsumer extends AbstractMCOutputConsumer {
 	 * 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;
-		
+	public void consumeOutput() throws IOException {
 		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) {
@@ -79,13 +71,11 @@ public class MCOutputPipeConsumer extends AbstractMCOutputConsumer {
 		} catch (final IOException ioe) {
 			if (outputHadNoToolMessages()) {
 				// Either we threw this from handleUnknownReadLine(String), or the output was abortive.
-				return encounteredMessages;
+				return;
 			}
 			
 			throw ioe;
 		}
-		
-		return encounteredMessages;
 	}
 	
 	public boolean outputHadNoToolMessages() {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/input/MCParser.java b/tlatools/org.lamport.tlatools/src/tlc2/input/MCParser.java
index b4fe53ebe4abaef18a20bbaaae2d5eae438e51be..c7d6a697a1e9067872900716dc8055938d5f2189 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/input/MCParser.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/input/MCParser.java
@@ -7,6 +7,7 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import tla2sany.semantic.InstanceNode;
 import tla2sany.semantic.ModuleNode;
@@ -20,6 +21,7 @@ import tlc2.tool.impl.ModelConfig;
 import tlc2.tool.impl.SpecProcessor;
 import tlc2.tool.impl.SymbolNodeValueLookupProvider;
 import tlc2.tool.impl.TLAClass;
+import tlc2.tool.impl.Tool;
 import util.FilenameToStream;
 import util.SimpleFilenameToStream;
 import util.TLAConstants;
@@ -71,7 +73,7 @@ public class MCParser {
 		}
 		initNextLocationsToDelete.sort(new LocationComparator());
 
-		final ArrayList<String> extendees = new ArrayList<>();
+		final HashSet<String> extendees = new HashSet<>();
 		root.getExtendedModuleSet(false).stream().forEach(moduleNode -> extendees.add(moduleNode.getName().toString()));
 
 		final HashSet<String> allExtendees = new HashSet<>();
@@ -201,7 +203,7 @@ public class MCParser {
 			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);
+						configParser, defaultLookup, null, tlaClass, Tool.Mode.MC);
 				parserResults = generateResultsFromProcessorAndConfig(specProcessor, configParser);
 				
 				if (outputParser != null) {
@@ -211,7 +213,7 @@ public class MCParser {
 			} 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<>(),
+													encounteredMessages, new HashSet<>(), new HashSet<>(),
 													new HashSet<>(), new ArrayList<>(), true, null, configParser);
 			}
 			
@@ -236,7 +238,7 @@ public class MCParser {
 			System.out.println(error.toSequenceOfRecords(true));
 		}
 		
-		final List<String> extendedModules = results.getOriginalExtendedModules();
+		final Set<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);
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/input/MCParserResults.java b/tlatools/org.lamport.tlatools/src/tlc2/input/MCParserResults.java
index b3f57e89acfc96ddf63dbb780e7c88098551954c..5b9bbef777ddb716e0e7d42126e7459e5d7feb82 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/input/MCParserResults.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/input/MCParserResults.java
@@ -13,7 +13,7 @@ public class MCParserResults {
 	private MCError error;
 	private List<MCOutputMessage> outputMessages;
 
-	private final List<String> immediateExtendedModules;
+	private final Set<String> immediateExtendedModules;
 	private final Set<String> allExtendedModules;
 	private final Set<String> modulesInstantiated;
 
@@ -25,7 +25,7 @@ public class MCParserResults {
 	
 	private final ModelConfig modelConfig;
 
-	MCParserResults(final String rootModuleName, final List<String> immediateExtendeds, final Set<String> allExtendeds,
+	MCParserResults(final String rootModuleName, final Set<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;
@@ -45,7 +45,7 @@ public class MCParserResults {
 	}
 	
 	MCParserResults(final String rootModuleName, final MCError mcError, final List<MCOutputMessage> messages,
-					final List<String> immediateExtendeds, final Set<String> allExtendeds,
+					final Set<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,
@@ -87,7 +87,7 @@ public class MCParserResults {
 	 * @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() {
+	public Set<String> getOriginalExtendedModules() {
 		return immediateExtendedModules;
 	}
 	
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/model/MCError.java b/tlatools/org.lamport.tlatools/src/tlc2/model/MCError.java
index 69b60a9e894d84394628760bc6c5bc633892bdf1..bdfce54b66356c5cb4e0e8f63cdcdd2971cdcffb 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/model/MCError.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/model/MCError.java
@@ -1,39 +1,90 @@
 package tlc2.model;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import util.TLAConstants;
 
+/**
+ * Class encapsulating information about an error trace found by TLC model checking.
+ */
 public class MCError {
+	
+	/**
+	 * Human-readable message describing cause of error trace.
+	 */
 	private final String message;
+	
+	/**
+	 * Nested error. It is uncertain whether or how nested errors ever occur.
+	 */
 	private final MCError cause;
+	
+	/**
+	 * Sequence of states which comprise the error trace.
+	 */
 	private final ArrayList<MCState> states;
 	
+	/**
+	 * Initializes a new instance of the {@link MCError} class.
+	 */
+	public MCError() {
+		this(null);
+	}
+	
+	/**
+	 * Initializes a new instance of the {@link MCError} class.
+	 * @param errorMessage Message describing the error.
+	 */
+	public MCError(final String errorMessage) {
+		this(null, errorMessage);
+	}
+	
+	/**
+	 * Initializes a new instance of the {@link MCError} class.
+	 * @param errorCause A nested error.
+	 * @param errorMessage Message describing the error.
+	 */
 	public MCError(final MCError errorCause, final String errorMessage) {
-		cause = errorCause;
-		message = errorMessage;
-		states = new ArrayList<>();
+		this.cause = errorCause;
+		this.message = errorMessage;
+		this.states = new ArrayList<>();
 	}
 	
+	/**
+	 * Adds a state to the error trace.
+	 * @param state The state to add to the trace.
+	 */
 	public void addState(final MCState state) {
-		states.add(state);
+		this.states.add(state);
 	}
 	
+	/**
+	 * Gets the ordered list of states in the error trace.
+	 * @return An ordered list of states comprising the error trace.
+	 */
 	public List<MCState> getStates() {
-		return states;
+		return this.states;
 	}
 	
+	/**
+	 * Gets the message describing the error.
+	 * @return A message describing the error.
+	 */
 	public String getMessage() {
-		return message;
+		return this.message;
 	}
 	
+	/**
+	 * Returns the nested error.
+	 * @return The nested error. Likely to be null.
+	 */
 	public MCError getCause() {
-		return cause;
+		return this.cause;
 	}
 	
-	public void updateStatesForTraceExpression(final HashMap<String, String> variableExpressionMap) {
+	public void updateStatesForTraceExpression(final Map<String, String> variableExpressionMap) {
 		for (final MCState state : states) {
 			for (final MCVariable variable : state.getVariables()) {
 				final String expression = variableExpressionMap.get(variable.getName());
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/model/MCState.java b/tlatools/org.lamport.tlatools/src/tlc2/model/MCState.java
index aa1af885eab94d9126af0437d2c2fc68812ef216..f398770a2ecfc0be5a1dce362e4c7215df19dbac 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/model/MCState.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/model/MCState.java
@@ -1,13 +1,70 @@
 package tlc2.model;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 import tla2sany.st.Location;
+import tlc2.tool.TLCStateInfo;
+import tlc2.value.IValue;
 import util.TLAConstants;
+import util.UniqueString;
 
+/**
+ * Encapsulates information about a TLC state.
+ */
 public class MCState {
+	
 	private static final String BACK_TO_STATE = " " + TLAConstants.BACK_TO_STATE;
+	
+	/**
+	 * The variables captured by this state.
+	 */
+	private final MCVariable[] variables;
+	
+	/**
+	 * The name of the next-state-relation taken to arrive in this state.
+	 */
+	private final String name;
+	
+	/**
+	 * The state label; takes form of <$name $location>
+	 */
+	private final String label;
+	
+	/**
+	 * The location of the next-state-relation, a parsed representation of (for example):
+	 * line 7, col 9 to line 11, col 23 of module Alias
+	 */
+	private final Location location;
+	
+	/**
+	 * Whether this state was reached by stuttering.
+	 * Found in behaviors witnessing a liveness property violation.
+	 */
+	private final boolean isStuttering;
+	
+	/**
+	 * Whether this state returns to a previous state in the behavior.
+	 * Found in behaviors witnessing a liveness property violation.
+	 */
+	private final boolean isBackToState;
+	
+	/**
+	 * The depth of this state in the behavior, counting from 1.
+	 */
+	private final int stateNumber;
 
+	/**
+	 * Parses a state from the standard TLC command line output format, for example:
+	 * 
+	 * 3: <Next line 7, col 9 to line 11, col 23 of module Alias>
+	 * /\ x = 3
+	 * /\ y = FALSE
+	 * 
+	 * @param stateInputString The unparsed state in TLC command line output format.
+	 * @return A parsed {@link MCState} instance.
+	 */
 	public static MCState parseState(final String stateInputString) {
 		// state number
 		final int index = stateInputString.indexOf(TLAConstants.COLON);
@@ -30,13 +87,23 @@ public class MCState {
 			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));
+			// The format of states in the output of depth-first (iterative deepening)
+			// obviously differs from BFS (why use one implementation when we can have 2 and
+			// more). Thus, take care of states that lack a label.
+			final String sublabel;
+			if (label.length() > 2) {
+				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 = sublabel;
+				name = null;
 				location = null;
 			}
 		} else {
@@ -46,16 +113,7 @@ public class MCState {
 
 		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
@@ -65,8 +123,14 @@ public class MCState {
 	 * @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) {
+	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;
@@ -75,25 +139,70 @@ public class MCState {
 		isBackToState = backToState;
 		stateNumber = ordinal;
 	}
+	
+	/**
+	 * Initializes a new instance of this class.
+	 * @param other The state from which to copy values.
+	 * @param isStuttering Whether to mark this state as stuttering.
+	 * @param isBackToState Whether to mark this state as the end of a lasso.
+	 */
+	public MCState(final MCState other, boolean isStuttering, boolean isBackToState) {
+		this.variables = other.variables;
+		this.name = other.name;
+		this.label = other.label;
+		this.location = other.location;
+		this.stateNumber = other.stateNumber;
+		this.isStuttering = isStuttering;
+		this.isBackToState = isBackToState;
+	}
+	
+	public MCState(TLCStateInfo tlcState) {
+		this.name = "";
+		this.label = "";
+		this.location = null;
+		this.isStuttering = false;
+		this.isBackToState = false;
+		this.stateNumber = (int)tlcState.stateNumber;
+
+		Map<UniqueString, IValue> variableMap = tlcState.getOriginalState().getVals();
+		List<MCVariable> variableList = new ArrayList<MCVariable>();
+		for (UniqueString key : variableMap.keySet()) {
+			IValue value = variableMap.get(key);
+			// value is null if the successor state is not completely specified by the
+			// next-state relation. See e.g. IncompleteNextTest.java
+			MCVariable variable = new MCVariable(key.toString(), value != null ? value.toString() : "");
+			variableList.add(variable);
+		}
+		
+		this.variables = variableList.toArray(new MCVariable[variableList.size()]);
+	}
 
 	public MCVariable[] getVariables() {
-		return variables;
+		return this.variables;
 	}
 	
 	public String getLabel() {
-		 return label;
+		 return this.label;
+	}
+	
+	public String getName() {
+		return this.name;
 	}
 
 	public boolean isStuttering() {
-		return isStuttering;
+		return this.isStuttering;
 	}
 
 	public boolean isBackToState() {
-		return isBackToState;
+		return this.isBackToState;
 	}
 
 	public int getStateNumber() {
-		return stateNumber;
+		return this.stateNumber;
+	}
+	
+	public Location getLocation() {
+		return this.location;
 	}
 	
 	public String asRecord(final boolean includeHeader) {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/model/MCVariable.java b/tlatools/org.lamport.tlatools/src/tlc2/model/MCVariable.java
index ff057303a0cbd1cd270cf2935cea9d07e652382f..03260e88f1e3bfc634815007dab12bc4a42a43c0 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/model/MCVariable.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/model/MCVariable.java
@@ -1,5 +1,8 @@
 package tlc2.model;
 
+import tlc2.output.EC;
+import util.Assert;
+
 public class MCVariable {
     private final String name;
     private final String valueAsString;
@@ -10,7 +13,9 @@ public class MCVariable {
 	 * @param value     TLC string representation of the variable value
 	 */
 	public MCVariable(final String varName, final String value) {
+		Assert.check(varName != null, EC.GENERAL);
 		name = varName;
+		Assert.check(value != null, EC.GENERAL);
 		valueAsString = value;
 		traceExpression = null;
 	}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/module/TLC.java b/tlatools/org.lamport.tlatools/src/tlc2/module/TLC.java
index 1f87e6cea5471800cf97d19c15381dd40dd4dbd7..53a07668d6af91e75f65191d6cfd301b50dd09e0 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/module/TLC.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/module/TLC.java
@@ -14,6 +14,7 @@ import tlc2.output.MP;
 import tlc2.tool.EvalControl;
 import tlc2.tool.EvalException;
 import tlc2.tool.ModelChecker;
+import tlc2.tool.SimulationWorker;
 import tlc2.tool.TLCState;
 import tlc2.tool.impl.TLARegistry;
 import tlc2.util.IdThread;
@@ -44,6 +45,7 @@ public class TLC implements ValueConstants
 	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 GENERATED = UniqueString.uniqueStringOf("generated");
 	private static final UniqueString DIAMETER = UniqueString.uniqueStringOf("diameter");
 	private static final UniqueString EXIT = UniqueString.uniqueStringOf("exit");
 	private static final UniqueString PAUSE = UniqueString.uniqueStringOf("pause");
@@ -157,7 +159,7 @@ public class TLC implements ValueConstants
                 } else if (TLCGlobals.mainChecker != null)
                 {
                     res = (Value) tlc2.TLCGlobals.mainChecker.getValue(0, idx);
-                } else 
+                } else if (tlc2.TLCGlobals.simulator != null)
                 {	
                     res = (Value) tlc2.TLCGlobals.simulator.getLocalValue(idx);
                 }
@@ -178,7 +180,24 @@ public class TLC implements ValueConstants
 		final StringValue sv = (StringValue) vidx;
 		if (DIAMETER == sv.val) {
 			try {
-				return IntValue.gen(TLCGlobals.mainChecker.getProgress());
+				if (TLCGlobals.mainChecker != null) {
+					return IntValue.gen(TLCGlobals.mainChecker.getProgress());
+				} else if (TLCGlobals.simulator != null) {
+					if (Thread.currentThread() instanceof SimulationWorker) {
+						// non-initial states.
+						final SimulationWorker sw = (SimulationWorker) Thread.currentThread();
+						final long traceCnt = sw.getTraceCnt();
+						if (traceCnt > Integer.MAX_VALUE) {
+							return IntValue.gen(Integer.MAX_VALUE);
+						}
+						return IntValue.gen((int) traceCnt);
+					} else {
+						// Called while evaluating the initial predicate/generating initial states.
+						return IntValue.gen(0);
+					}
+				} else {
+					throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
+				}
 			} catch (ArithmeticException e) {
 				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
 						Long.toString(TLCGlobals.mainChecker.getProgress()));
@@ -188,6 +207,15 @@ public class TLC implements ValueConstants
 				// NPE.
 				throw new EvalException(EC.TLC_MODULE_TLCGET_UNDEFINED, String.valueOf(sv.val));
 			}
+		} else if (GENERATED == sv.val) {
+			try {
+				return IntValue.gen(Math.toIntExact(TLCGlobals.mainChecker.getStatesGenerated()));
+			} catch (ArithmeticException e) {
+				throw new EvalException(EC.TLC_MODULE_OVERFLOW,
+						Long.toString(TLCGlobals.mainChecker.getStatesGenerated()));
+			} catch (NullPointerException 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()));
@@ -252,7 +280,7 @@ public class TLC implements ValueConstants
                     TLCGlobals.mainChecker.setAllValues(idx, val);
                 } else 
                 {	
-                    tlc2.TLCGlobals.simulator.setLocalValue(idx, val);
+                    tlc2.TLCGlobals.simulator.setAllValues(idx, val);
                 }
                 return BoolValue.ValTrue;
             }
@@ -260,7 +288,12 @@ public class TLC implements ValueConstants
         	final StringValue sv = (StringValue) vidx;
         	if (EXIT == sv.val) {
         		if (val == BoolValue.ValTrue) {
-        			TLCGlobals.mainChecker.stop();
+        			if (TLCGlobals.mainChecker != null) {
+        				TLCGlobals.mainChecker.stop();
+        			}
+        			if (TLCGlobals.simulator != null) {
+        				TLCGlobals.simulator.stop();
+        			}
         		}
         		return BoolValue.ValTrue;
         	} else if (PAUSE == sv.val) {
@@ -564,6 +597,10 @@ public class TLC implements ValueConstants
             }
             return new TupleValue(vals);
         }
+        case INTERVALVALUE: {
+        	final IntervalValue iv = (IntervalValue) val;
+        	return iv.randomElement();
+        }
         default: {
             SetEnumValue enumVal = (SetEnumValue) val.toSetEnum();
             if (enumVal == null)
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/AbstractSpecWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/output/AbstractSpecWriter.java
index bb6e81936a8fe2470934743f536ddcb55b3d24af..83d94c426790f6dd02628f38cd1d0baccc32eda6 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/AbstractSpecWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/AbstractSpecWriter.java
@@ -4,7 +4,7 @@ import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.file.CopyOption;
+import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
@@ -29,11 +29,15 @@ public abstract class AbstractSpecWriter {
      */
 	public static String addArrowAssignmentToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
 			final Assignment constant, final String schema) {
+		return addArrowAssignmentIdToBuffers(tlaBuffer, cfgBuffer, constant, SpecWriterUtilities.getValidIdentifier(schema));
+	}
+
+	public static String addArrowAssignmentIdToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final Assignment constant, final String id) {
 		// 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);
 		
@@ -41,10 +45,8 @@ public abstract class AbstractSpecWriter {
 			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
@@ -98,6 +100,29 @@ public abstract class AbstractSpecWriter {
     	}
     }
     
+    /**
+     * Writes the buffers to streams.
+     * @param tlaStream TLA stream; if null, nothing is written.
+     * @param cfgStream CFG stream; if null, nothing is written.
+     * @throws IOException If there is an error writing to stream.
+     */
+    public void writeStreams(final OutputStream tlaStream, final OutputStream cfgStream) throws IOException {
+    	final ContentWriter cw = (inputStream, forTlaFile) -> {
+    		final OutputStream outputStream = forTlaFile ? tlaStream : cfgStream;
+    		if (null != outputStream) {
+    			// If we upgrade to Java 9 we can use InputStream.transferTo()
+    			// For Java 8 compatibility we will copy the bytes manually
+    			final byte[] buffer = new byte[8192];
+    			int length;
+    			while ((length = inputStream.read(buffer)) > 0) {
+    				outputStream.write(buffer, 0, length);
+    			}
+    		}
+    	};
+    	
+    	writeFiles(cw);
+    }
+    
     /**
      * Write the buffers to files.
      * 
@@ -197,6 +222,13 @@ public abstract class AbstractSpecWriter {
 		tlaBuffer.append(nextDefinition[1]).append(TLAConstants.CR);
 	}
 
+	public void addConstants(List<String> rawConstants) {
+		for (String constant : rawConstants) {
+			cfgBuffer.append(constant);	
+			cfgBuffer.append("\n");	
+		}
+	}
+
     /**
      * Documentation by SZ: Add constants declarations. 
      * 
@@ -453,6 +485,45 @@ public abstract class AbstractSpecWriter {
 		}
 	}
 	
+	public void addPostCondition(final String postConditionString, final String attributeName) {
+		if (postConditionString.trim().length() != 0) {
+			final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.POST_CONDITION_SCHEME);
+			
+			if (cfgBuffer != null) {
+				cfgBuffer.append(TLAConstants.COMMENT).append("POSTCONDITION definition").append(TLAConstants.CR);
+				cfgBuffer.append("POSTCONDITION").append(TLAConstants.CR).append(id).append(TLAConstants.CR);
+			}
+
+			tlaBuffer.append(TLAConstants.COMMENT).append("POSTCONDITION definition ").append(TLAConstants.ATTRIBUTE);
+			tlaBuffer.append(attributeName).append(TLAConstants.CR);
+			tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(postConditionString);
+			tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+		}
+	}
+	
+	public void addAlias(final String aliasString, final String attributeName) {
+		if (aliasString.trim().length() != 0) {
+			final String id = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.ALIAS_SCHEME);
+			this.addAliasToCfg(id);
+
+			tlaBuffer.append(TLAConstants.COMMENT).append("ALIAS definition ").append(TLAConstants.ATTRIBUTE);
+			tlaBuffer.append(attributeName).append(TLAConstants.CR);
+			tlaBuffer.append(id).append(TLAConstants.DEFINES).append(TLAConstants.CR).append(aliasString);
+			tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+		}
+	}
+	
+	/**
+	 * Specifies an alias in the config file.
+	 * @param aliasName Name of the alias to specify.
+	 */
+	public void addAliasToCfg(final String aliasName) {
+		if (this.cfgBuffer != null) {
+			this.cfgBuffer.append(TLAConstants.COMMENT).append("ALIAS definition").append(TLAConstants.CR);
+			this.cfgBuffer.append("ALIAS").append(TLAConstants.CR).append(aliasName).append(TLAConstants.CR);
+		}
+	}
+	
     /**
      * Assigns a right side to a label using an id generated from given schema
      * @param constant, constant containing the values
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/BroadcastMessagePrinterRecorder.java b/tlatools/org.lamport.tlatools/src/tlc2/output/BroadcastMessagePrinterRecorder.java
new file mode 100644
index 0000000000000000000000000000000000000000..e45ffc1dfbaa7245cda6b8f2f82ac739f97b1b28
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/BroadcastMessagePrinterRecorder.java
@@ -0,0 +1,41 @@
+package tlc2.output;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Pushes recorded events to recorders which subscribe to this broadcaster.
+ */
+public class BroadcastMessagePrinterRecorder implements IMessagePrinterRecorder {
+	
+	/**
+	 * The list of recorders subscribed to this broadcaster.
+	 */
+	private Set<IMessagePrinterRecorder> subscribers = new HashSet<IMessagePrinterRecorder>();
+	
+	@Override
+	public void record(int code, Object... objects) {
+		for (IMessagePrinterRecorder recorder : this.subscribers)
+		{
+			recorder.record(code, objects);
+		}
+	}
+	
+	/**
+	 * Subscribes a recorder to this broadcaster.
+	 * This function is idempotent; recorders cannot be subscribed twice.
+	 */
+	public void subscribe(final IMessagePrinterRecorder recorder)
+	{
+		this.subscribers.add(recorder);
+	}
+	
+	/**
+	 * Unsubscribes a recorder from this broadcaster.
+	 * @param recorder The recorder the unsubscribe.
+	 */
+	public void unsubscribe(final IMessagePrinterRecorder recorder)
+	{
+		this.subscribers.remove(recorder);
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/CFGCopier.java b/tlatools/org.lamport.tlatools/src/tlc2/output/CFGCopier.java
index 89f80c77dedbff5b22d8cd65a3e16df5eda3eb35..032fefbb79772c02ec8eed2bcb9d2a862061c779 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/CFGCopier.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/CFGCopier.java
@@ -92,7 +92,7 @@ public class CFGCopier extends AbstractCopier {
 	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,
+											   TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME,
 											   new File(args[0]),
 											   initNext);
 		copier.copy();
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/EC.java b/tlatools/org.lamport.tlatools/src/tlc2/output/EC.java
index 48203f947eb0b674ff3c768382525bffb4140380..ac3ebe4b68b058f4f95a891eac9295ec558ff8cb 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/EC.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/EC.java
@@ -88,7 +88,28 @@ public interface EC
 
     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.
+    
+    /**
+     * This error code accompanies the final state in a liveness property
+     * counter-example trace, when the trace takes the form of a lasso
+     * (so the accompanied state is a state we are going "back to").
+     * For traces ending in stuttering, see {@link EC#TLC_STATE_PRINT3}.
+     * Note this code only accompanies the *final* state in the trace,
+     * and the prefix traces are accompanied by the
+     * {@link EC#TLC_STATE_PRINT2} code.
+     */
+    public static final int TLC_BACK_TO_STATE = 2122;
+
+    /**
+     * This error code accompanies the final state in a liveness property
+     * counter-example trace, when the trace ends in stuttering. For traces
+     * ending in a lasso, see {@link EC#TLC_BACK_TO_STATE}. Note this code
+     * only accompanies the *final* state in the trace, and the prefix
+     * traces are accompanied by the {@link EC#TLC_STATE_PRINT2} code.
+     */
+    public static final int TLC_STATE_PRINT3 = 2218;
+
+    public static final int TLC_SANY_END = 2219;
     public static final int TLC_FAILED_TO_RECOVER_INIT = 2123;
     public static final int TLC_REPORTER_DIED = 2124;
 
@@ -240,11 +261,23 @@ public interface EC
     public static final int TLC_EXPECTED_EXPRESSION_IN_COMPUTING2 = 2248;
     
     
-    // state printing
+    /**
+     * This error code is used in the following situations:
+     *  - During DFID model checking, when next state is not fully defined
+     *  - In the {@link tlc2.tool.CheckImpl} tool, when there is an invalid step
+     *  - During Simulation model checking, when maximum trace depth is reached
+     */
     public static final int TLC_STATE_PRINT1 = 2216;
+    
+    /**
+     * This error code is used in the following situations:
+     *  - Printing a safety invariant violation error trace
+     *  - Printing every state except the final state of a liveness error trace; the final state is printed with:
+     *    * {@link EC#TLC_BACK_TO_STATE} for liveness traces ending in a lasso
+     *    * {@link EC#TLC_STATE_PRINT3} for liveness traces ending in stuttering
+     */
     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;
@@ -252,6 +285,7 @@ public interface EC
     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_CONSTRAINT = 2778;
     public static final int TLC_COVERAGE_END_OVERHEAD = 2777;
     
     // config file errors
@@ -292,6 +326,9 @@ public interface EC
     
     public static final int TLC_ENVIRONMENT_JVM_GC = 2401;
 
+    // Codes for trace expression spec generation events
+    public static final int TLC_TE_SPEC_GENERATION_COMPLETE = 2501;
+    public static final int TLC_TE_SPEC_GENERATION_ERROR = 2502;
     
     //**************************************************************//
     // Mapping error constants above to process exit/return values. //
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/ErrorTraceMessagePrinterRecorder.java b/tlatools/org.lamport.tlatools/src/tlc2/output/ErrorTraceMessagePrinterRecorder.java
new file mode 100644
index 0000000000000000000000000000000000000000..5757b21e71c367ab2c00c02065b5508945872da8
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/ErrorTraceMessagePrinterRecorder.java
@@ -0,0 +1,116 @@
+package tlc2.output;
+
+import java.util.List;
+import java.util.Optional;
+
+import tlc2.model.MCError;
+import tlc2.model.MCState;
+import tlc2.tool.TLCStateInfo;
+
+/**
+ * Saves all messages containing info about error traces that pass through {@link tlc.output.MP}.
+ * Ideally this will eventually go away and all of TLC's model checking implementations will
+ * bubble their error traces up through their top-level .run() methods, but until that
+ * refactoring takes place this is how we get the error trace: by hooking into the static
+ * console output handler class and intercepting TLC output.
+ * 
+ * There are a number of places that error traces are generated within TLC:
+ *  - Basic local BFS model checking in {@link tlc2.tool.ModelChecker#doNextCheckInvariants}
+ *    > note: appears to be dead, the concurrent BFS implementation is always used instead
+ *  - Concurrent local BFS model checking in {@link tlc2.tool.Worker#doNextCheckInvariants}
+ *  - DFID local model checking in {@link tlc2.tool.DFIDModelChecker#doNext}
+ *  - Simulator local model checking in {@link tlc2.tool.Simulator#simulate}
+ *  - Liveness checking in {@link tlc2.tool.liveness.LiveCheck#check0}
+ *  - Distributed model checking in {@link tlc2.tool.distributed.TLCServerThread#run}
+ * All of these pass through {@link tlc2.output.StatePrinter} then {@link tlc.output.MP}.
+ *  
+ * The purpose of this class is to record error trace output from all of those sources while
+ * ignoring output that is not an error trace (some of which superficially resembles error
+ * traces, for example printing out an invalid/incomplete state transition).
+ */
+public class ErrorTraceMessagePrinterRecorder implements IMessagePrinterRecorder {
+	
+	/**
+	 * The error trace, if one exists.
+	 */
+	private Optional<MCError> errorTrace = Optional.empty();
+	
+	/**
+	 * Whether the trace has terminated or more states are expected;
+	 */
+	private boolean traceFinished = false;
+	
+	/**
+	 * Gets the error trace, if it exists.
+	 * @return The error trace.
+	 */
+	public Optional<MCError> getMCErrorTrace() {
+		return this.errorTrace;
+	}
+	
+	@Override
+	public void record(int code, Object... objects) {
+		if (!traceFinished) {
+			switch (code) {
+				case EC.TLC_STATE_PRINT2:
+					if (objects.length >= 2
+						&& objects[0] instanceof TLCStateInfo
+						&& objects[1] instanceof Integer) {
+						TLCStateInfo stateInfo = (TLCStateInfo)objects[0];
+						Integer stateOrdinal = (Integer)objects[1];
+						stateInfo.stateNumber = stateOrdinal;
+
+						// Idempotent transition from no trace to safety trace
+						this.errorTrace = Optional.of(this.errorTrace.orElse(new MCError()));
+
+						// Add state to trace
+						MCState state = new MCState(stateInfo);
+						this.errorTrace.ifPresent(trace -> trace.addState(state));
+					}
+
+					break;
+				case EC.TLC_STATE_PRINT3:
+					// Mark trace as ending in stuttering
+					this.traceFinished = true;
+					this.errorTrace.ifPresent(trace -> {
+						List<MCState> states = trace.getStates();
+						if (states.size() > 0) {
+							MCState finalState = states.get(states.size() - 1);
+							MCState stutteringState = new MCState(finalState, true, false);
+							trace.addState(stutteringState);
+						}
+					});
+
+					break;
+				case EC.TLC_BACK_TO_STATE:
+					// Lasso reporting output varies based on -tool setting
+					Optional<Integer> stateOrdinal = Optional.empty();
+					if (objects.length >= 2
+						&& objects[0] instanceof TLCStateInfo
+						&& objects[1] instanceof Integer) {
+						stateOrdinal = Optional.of((Integer)objects[1]);
+					} else if (objects.length >= 2
+						&& objects[0] instanceof String
+						&& objects[1] instanceof String) {
+						try {
+							stateOrdinal = Optional.of(Integer.parseInt((String)objects[0]));
+						} catch (NumberFormatException e) { }
+					}
+
+					stateOrdinal.ifPresent(ord -> {
+						this.traceFinished = true;
+						this.errorTrace.ifPresent(trace -> {
+							List<MCState> states = trace.getStates();
+							if (0 < ord && ord <= states.size()) {
+								MCState finalState = states.get(ord - 1);
+								MCState lassoState = new MCState(finalState, false, true);
+								trace.addState(lassoState);
+							}
+						});
+					});
+					
+					break;
+			}
+		}
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/IMessagePrinterRecorder.java b/tlatools/org.lamport.tlatools/src/tlc2/output/IMessagePrinterRecorder.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfc3def87b293b01b8b569fc071f2e2e45221841
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/IMessagePrinterRecorder.java
@@ -0,0 +1,14 @@
+package tlc2.output;
+
+/**
+ * Interface for recording messages that pass through {@link tlc2.output.MP}.
+ */
+public interface IMessagePrinterRecorder {
+	
+	/**
+	 * Records a message.
+	 * @param code Code identifying the message type.
+	 * @param objects Data comprising the message.
+	 */
+	public void record(int code, Object... objects);
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/MP.java b/tlatools/org.lamport.tlatools/src/tlc2/output/MP.java
index 2a5847ffcdcbf99fcac969d24e99f51baeea04c5..34ec44e91f531330f1fd70cadcd87fda5ff469d0 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/MP.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/MP.java
@@ -207,7 +207,7 @@ public class MP
     public static final String NOT_APPLICABLE_VAL = "-1";
 
     private static MP instance = null;
-	private static MPRecorder recorder = new MPRecorder();
+	private static BroadcastMessagePrinterRecorder recorder = new BroadcastMessagePrinterRecorder();
     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$ 
@@ -1097,12 +1097,12 @@ 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 " + now()
+                b.append("Progress(%2%) at " + now()
                         + ": %1% states generated, " + NOT_APPLICABLE_VAL + " distinct states found, "
                         + NOT_APPLICABLE_VAL + " states left on queue.");
             } else
             {
-                b.append("Progress: %1% states checked.");
+                b.append("Progress: %1% states checked, %2% traces generated (trace length: mean=%3%, var(x)=%4%, sd=%5%)");
             }
             break;
 
@@ -1121,6 +1121,9 @@ public class MP
         case EC.TLC_COVERAGE_NEXT:
        		b.append("%1%: %2%:%3%");
             break;
+        case EC.TLC_COVERAGE_CONSTRAINT:
+       		b.append("%1%: %2%:%3%");
+            break;
         case EC.TLC_COVERAGE_PROPERTY:
        		b.append("%1%");
             break;
@@ -1249,7 +1252,7 @@ public class MP
             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.");
+					"Please run the Java VM, which executes TLC with a throughput optimized garbage collector, by passing the \"-XX:+UseParallelGC\" property.");
             break;
 
         /* ************************************************************************ */
@@ -1267,7 +1270,15 @@ public class MP
         case EC.TLC_STATE_PRINT3:
             b.append("%1%:").append(TLAConstants.STUTTERING);
             break;
-
+            
+        /* ************************************************************************ */
+        case EC.TLC_TE_SPEC_GENERATION_COMPLETE:
+        	b.append("Trace exploration spec path: %1%");
+        	break;
+        case EC.TLC_TE_SPEC_GENERATION_ERROR:
+        	b.append("Failed to generate trace exploration spec; error message: %1%");
+        	break;
+            
         /* ************************************************************************ */
         // configuration file errors
         case EC.CFG_MISSING_ID:
@@ -1613,15 +1624,12 @@ public class MP
      */
     public static void printState(int code, String[] parameters, TLCState state, int num)
     {
-		recorder.record(code, new TLCStateInfo(state, ""), num);
-        DebugPrinter.print("entering printState(String[])"); //$NON-NLS-1$
-        ToolIO.out.println(getMessage(STATE, code, parameters));
-        DebugPrinter.print("leaving printState(String[])"); //$NON-NLS-1$
+        printState(code, parameters, new TLCStateInfo(state, num), num);
     }
     
     public static void printState(int code, String[] parameters, TLCStateInfo stateInfo, int num)
     {
-		recorder.record(code, (TLCStateInfo) stateInfo, num);
+		recorder.record(code, stateInfo, num);
         DebugPrinter.print("entering printState(String[])"); //$NON-NLS-1$
         ToolIO.out.println(getMessage(STATE, code, parameters));
         DebugPrinter.print("leaving printState(String[])"); //$NON-NLS-1$
@@ -1759,8 +1767,12 @@ public class MP
         ToolIO.err.flush();
     }
 
-	public static void setRecorder(MPRecorder aRecorder) {
-		recorder = aRecorder;
+	public static void setRecorder(IMessagePrinterRecorder mpRecorder) {
+		recorder.subscribe(mpRecorder);
+	}
+	
+	public static void unsubscribeRecorder(IMessagePrinterRecorder mpRecorder) {
+		recorder.unsubscribe(mpRecorder);
 	}
 
     private static String now() {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/SpecTraceExpressionWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/output/SpecTraceExpressionWriter.java
index 0ccd401726e88e50bf6a875a3f9cf4d19e559a90..ecef70aeefb5703ea4f076c71694a1dc2ab1f4f8 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/SpecTraceExpressionWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/SpecTraceExpressionWriter.java
@@ -11,6 +11,7 @@ import tlc2.model.Formula;
 import tlc2.model.MCState;
 import tlc2.model.MCVariable;
 import tlc2.model.TraceExpressionInformationHolder;
+import tlc2.tool.impl.ModelConfig;
 import util.TLAConstants;
 
 /**
@@ -199,7 +200,7 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	 * @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}
+	 * 				definitions for Init and finally Next.
 	 */
 	public static StringBuilder[] addInitNextToBuffers(final StringBuilder cfgBuffer,
 													   final List<MCState> trace,
@@ -265,7 +266,7 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 						initAndNext.append(TLAConstants.TRACE_NA);
 					} else {
 	                    // add the actual expression if it is not temporal level
-						initAndNext.append(expressionInfo.getExpression());
+						initAndNext.append(expressionInfo.getIdentifier());
 	                }
 	
 					initAndNext.append(TLAConstants.CR).append(TLAConstants.INDENT).append(TLAConstants.INDENT);
@@ -307,15 +308,7 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	         * 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
+	         * relation (42 for the example above).
 	         */
 	        final StringBuilder nextDisjunctBuffer = new StringBuilder();
 	        nextDisjunctBuffer.append(nextId).append(TLAConstants.DEFINES_CR);
@@ -453,7 +446,7 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	                    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.getExpression()).append(TLAConstants.CR);
+	                    subActionsAndConstraint.append(expressionInfo.getIdentifier()).append(TLAConstants.CR);
 	                    subActionsAndConstraint.append(TRI_INDENT).append(TLAConstants.R_PAREN);
 	
 						if (expressionInfo.getLevel() < 2) {
@@ -493,6 +486,9 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 			initAndNext.append(TLAConstants.CR).append(TLAConstants.CR);
 	        
 			
+			/**
+			 * Action Constraint
+			 */
 			subActionsAndConstraint.append(TLAConstants.COMMENT).append("TRACE Action Constraint definition ");
 			subActionsAndConstraint.append(TLAConstants.TraceExplore.TRACE_EXPLORE_ACTION_CONSTRAINT);
 			subActionsAndConstraint.append(TLAConstants.CR).append(actionConstraintBuffer.toString());
@@ -504,38 +500,40 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	        return new StringBuilder[] { subActionsAndConstraint, initAndNext };
 	    }
 		
-		return null;
+		return new StringBuilder[] { new StringBuilder(), new StringBuilder() };
 	}
 
-	public static void addTraceFunctionToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
-			final List<MCState> input) {
+	public static String addTraceFunctionToBuffers(final StringBuilder tlaBuffer, final StringBuilder cfgBuffer,
+			final List<MCState> input, final String id) {
 		// 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;
+			return addArrowAssignmentIdToBuffers(tlaBuffer, cfgBuffer,
+					new Assignment(TLAConstants.TraceExplore.TRACE, new String[0], TLAConstants.BEGIN_TUPLE + TLAConstants.END_TUPLE),
+					id);
 	    }
 		
 		// Trace
 		final StringBuilder traceFunctionDef = new StringBuilder();
-		traceFunctionDef.append(TLAConstants.BEGIN_TUPLE).append(TLAConstants.CR);
+		traceFunctionDef.append(TLAConstants.INDENT).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);
+			traceFunctionDef.append(TLAConstants.INDENT).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(TLAConstants.CR).append(TLAConstants.INDENT).append(TLAConstants.END_TUPLE);
 		traceFunctionDef.append(CLOSING_SEP).append(TLAConstants.CR);
 		
-		addArrowAssignmentToBuffers(tlaBuffer, cfgBuffer,
+		return addArrowAssignmentIdToBuffers(tlaBuffer, cfgBuffer,
 				new Assignment(TLAConstants.TraceExplore.TRACE, new String[0], traceFunctionDef.toString()),
-				TLAConstants.Schemes.DEFOV_SCHEME);
+				id);
 	}
 	
 	
@@ -585,7 +583,13 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 		addPrimer(moduleFilename, extendedModuleName, new HashSet<>());
 	}
 	
-	public void addPrimer(final String moduleFilename, final String extendedModuleName, final Set<String> extraExtendedModules) {
+	/**
+	 * Adds MODULE and EXTENDS statements.
+	 */
+	public void addPrimer(
+			final String moduleFilename,
+			final String extendedModuleName,
+			final Set<String> extraExtendedModules) {
 		if (extendedModuleName != null) {
 			extraExtendedModules.add(extendedModuleName);
 		}
@@ -600,7 +604,33 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 		tlaBuffer.append(SpecWriterUtilities.getExtendingModuleContent(moduleFilename,
 				extraExtendedModules.toArray(new String[extraExtendedModules.size()])));
 	}
+	
+	/**
+	 * Adds the trace expression stub to the TE spec.
+	 * This is an alias function which applies the identity transformation
+	 * to the spec's variables, with some comments explaining how to add
+	 * additional transformations for custom trace expressions.
+	 * @param teName Name of trace expression.
+	 * @param variables Spec variables; transformed by identity.
+	 */
+	public void addTraceExpressionStub(String teName, final List<String> variables) {
+		this.tlaBuffer.append(teName + TLAConstants.DEFINES + TLAConstants.CR);
+		this.tlaBuffer
+				.append(TLAConstants.INDENT + TLAConstants.L_SQUARE_BRACKET + TLAConstants.CR + TLAConstants.INDENT);
+		this.tlaBuffer.append(TLAConstants.INDENT
+				+ variables.stream().map(var -> var + TLAConstants.RECORD_ARROW + var).collect(Collectors
+						.joining(TLAConstants.CR + TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.COMMA)));
+		this.tlaBuffer.append(TLAConstants.CR);
+		this.tlaBuffer.append(TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.COMMENT + "Put additional trace expressions here; examples:" + TLAConstants.CR);
+		this.tlaBuffer.append(TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.COMMENT + TLAConstants.COMMA + "x" + TLAConstants.RECORD_ARROW + TLAConstants.TLA_NOT + "y" + TLAConstants.PRIME + TLAConstants.CR);
+		this.tlaBuffer.append(TLAConstants.INDENT + TLAConstants.INDENT + TLAConstants.COMMENT + TLAConstants.COMMA + "e" + TLAConstants.RECORD_ARROW + TLAConstants.KeyWords.ENABLED + TLAConstants.SPACE + "ActionName" + TLAConstants.CR);
+		this.tlaBuffer.append(TLAConstants.INDENT + TLAConstants.R_SQUARE_BRACKET + TLAConstants.CR + TLAConstants.CR);
+	}
 
+	public void addFooter() {
+		tlaBuffer.append(getTLAModuleClosingTag());
+	}
+	
 	/**
 	 * This only changes the tla file. This method adds a variable declaration
 	 * for each element of traceExpressionData and, if the flag addDefinitions is true,
@@ -689,12 +719,60 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	
 	    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(TLAConstants.INDENT).append(TLAConstants.TLA_NOT).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+	    tlaBuffer.append(TLAConstants.INDENT).append(getStateConjunction(finalState)).append(TLAConstants.CR);
+	    tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.R_PAREN);
 	
 	    tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
 	}
 
+	public void addProperties(final List<MCState> trace) {
+        MCState finalState = trace.get(trace.size() - 1);
+        boolean isBackToState = finalState.isBackToState();
+        boolean isStuttering = finalState.isStuttering();
+
+        // add temporal property or invariant depending on type of trace
+        // read the method comments to see the form of the invariant or property
+        if (isStuttering)
+        {
+            addStutteringProperty(trace.get(trace.size() - 2));
+        } else if (isBackToState)
+        {
+            addBackToStateProperty(trace.get(trace.size() - 2), trace.get(finalState.getStateNumber() - 1));
+        } else
+        {
+            // checking deadlock eliminates the need for the following
+			// MAK 06/26/2020: write.addInvariant(finalState) below used to be commented
+			// with the comment above about deadlock checking taking care of it. The
+			// statement is wrong when an error-trace is not the shortest possible trace,
+			// because bfs (run with TE) on such a trace might a) shorten it and b) no
+			// longer deadlocks. This is the possible with traces that can come out of
+			// simulation mode.
+			// Assume any spec and an invariant such as TLCGet("level") < n for some n \in Nat
+        	// (larger n increase the probability of a behavior with a sequence of stuttering
+        	// steps). If the simulator happens to generate a behavior with a sequence of
+			// stuttering step, the generated TE.tla will define a behavior that allows infinite
+			// stuttering (for each stuttering step, there will be a disjunct in the disjuncts
+			// of the next-state relation), which is not a deadlock. We could require the
+        	// simulator to run with the "-difftrace" command-line parameter, which will remove
+        	// successive stuttering steps.  However, it seems like an unnecessary requirement
+        	// given that checking an invariant instead of deadlock has no drawback.
+			// I ran into this issue when I used the simulator to generate very long traces
+			// (1000+) for a spec (AsyncGameOfLife.tla) that models an Asynchronous Cellular
+			// Automaton (https://uhra.herts.ac.uk/bitstream/handle/2299/7041/102007.pdf),
+			// to use as input for Will Schultz's animation module (result at
+			// https://github.com/lemmy/tlaplus_specs/blob/master/AsyncGameOfLifeAnimBlinker.mp4).
+            addInvariant(finalState);
+        }
+        
+		// Do not require to pass -deadlock on the command-line (properties assert that
+		// TLC re-finds the error-trace).
+		cfgBuffer.append(TLAConstants.COMMENT).append(ModelConfig.CheckDeadlock).append(" off because of PROPERTY or INVARIANT above.")
+				.append(TLAConstants.CR);
+		cfgBuffer.append(ModelConfig.CheckDeadlock).append(TLAConstants.SPACE).append(TLAConstants.FALSE);
+		cfgBuffer.append(TLAConstants.CR);
+	}
+
 	/**
 	 * Adds the temporal property ~<>[](P) where P is the formula describing finalState.
 	 * The format in the tla file is as follows:
@@ -722,7 +800,7 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 
 	/**
 	 * 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:
+	 * Q the formula describing backToState. The formatting in the tla file is as follows:
 	 * 
 	 * prop_21321312 ==
 	 * ~(([]<>(
@@ -774,8 +852,8 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 	/**
 	 * @see #addInitNextToBuffers(StringBuilder, StringBuilder, List, TraceExpressionInformationHolder[])
 	 */
-	public String[] addInitNext(final List<MCState> trace, final TraceExpressionInformationHolder[] expressionData) {
-		return addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData);
+	public String[] addInitNext(final List<MCState> trace) {
+		return addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, null);
 	}
 
 	/**
@@ -795,11 +873,142 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
 		addInitNextToBuffers(tlaBuffer, cfgBuffer, trace, expressionData, initId, nextId, actionConstraintId,
 							 nextSubActionBasename, true);
 	}
+	
+	public void addInitNext(final List<MCState> trace, final String initId, String nextId,
+			final String actionConstraintId, final String nextSubActionBasename) {
+		addInitNext(trace, null, initId, nextId, actionConstraintId, nextSubActionBasename);
+	}
+
+	public void addInitNextTraceFunction(final List<MCState> trace, final List<String> vars, final String initId, String nextId) {
+        /*******************************************************
+         * 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);
+		}
+		
+		tlaBuffer.append(TLAConstants.COMMENT).append("TRACE INIT definition ");
+		tlaBuffer.append(TLAConstants.TraceExplore.TRACE_EXPLORE_INIT).append(TLAConstants.CR);
+		tlaBuffer.append(initId).append(TLAConstants.DEFINES_CR);
+        
+        // variables from spec
+		for (String var : vars) {
+            tlaBuffer.append(TLAConstants.INDENTED_CONJUNCTIVE);
+            tlaBuffer.append(var).append(TLAConstants.EQ).append("_TETrace[1].").append(var);
+            tlaBuffer.append(TLAConstants.CR);
+        }
+		
+		tlaBuffer.append(CLOSING_SEP).append(TLAConstants.CR);
+		
+        /************************************************
+         *  Now add the next state relation             *
+         ************************************************/
+		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);
+		}
+		
+		tlaBuffer.append(TLAConstants.COMMENT).append("TRACE NEXT definition ");
+		tlaBuffer.append(TLAConstants.TraceExplore.TRACE_EXPLORE_NEXT).append(TLAConstants.CR);
+		// _SpecTENext == 
+		tlaBuffer.append(nextId).append(TLAConstants.DEFINES_CR);
+		if (trace.size() == 1) {
+			tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+			tlaBuffer.append("FALSE").append(TLAConstants.CR);
+		} else {
+			tlaBuffer.append(TLAConstants.INDENTED_CONJUNCTIVE).append("\\E i,j \\in DOMAIN _TETrace:")
+					.append(TLAConstants.CR);
+			tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE).append(TLAConstants.TLA_OR).append(" j = i + 1")
+					.append(TLAConstants.CR);
+			// Back to state?
+			final MCState finalState = trace.get(trace.size() - 1);
+			final boolean isBackToState = finalState.isBackToState();
+			if (isBackToState) {
+				// Instead of this disjunct, we could append backToState to the trace function
+				// (_TETrace). Len(_TETrace) would however be off by one.
+				MCState backToState = trace.get(finalState.getStateNumber() - 1);
+				tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENT).append("   ")
+						.append(TLAConstants.TLA_OR).append(TLAConstants.SPACE).append(TLAConstants.TLA_AND)
+						// Len(_TETrace) requires EXTENDS Sequences
+						.append(" i = ").append(trace.size() - 1).append(TLAConstants.CR);
+				tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENT).append("  ")
+						.append(TLAConstants.INDENTED_CONJUNCTIVE).append("j = ").append(backToState.getStateNumber())
+						.append(TLAConstants.CR);
+			}
+			for (String var : vars) {
+	            // x = _TETrace[_TEPosition].x
+				tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+				tlaBuffer.append(var).append(" ").append(TLAConstants.EQ).append("_TETrace[i].")
+						.append(var);
+				tlaBuffer.append(TLAConstants.CR);
+
+				// x' = _TETrace[_TEPosition+1].x
+				tlaBuffer.append(TLAConstants.INDENT).append(TLAConstants.INDENTED_CONJUNCTIVE);
+				tlaBuffer.append(var).append(TLAConstants.PRIME);
+				tlaBuffer.append(TLAConstants.EQ).append("_TETrace[j].").append(var);
+				tlaBuffer.append(TLAConstants.CR);
+	        }
+		}
+
+		tlaBuffer.append(TLAConstants.CR).append(TLAConstants.CR);
+	}
 
-	public void addTraceFunction(final List<MCState> input) {
-		addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, input);
+	public String addTraceFunction(final List<MCState> input) {
+		return addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, input,
+				SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.DEFOV_SCHEME));
 	}
-	
+
+	public String addTraceFunction(final List<MCState> input, final String id) {
+		return addTraceFunctionToBuffers(tlaBuffer, cfgBuffer, input, id);
+	}
+
+	/*
+	 * See https://github.com/tlaplus/tlaplus/issues/482 for why we create the
+	 * _SpecTETraceDef symbol. In short, it leads to faster evaluation because TLC's
+	 * caching kicks in.
+	 * 
+	 * The reason why the trace function is in a dedicated module (via monolith spec
+	 * functionality) is to make it easy for users to edit SpecTE to replace the
+	 * TLA+ encoded trace function with a significantly more efficient binary
+	 * encoding to work around deficiencies in SANY and semantic processing.
+	 */
+	public String addTraceFunctionInstance() {
+		/*
+		 * SpecTETraceDef == INSTANCE SpecTETraceDef
+		 * def_ov_15940964130543000 == SpecTETraceDef!def_ov_15940964130543000
+		 */
+		tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME)
+				.append(" definition").append(TLAConstants.CR);
+		final String identifier = SpecWriterUtilities.getValidIdentifier(TLAConstants.Schemes.DEFOV_SCHEME);
+		tlaBuffer.append(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + "TraceDef == INSTANCE "
+				+ TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME).append(TLAConstants.CR);
+		tlaBuffer.append(identifier).append(TLAConstants.DEFINES)
+				.append(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME + "TraceDef!").append(identifier)
+				.append(TLAConstants.CR).append(TLAConstants.CR);
+		return identifier;
+	}
+
+	public void addTraceExpressionInstance(final String moduleName) {
+		/* With EWD840_TE as moduleName:
+		   \* Trace Expression declaration
+           TTraceExpression == 
+               LET EWD840_TE == INSTANCE EWD840_TE 
+               IN EWD840_TE!TraceExpression
+		 */
+		tlaBuffer.append(TLAConstants.COMMENT).append(TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME)
+				.append(" declaration").append(TLAConstants.CR);
+		tlaBuffer
+				.append(String.format("%s ==%s%sLET %s == INSTANCE %s%s%sIN %s!%s",
+						TLAConstants.TraceExplore.SPEC_TE_TTRACE_EXPRESSION, TLAConstants.CR, TLAConstants.INDENT,
+						moduleName, moduleName, TLAConstants.CR, TLAConstants.INDENT, moduleName,
+						TLAConstants.TraceExplore.SPEC_TE_TRACE_EXPRESSION))
+				.append(TLAConstants.CR).append(TLAConstants.CR);
+	}
+
     /**
      * Returns a string representing the formula describing the state.
      * If the state has var1=expr1, var2 = expr2, and var3=expr3, then this returns:
@@ -832,16 +1041,28 @@ public class SpecTraceExpressionWriter extends AbstractSpecWriter {
             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);
+				formula.append(TLAConstants.INDENT).append(var.getName()).append(TLAConstants.EQ).append(TLAConstants.L_PAREN).append(TLAConstants.CR);
+				formula.append(TLAConstants.INDENT).append(var.getValueAsString()).append(TLAConstants.CR).append(TLAConstants.INDENT).append(TLAConstants.R_PAREN);
 
 				// append /\ except for the last variable
 				if (i != (vars.length - 1)) {
-                    formula.append(TLAConstants.TLA_AND).append(TLAConstants.CR);
+                    formula.append(TLAConstants.INDENT).append(TLAConstants.TLA_AND).append(TLAConstants.CR);
                 }
             }
 
             return formula.toString();
         }
     }
+
+	public StringBuilder append(String str) {
+		return tlaBuffer.append(str);
+	}
+	
+	public String toString() {
+		return tlaBuffer.toString();
+	}
+
+	public String getComment() {
+		return tlaBuffer.toString().replaceFirst("^", "\\\\*").replaceAll("\n", "\n\\\\*");
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/StatePrinter.java b/tlatools/org.lamport.tlatools/src/tlc2/output/StatePrinter.java
index 819cb666eac11404a0938d3f3b76d0feb88e6ae0..8e122fb45969630b5db076daf0bab6182925d45d 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/StatePrinter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/output/StatePrinter.java
@@ -11,96 +11,103 @@ import tlc2.tool.TLCStateInfo;
  */
 public class StatePrinter
 {
-    /**
-     * 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 
-     */
-    public static void printState(TLCState currentState, TLCState lastState, int num)
-    {
-        String stateString;
-        /* Added by rjoshi. */
-        if (lastState != null && TLCGlobals.printDiffsOnly)
-        {
-            stateString = currentState.toString(lastState);
-        } else
-        {
-            stateString = currentState.toString();
-        }
-        MP.printState(EC.TLC_STATE_PRINT1, new String[] { String.valueOf(num), stateString }, currentState, num);
-    }
-
-    /**
-     * Prints the state with number
-     */
-    public static void printState(TLCState currentState, int num)
+	/**
+	 * Prints a single state out of a larger error trace, when the error is not
+	 * related to invalidation of an invariant. This would be used if, for
+	 * example, TLC finds a step where not all variables have defined values.
+	 * @param currentState The single state in the trace.
+	 * @param num The index of the state in the trace, counting from one.
+	 */
+    public static void printRuntimeErrorStateTraceState(TLCState currentState, int num)
     {
         MP.printState(EC.TLC_STATE_PRINT1, new String[] { String.valueOf(num), currentState.toString() }, currentState, num);
     }
 
     /**
-     * Prints the state
+     * Prints a single state for error reporting purposes, for example if the
+     * state is invalid in some way. Not to be used when printing states just
+     * because an invariant has been violated.
+     * @param currentState The state to print.
      */
-    public static void printState(TLCState currentState)
+    public static void printStandaloneErrorState(TLCState currentState)
     {
         MP.printState(EC.TLC_STATE_PRINT1, new String[] { "", currentState.toString() }, currentState, -1);
     }
 
-    public static void printState(TLCStateInfo currentStateInfo) {
+    /**
+     * Prints a single state out of a larger state trace, for use when an
+     * invariant is violated.
+     * @param currentStateInfo
+     */
+    public static void printInvariantViolationStateTraceState(TLCStateInfo currentStateInfo) {
     	if (currentStateInfo.predecessorState == null) {
     		// It's an initial state
-			printState(currentStateInfo, null, (int) currentStateInfo.stateNumber);
+			StatePrinter.printInvariantViolationStateTraceState(currentStateInfo, null, (int) currentStateInfo.stateNumber);
     	} else {
-			printState(currentStateInfo, currentStateInfo.predecessorState.state, (int) currentStateInfo.stateNumber);
+			StatePrinter.printInvariantViolationStateTraceState(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 
+     * Prints a single state out of a larger state trace, for use when an
+     * invariant is violated.
+     * If the {@link TLCGlobals#printDiffsOnly} flag is set, will print state
+     * diffs instead of full state.
+     * @param currentStateInfo Information about the single state in the trace.
+     * @param previousState The previous state in the trace for diff printing.
+     * @param num The index of the state in the trace, counting from one.
      */
-    public static void printState(TLCStateInfo currentStateInfo, TLCState lastState, int num)
+    public static void printInvariantViolationStateTraceState(TLCStateInfo currentStateInfo, TLCState previousState, int num)
     {
-        String stateString;
+        final String stateString =
+        		null != previousState && TLCGlobals.printDiffsOnly
+        		? currentStateInfo.state.toString(previousState)
+        		: currentStateInfo.state.toString();
+ 
+		// Fingerprint can't be calculated when state is incomplete; just use a random value.
+        final String fingerprint =
+        		currentStateInfo.state.allAssigned()
+        		? String.valueOf(currentStateInfo.fingerPrint())
+        		: "-1";
+
+		// Metadata is the state number/ordinal and action name/location that is printed
+		// above the actual state (values to variables).
+        final String[] metadata = new String[] {
+				String.valueOf(num),
+				currentStateInfo.info.toString(),
+				stateString,
+				fingerprint
+        };
 
-        /* Added by rjoshi. */
-        if (lastState != null && TLCGlobals.printDiffsOnly)
-        {
-            stateString = currentStateInfo.state.toString(lastState);
-        } else
-        {
-            stateString = currentStateInfo.state.toString();
-        }
-        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);
-        }
-        OutputCollector.addStateToTrace(currentStateInfo);
+		MP.printState(EC.TLC_STATE_PRINT2, metadata, currentStateInfo, num);
+		OutputCollector.addStateToTrace(currentStateInfo);
     }
 
     /**
-     * Reports that the state with a given number is stuttering
+     * Prints a marker for a stuttering state that concludes a liveness
+     * counterexample error trace.
+     * @param num The index of the state in the trace, counting from one.
      */
     public static void printStutteringState(int 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) {
+    /**
+     * Prints a marker for a loopback (lasso) state that concludes a liveness
+     * counterexample error trace.
+     * @param currentStateInfo Info about the loopback state.
+     * @param stateNum The index of the state in the trace, counting from one.
+     */
+	public static void printBackToState(final TLCStateInfo currentStateInfo, final int stateNum) {
 		if (TLCGlobals.tool) {
-			MP.printState(EC.TLC_BACK_TO_STATE, new String[] { "" + stateNum, currentStateInfo.info.toString() }, (TLCState) null, -1);
+			//TODO If the unit test suite runs with -tool mode always turned on, many tests fail because of a NPE.  The NPE results
+			//from null passed here as TLCState, which eventually causes the NPE in tlc2.tool.TLCStateInfo.toString().  When I changed
+			//this from printState to printMessage, nothing obvious broke. However, I suspect some corner case in the Toolbox breaks,
+			//which is why I decided not to touch this. 
+			MP.printState(EC.TLC_BACK_TO_STATE, new String[] { Integer.toString(stateNum), currentStateInfo.info.toString() }, (TLCState)null, stateNum);
 		} else {
-			MP.printMessage(EC.TLC_BACK_TO_STATE, new String[] {"" + stateNum, currentStateInfo.info.toString()});
+			MP.printMessage(EC.TLC_BACK_TO_STATE, new String[] {Integer.toString(stateNum), currentStateInfo.info.toString()});
 		}
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/pprint/Parse.java b/tlatools/org.lamport.tlatools/src/tlc2/pprint/Parse.java
index 902711e27ec23812a628be227796dde413b2e3c2..1b1b6713e2d40400a48119eb3492f7b0a47833a9 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/pprint/Parse.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/pprint/Parse.java
@@ -607,7 +607,7 @@ public class Parse {
     
       // parse the first integer i
       if (! Character.isDigit(string.charAt(last)) ) return null;
-      while (Character.isDigit(string.charAt(last))) {
+      while (last < string.length() && Character.isDigit(string.charAt(last))) {
 	last++;
       }
 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/AbstractChecker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/AbstractChecker.java
index 0c1c885c517194afa09d6c041d08b9eb6eb70748..6cfd9b744c04fa8b1e9811cb288cf0bbffd8a80e 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/AbstractChecker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/AbstractChecker.java
@@ -3,8 +3,11 @@ package tlc2.tool;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.MathContext;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.stream.Collectors;
 
 import tlc2.TLC;
 import tlc2.TLCGlobals;
@@ -12,7 +15,6 @@ 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;
@@ -133,16 +135,18 @@ public abstract class AbstractChecker
 		});
     }
 
-    public final void setDone()
+    public final boolean setDone()
     {
+    	boolean old = this.done;
         this.done = true;
+        return old;
     }
 
     /**
      * Set the error state. 
      * <strong>Note:</note> this method must be protected by lock 
      */
-    public boolean setErrState(TLCState curState, TLCState succState, boolean keep, int errorCode)
+    public boolean setErrState(TLCState curState, TLCState succState, boolean keepCallStack, int errorCode)
     {
        assert Thread.holdsLock(this) : "Caller thread has to hold monitor!";
        if (!TLCGlobals.continuation && this.done)
@@ -152,10 +156,18 @@ public abstract class AbstractChecker
         this.errState = (succState == null) ? curState : succState;
         this.errorCode = errorCode;
         this.done = true;
-        this.keepCallStack = keep;
+        this.keepCallStack = keepCallStack;
         return true;
     }
 
+	public void setError(boolean keepCallStack, int errorCode) {
+		assert Thread.holdsLock(this) : "Caller thread has to hold monitor!";
+		IdThread.resetCurrentState();
+		this.errorCode = errorCode;
+		this.done = true;
+		this.keepCallStack = keepCallStack;
+	}
+
     /**
      * Responsible for printing the coverage information
      * @param workers
@@ -462,7 +474,14 @@ public abstract class AbstractChecker
         {
             workers[i].join();
         }
-        return EC.NO_ERROR;
+		if (!this.keepCallStack) {
+			// A worker explicitly set an errorCode (without interrupting
+			// state-space exploration) and doesn't request to keep the call-stack.
+			// (If a call-stack is requested, this has to return NO_ERROR to not
+			// intercept the outer logic)
+			return this.errorCode != EC.NO_ERROR ? this.errorCode : EC.NO_ERROR;
+		}
+		return EC.NO_ERROR;
     }
     
 	public final void setAllValues(int idx, IValue val) {
@@ -471,6 +490,10 @@ public abstract class AbstractChecker
 		}
 	}
 
+	public final List<IValue> getAllValues(final int idx) {
+		return Arrays.asList(workers).stream().map(w -> w.getLocalValue(idx)).collect(Collectors.toList());
+	}
+
 	public final IValue getValue(int i, int idx) {
 		return workers[i].getLocalValue(idx);
 	}
@@ -563,4 +586,8 @@ public abstract class AbstractChecker
 	public long getDistinctStatesGenerated() {
 		return -1;
 	}
+
+	public long getStatesGenerated() {
+		return -1;
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/CheckImpl.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/CheckImpl.java
index 9b5b91f5a3e74b9b9343fdcf8aa35b915f78731c..b20d20aacd346e6c93727a3a1b90ff5e53ca3250 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/CheckImpl.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/CheckImpl.java
@@ -115,8 +115,8 @@ public abstract class CheckImpl extends ModelChecker {
     Action next = this.tool.getNextStateSpec();    
     if (!this.tool.isValid(next, s0, s1)) {
       ToolIO.out.println("The following transition is illegal: ");
-      StatePrinter.printState(s0);
-      StatePrinter.printState(s1);
+      StatePrinter.printStandaloneErrorState(s0);
+      StatePrinter.printStandaloneErrorState(s1);
       return false;
     }
     int cnt = this.tool.getImpliedActions().length;
@@ -124,8 +124,8 @@ public abstract class CheckImpl extends ModelChecker {
       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);
-	StatePrinter.printState(s1);
+	StatePrinter.printStandaloneErrorState(s0);
+	StatePrinter.printStandaloneErrorState(s1);
 	return false;
       }
     }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/ConcurrentTLCTrace.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/ConcurrentTLCTrace.java
index 5fb94b5761659bc643594fceeeef5728a3e7ce4c..5b808c0078d50c64da195939ec69dc6cd4d01591 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/ConcurrentTLCTrace.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/ConcurrentTLCTrace.java
@@ -25,10 +25,16 @@
  ******************************************************************************/
 package tlc2.tool;
 
+import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import tlc2.TLCGlobals;
+import tlc2.output.EC;
+import tlc2.output.MP;
 import tlc2.util.LongVec;
+import tlc2.value.RandomEnumerableValues;
 
 /**
  * This implementation of a Trace is concurrent in that multiple workers can add
@@ -81,7 +87,8 @@ public class ConcurrentTLCTrace extends TLCTrace {
 			return new TLCStateInfo[] {new TLCStateInfo(state)};
 		}
 		
-		final LongVec fps = new LongVec();
+		final List<Record> records = new ArrayList<>(state.getLevel());
+		records.add(Record.getRecord(state, this.workers));
 
 		// Starting at the given start fingerprint (which is the end of the
 		// trace from the point of the initial states), the sequence of
@@ -90,15 +97,15 @@ public class ConcurrentTLCTrace extends TLCTrace {
 		synchronized (this) {
 			Record record = Record.getPredecessor(state, this.workers);
 			while (!record.isInitial()) {
-				fps.addElement(record.fp);
+				records.add(record);
 				record = record.getPredecessor();
 			}
 			// The fp of the final initial state.
-			fps.addElement(record.fp);
-			assert 0 <= fps.size() && fps.size() <= getLevel();
+			records.add(record);
+			assert 0 <= records.size() && records.size() <= getLevel();
 		}
 		
-		return getTrace(fps);
+		return getTrace(null, records);
 	}
 	
 	public TLCStateInfo[] getTrace(final TLCState from, final TLCState to) throws IOException {
@@ -106,8 +113,9 @@ public class ConcurrentTLCTrace extends TLCTrace {
 			return new TLCStateInfo[] {new TLCStateInfo(to)};
 		}
 		
-		final LongVec fps = new LongVec();
-
+		final List<Record> records = new ArrayList<>(to.getLevel() - from.getLevel());
+		records.add(Record.getRecord(to, this.workers));
+		
 		// 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
@@ -115,15 +123,73 @@ public class ConcurrentTLCTrace extends TLCTrace {
 		synchronized (this) {
 			Record record = Record.getPredecessor(to, this.workers);
 			while (record.fp != from.fingerPrint()) {
-				fps.addElement(record.fp);
+				records.add(record);
 				record = record.getPredecessor();
 			}
 			// The fp of the final initial state.
-			fps.addElement(record.fp);
-			assert 0 <= fps.size() && fps.size() <= getLevel();
+			records.add(record);
+			assert 0 <= records.size() && records.size() <= getLevel();
 		}
 		
-		return getTrace(new TLCStateInfo(from), fps);
+		return getTrace(new TLCStateInfo(from), records);
+	}
+
+	protected final TLCStateInfo[] getTrace(TLCStateInfo sinfo, final List<Record> records) {
+		// 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 = records.size() - 1;
+		final TLCStateInfo[] res = new TLCStateInfo[len];
+		if (len > 0) {
+			if (sinfo == null) {
+				// Recreate initial state from its fingerprint.
+				Record record = records.get(len);
+				assert record.isInitial();
+				sinfo = this.tool.getState(record.fp);
+				
+				Record prev = records.get(len - 1);
+				sinfo.state.workerId = (short) prev.worker;
+				sinfo.state.uid = prev.ptr;
+			}
+			// Recover successor states from its predecessor and its fingerprint.
+			res[stateNum++] = sinfo;
+			for (int i = len-2; i >= 0; i--) {
+				Record record = records.get(i+1);
+				long fp = record.fp;
+				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);
+				}
+				Record prev = records.get(i);
+				sinfo.state.workerId = (short) prev.worker;
+				sinfo.state.uid = prev.ptr;
+				
+				res[stateNum++] = sinfo;
+			}
+		}
+		return res;
 	}
 
 	/**
@@ -157,6 +223,23 @@ public class ConcurrentTLCTrace extends TLCTrace {
 		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 { 
@@ -218,11 +301,14 @@ public class ConcurrentTLCTrace extends TLCTrace {
 	
 	public static class Record {
 		
-
-		static Record getPredecessor(final TLCState state, final Worker[] workers) throws IOException {
-			Record record = workers[state.workerId].readStateRecord(state.uid);
+        static Record getRecord(final TLCState state, final Worker[] workers) throws IOException {
+			final Record record = workers[state.workerId].readStateRecord(state.uid);
 			record.workers = workers;
-			return record.getPredecessor();
+			return record;
+        }
+		
+		static Record getPredecessor(final TLCState state, final Worker[] workers) throws IOException {
+			return getRecord(state, workers).getPredecessor();
 		}
 
 		private final long ptr;
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDModelChecker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDModelChecker.java
index 404fc7b6b66873cd7bc9a3377afb7595a256bff1..c68550b15ad4f1ea83c5d4f49fba5db2c9839a7e 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDModelChecker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDModelChecker.java
@@ -20,6 +20,7 @@ import tlc2.util.IStateWriter;
 import tlc2.util.IdThread;
 import tlc2.util.LongVec;
 import tlc2.util.SetOfStates;
+import util.Assert;
 import util.FileUtil;
 import util.UniqueString;
 
@@ -58,7 +59,14 @@ public class DFIDModelChecker extends AbstractChecker
         // call the abstract constructor
         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, this.tool.getRootFile());
@@ -186,7 +194,9 @@ public class DFIDModelChecker extends AbstractChecker
                 result = this.runTLC(level);
 				// Recent done flag before after the workers have checked the
 				// current level in preparation for the next level.
-                this.done = false;
+                synchronized (this) {
+                	this.done = false;
+				}
                 if (result != EC.NO_ERROR)
                     return result;
 
@@ -617,7 +627,14 @@ public class DFIDModelChecker extends AbstractChecker
 
     private final void printTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2)
     {
-        ((DFIDWorker) this.workers[IdThread.GetId()]).printTrace(errorCode, parameters, s1, s2);
+    	if (EC.TLC_INVARIANT_VIOLATED_BEHAVIOR == errorCode)
+    	{
+			((DFIDWorker) this.workers[IdThread.GetId()]).printInvariantTrace(errorCode, parameters, s1, s2);
+    	}
+    	else
+    	{
+			((DFIDWorker) this.workers[IdThread.GetId()]).printErrorTrace(errorCode, parameters, s1, s2);
+    	}
     }
 
     /**
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDWorker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDWorker.java
index 5b7c1be71c2456ca20759703284d5713b6e8bb7c..81072c15680349f02a8a6cc25fc10126d6074645 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDWorker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/DFIDWorker.java
@@ -122,23 +122,53 @@ public class DFIDWorker extends IdThread implements IWorker {
    * @param s1 
    * @param s2
    */
-  public final void printTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2) 
+  public final void printErrorTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2) 
   {
       MP.printError(errorCode, parameters);
       MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
       int idx = 0;
       while (idx < this.curLevel) 
       {
-          StatePrinter.printState(this.stateStack[idx], ++idx);
+          StatePrinter.printRuntimeErrorStateTraceState(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);
+      StatePrinter.printRuntimeErrorStateTraceState(s1, ++idx);
       if (s2 != null) 
       {
-          StatePrinter.printState(s2, idx+1);
+          StatePrinter.printRuntimeErrorStateTraceState(s2, idx+1);
       }
   }
+  
+  /**
+   * Prints the state trace when an invariant fails.
+   * @param errorCode Error code identifying type of failure.
+   * @param parameters Error detail.
+   * @param s1 Predecessor state to state which fails to satisfy invariant.
+   * @param s2 State which fails to satisfy invariant.
+   */
+  public final void printInvariantTrace(int errorCode, String[] parameters, TLCState s1, TLCState s2)
+  {
+      MP.printError(errorCode, parameters);
+      MP.printError(EC.TLC_BEHAVIOR_UP_TO_THIS_POINT);
+      int idx = 0;
+      for (; idx <= this.curLevel; idx++)
+      {
+    	  if (this.curLevel == idx)
+    	  {
+    		  assert s1.equals(this.stateStack[idx]);
+    	  }
+    	  
+    	  final int ordinal = idx + 1;
+    	  final TLCStateInfo currentState = new TLCStateInfo(this.stateStack[idx], ordinal);
+    	  final TLCState previousState = 0 == idx ? null : this.stateStack[idx-1];
+    	  StatePrinter.printInvariantViolationStateTraceState(currentState, previousState, ordinal);
+      }
+      
+      final int ordinal = idx + 1;
+	  final TLCStateInfo currentState = new TLCStateInfo(s2, ordinal);
+	  StatePrinter.printInvariantViolationStateTraceState(currentState, s1, ordinal);
+  }
 
   /* This method does a depth-first search up to the depth of toLevel. */
   public final void run() {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/EvalException.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/EvalException.java
index c8bdfe0e3a87beab4126642a486ca1a8299c0aa8..11c64865172b6e245edd54997794f451e91698d5 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/EvalException.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/EvalException.java
@@ -21,28 +21,38 @@ public class EvalException extends RuntimeException
 
 
     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 = errorCode;
+    	this(errorCode, new String[] {parameter});
     }
 
     public EvalException(int errorCode)
     {
         super(MP.getMessage(errorCode));
-		this.errorCode = 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/org.lamport.tlatools/src/tlc2/tool/INextStateFunctor.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/INextStateFunctor.java
index 4412d89f06590b411d1fda2be78a2ea8abda8e99..f8548c5375101eb6641f57032e7bfa9aca0a4607 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/INextStateFunctor.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/INextStateFunctor.java
@@ -32,4 +32,8 @@ public interface INextStateFunctor extends IStateFunctor {
 	public static class InvariantViolatedException extends RuntimeException {
 		
 	}
+
+	default boolean hasStates() {
+		throw new UnsupportedOperationException();
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/ITool.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/ITool.java
index 79b93dcd76af14e745afd58e90414eff74625ea2..08e626c80e2fd25973855a3e25e0e5ef86d2b973 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/ITool.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/ITool.java
@@ -37,6 +37,7 @@ import tla2sany.semantic.SemanticNode;
 import tla2sany.semantic.SymbolNode;
 import tlc2.tool.coverage.CostModel;
 import tlc2.tool.impl.ModelConfig;
+import tlc2.tool.impl.SpecProcessor;
 import tlc2.util.Context;
 import tlc2.util.ObjLongTable;
 import tlc2.util.Vect;
@@ -162,6 +163,8 @@ public interface ITool extends TraceApp {
 
 	boolean[] getAssumptionIsAxiom();
 
+	int checkAssumptions();
+
 	String[] getInvNames();
 
 	String[] getImpliedActNames();
@@ -237,10 +240,20 @@ public interface ITool extends TraceApp {
 
 	SemanticNode getViewSpec();
 
+	SemanticNode getPostConditionSpec();
+
 	int getId();
 
 	List<File> getModuleFiles(FilenameToStream resolver);
 
 	ModelConfig getModelConfig();
 
+	SpecProcessor getSpecProcessor();
+
+	ExprNode[] getActionConstraints();
+
+	ExprNode[] getModelConstraints();
+
+	TLCState evalAlias(TLCState curState, TLCState sucState);
+
 }
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/ModelChecker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/ModelChecker.java
index 22f3c315b00a83c8092bb79d721d01b4cef390f7..4280e1f027d0b06cd56132e075c79d93e0eef46f 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/ModelChecker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/ModelChecker.java
@@ -14,7 +14,6 @@ import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.stream.Collectors;
 
-import tla2sany.semantic.ExprNode;
 import tla2sany.semantic.OpDeclNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
@@ -283,6 +282,10 @@ public class ModelChecker extends AbstractChecker
                 } 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);
@@ -324,27 +327,7 @@ public class ModelChecker extends AbstractChecker
      */
     public int checkAssumptions()
     {
-        ExprNode[] assumps = this.tool.getAssumptions();
-        boolean[] isAxiom = this.tool.getAssumptionIsAxiom();
-        int assumptionsError = EC.NO_ERROR;
-        for (int i = 0; i < assumps.length; i++)
-        {
-            try
-            {
-                if ((!isAxiom[i]) && !this.tool.isValid(assumps[i]))
-                {
-                    OutputCollector.addViolatedAssumption(assumps[i]);
-                    assumptionsError = MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
-                }
-            } catch (Exception e)
-            {
-                // Assert.printStack(e);
-                OutputCollector.addViolatedAssumption(assumps[i]);
-                assumptionsError = MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
-                        new String[] { assumps[i].toString(), e.getMessage() });
-            }
-        }
-        return assumptionsError;
+    	return this.tool.checkAssumptions();
     }
 
     /**
@@ -639,6 +622,9 @@ public class ModelChecker extends AbstractChecker
 			} else if (e instanceof AssertionError)
 			{
 				ec = EC.TLC_BUG;
+			} else if (e instanceof EvalException)
+			{
+				ec = ((EvalException) e).getErrorCode();
 			} else
 			{
 				ec = EC.GENERAL;
@@ -648,7 +634,15 @@ public class ModelChecker extends AbstractChecker
 		    {
 				if (!(ec == EC.GENERAL && e.getMessage() == null))
 		        {
-					MP.printError(ec, e);
+					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();
@@ -831,6 +825,10 @@ public class ModelChecker extends AbstractChecker
     		FileUtil.deleteDir(this.metadir, success);
     	}
 	}
+    
+    public final void printSummary(boolean success) throws IOException {
+    	printSummary(success, startTime);
+    }
 
     public final void printSummary(boolean success, final long startTime) throws IOException
     {
@@ -848,9 +846,11 @@ public class ModelChecker extends AbstractChecker
 
         MP.printMessage(EC.TLC_STATS, new String[] { String.valueOf(getStatesGenerated()),
                 String.valueOf(this.theFPSet.size()), String.valueOf(this.theStateQueue.size()) });
+        // The depth used to only be reported on success, but this seems bogus since TLC reports
+        // the number states above.
+        MP.printMessage(EC.TLC_SEARCH_DEPTH, String.valueOf(this.trace.getLevelForReporting()));
         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");
@@ -995,6 +995,10 @@ public class ModelChecker extends AbstractChecker
 		return useByteArrayQueue() ? "DiskByteArrayQueue" : "DiskStateQueue";
 	}
 
+	/* (non-Javadoc)
+	 * @see tlc2.tool.AbstractChecker#getStatesGenerated()
+	 */
+	@Override
     public long getStatesGenerated() {
     	long sum = numberOfInitialStates;
     	for (final IWorker worker : workers) {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/SimulationWorker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/SimulationWorker.java
index 64a4940fcebd3df054ebb73628cf3758986bb43e..4e2ae69c893693a64c1cb67aede6bd4f38ebce0a 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/SimulationWorker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/SimulationWorker.java
@@ -28,6 +28,7 @@ package tlc2.tool;
 import java.io.PrintWriter;
 import java.util.Optional;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.LongAdder;
 
 import tlc2.output.EC;
@@ -59,28 +60,28 @@ import util.FileUtil;
 public class SimulationWorker extends IdThread {
 	
 	// This worker's local source of randomness.
-	final RandomGenerator localRng;
+	private final RandomGenerator localRng;
 
 	// The state currently being processed.
 	TLCState curState;
 
 	// Stores a trace generated by this worker.
-	StateVec stateTrace;
+	private final StateVec stateTrace;
 	
 	// The set of initial states for the spec. 
-	private final StateVec initStates;
+	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;
+	private long 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;
+	private final int maxTraceDepth;
 	
 	// Should this worker check traces for deadlock.
 	private final boolean checkDeadlock;
@@ -95,10 +96,14 @@ public class SimulationWorker extends IdThread {
 	// update it whenever it generates a new state or trace.
 	private final LongAdder numOfGenStates;
 	private final LongAdder numOfGenTraces;
+	private final AtomicLong welfordM2AndMean;
 
 	private final ITool tool;
 	private final ILiveCheck liveCheck;	
 	
+	// Adjacency Matrix with link weights.
+	final long[][] actionStats;
+	
 	/**
 	 * Encapsulates information about an error produced by a simulation worker.
 	 */
@@ -109,24 +114,27 @@ public class SimulationWorker extends IdThread {
 			this.errorCode = errorCode;
 			this.parameters = parameters;
 			this.state = state;
-			this.stateTrace = stateTrace;
+			// Take of copy of the worker's stateTrace because this worker will concurrently
+			// modify its stateTrace (by generating more behaviors) when Simulator iterates
+			// (its copy of) stateTrace to print this error.
+			this.stateTrace = new StateVec(stateTrace);
 			this.exception = e;
 		}
 		
 		// The error code to report.
-		public int errorCode;
+		public final int errorCode;
 
 		// Any additional information to be included in a reported error string.
-		public String[] parameters;
+		public final String[] parameters;
 
 		// The TLC state associated with the error.
-		public TLCState state;
+		public final TLCState state;
 
 		// The TLC trace associated with the error.
-		public StateVec stateTrace;
+		public final StateVec stateTrace;
 
 		// An exception associated with the error.
-		public Exception exception;
+		public final Exception exception;
 	}
 	
 	
@@ -180,22 +188,38 @@ public class SimulationWorker extends IdThread {
 
 	}
 	
+	public SimulationWorker(int id, ITool tool, BlockingQueue<SimulationWorkerResult> resultQueue,
+			long seed, int maxTraceDepth, long maxTraceNum, boolean checkDeadlock, String traceFile,
+			ILiveCheck liveCheck) {
+		this(id, tool, resultQueue, seed, maxTraceDepth, maxTraceNum, checkDeadlock, traceFile, liveCheck,
+				new LongAdder(), new LongAdder(), new AtomicLong());
+	}
 
-	public SimulationWorker(int id, ITool tool, StateVec initStates, BlockingQueue<SimulationWorkerResult> resultQueue,
-			long seed, long maxTraceDepth, long maxTraceNum, boolean checkDeadlock, String traceFile,
-			ILiveCheck liveCheck, LongAdder numOfGenStates, LongAdder numOfGenTraces) {
+	public SimulationWorker(int id, ITool tool, BlockingQueue<SimulationWorkerResult> resultQueue,
+			long seed, int maxTraceDepth, long maxTraceNum, boolean checkDeadlock, String traceFile,
+			ILiveCheck liveCheck, LongAdder numOfGenStates, LongAdder numOfGenTraces, AtomicLong m2AndMean) {
 		super(id);
 		this.localRng = new RandomGenerator(seed);
 		this.tool = tool;
 		this.maxTraceDepth = maxTraceDepth;
 		this.maxTraceNum = maxTraceNum;
 		this.resultQueue = resultQueue;
-		this.initStates = initStates;
 		this.checkDeadlock = checkDeadlock;
 		this.traceFile = traceFile;
 		this.liveCheck = liveCheck;
 		this.numOfGenStates = numOfGenStates;
 		this.numOfGenTraces = numOfGenTraces;
+		this.welfordM2AndMean = m2AndMean;
+		this.stateTrace = new StateVec(maxTraceDepth);
+		
+		if (Simulator.actionStats) {
+			final Action[] actions = this.tool.getActions();
+			final int len = actions.length;
+			this.actionStats = new long[len][len];
+		} else {
+			// Write all statistics into a single cell that we will ignore.
+			this.actionStats = new long[1][1];
+		}
 	}
 	
 	/**
@@ -221,6 +245,10 @@ public class SimulationWorker extends IdThread {
 				if (res.isPresent()) {
 					final SimulationWorkerError err = res.get();
 					resultQueue.put(SimulationWorkerResult.Error(this.myGetId(), err));
+					// One would assume to return from this branch to stop the worker from creating
+					// additional behaviors. However, this is at the discretion of Simulator, which
+					// checks if the user ran simulation with "-continue".  If not, Simulator
+					// will signal termination asynchronously.
 				}
 
 				// Abide by the maximum trace generation count.
@@ -249,30 +277,11 @@ public class SimulationWorker extends IdThread {
 		}
 	}
 	
-	/**
-	 * 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 {
+	private final TLCState randomState(RandomGenerator rng, StateVec states) {
 		final int len = states.size();
 		if (len > 0) {
 			final int index = (int) Math.floor(rng.nextDouble() * len);
@@ -296,8 +305,11 @@ public class SimulationWorker extends IdThread {
 	 *
 	 */
 	private Optional<SimulationWorkerError> simulateRandomTrace() throws Exception {
-		this.curState = null;
-		this.stateTrace = new StateVec((int) maxTraceDepth);
+		// TODO With the introduction of TLCStateMutSimulation, stateTrace could be
+		// eliminated and the predecessor state stored as a reference in its successor
+		// (linked list of states). It would also be advantageous if "-depth N" is >>
+		// than the actual average trace length, in which case we allocate a way too
+		// large stateTrace.
 		stateTrace.clear();
 
 		// a) Randomly select a state from the set of init states.
@@ -305,6 +317,9 @@ public class SimulationWorker extends IdThread {
 		setCurrentState(curState);
 		
 		boolean inConstraints = tool.isInModel(curState);
+		
+		final Action[] actions = this.tool.getActions();
+		final int len = actions.length;
 
 		// Simulate a trace up to the maximum specified length.
 		for (int traceIdx = 0; traceIdx < maxTraceDepth; traceIdx++) {
@@ -322,8 +337,17 @@ public class SimulationWorker extends IdThread {
 			}
 
 			// b) Get the current state's successor states.
-			final StateVec nextStates = randomNextStates(this.localRng, curState);
-			if (nextStates == null) {
+			StateVec nextStates = null;
+			int index = (int) Math.floor(this.localRng.nextDouble() * len);
+			final int p = this.localRng.nextPrime();
+			for (int i = 0; i < len; i++) {
+				nextStates = this.tool.getNextStates(actions[index], curState);
+				if (!nextStates.empty()) {
+					break;
+				}
+				index = (index + p) % len;
+			}
+			if (nextStates == null || nextStates.empty()) {
 				if (checkDeadlock) {
 					// We get here because of deadlock.
 					return Optional.of(new SimulationWorkerError(EC.TLC_DEADLOCK_REACHED, null, curState, stateTrace, null));
@@ -335,6 +359,10 @@ public class SimulationWorker extends IdThread {
 			for (int i = 0; i < nextStates.size(); i++) {
 				numOfGenStates.increment();
 				final TLCState state = nextStates.elementAt(i);
+				// Any check below may terminate simulation, which then makes state the final
+				// state in the trace. To correctly print its state number, it needs to know its
+				// predecessor.
+				state.setPredecessor(curState);
 
 				if (!tool.isGoodState(state)) {
 					return Optional.of(new SimulationWorkerError(EC.TLC_STATE_NOT_COMPLETELY_SPECIFIED_NEXT, null, state,
@@ -347,11 +375,13 @@ public class SimulationWorker extends IdThread {
 					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.
+							state.setActionId(index);
 							return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_VIOLATED_BEHAVIOR,
 									new String[] { tool.getInvNames()[idx] }, state, stateTrace, null));
 						}
 					}
 				} catch (final Exception e) {
+					state.setActionId(index);
 					return Optional.of(new SimulationWorkerError(EC.TLC_INVARIANT_EVALUATION_FAILED,
 							new String[] { tool.getInvNames()[idx], e.getMessage() }, state, stateTrace, null));
 				}
@@ -361,11 +391,13 @@ public class SimulationWorker extends IdThread {
 					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.
+							state.setActionId(index);
 							return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_VIOLATED_BEHAVIOR,
 									new String[] { tool.getImpliedActNames()[idx] }, state, stateTrace, null));
 						}
 					}
 				} catch (final Exception e) {
+					state.setActionId(index);
 					return Optional.of(new SimulationWorkerError(EC.TLC_ACTION_PROPERTY_EVALUATION_FAILED,
 							new String[] { tool.getImpliedActNames()[idx], e.getMessage() }, state, stateTrace, null));
 				}
@@ -379,7 +411,18 @@ public class SimulationWorker extends IdThread {
 			// iteration of the loop.
 			final TLCState s1 = randomState(localRng, nextStates);
 			inConstraints = (tool.isInModel(s1) && tool.isInActions(curState, s1));
-			s1.setPredecessor(curState);
+			s1.setPredecessor(curState); // Should be redundant but let's be safe anyway.
+			s1.setActionId(index);
+			
+			// Execute callable on the state that was selected from the set of succesor states.
+			s1.execCallable();
+			//System.out.printf("%s\n", tool.evalAlias(curState, s1));
+			
+			// In case actionStats are off, we waste a few cycles to increment this counter
+			// nobody is going to look at.
+			if (Simulator.actionStats) {
+				this.actionStats[curState.getActionId()][s1.getActionId()]++;
+			}
 			curState = s1;
 			setCurrentState(curState);
 		}
@@ -390,7 +433,18 @@ public class SimulationWorker extends IdThread {
 		// Check if the current trace satisfies liveness properties.
 		liveCheck.checkTrace(tool, stateTrace);
 		
-
+		welfordM2AndMean.accumulateAndGet(stateTrace.size(), (acc, tl) -> {
+			// Welford's online algorithm (m2 and mean stuffed into high and low of the
+			// atomiclong because update concurrently by multiple workers).
+			// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
+			int mean = (int) (acc & 0x00000000FFFFFFFFL);
+			long m2 = acc >>> 32;
+			final long delta = tl - mean;
+			mean += delta / (numOfGenTraces.longValue() + 1); //+1 prevent div-by-zero
+			m2 += delta * (tl - mean);
+			return m2 << 32 | (mean & 0xFFFFFFFFL);
+		});
+		
 		// 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) {
@@ -410,4 +464,21 @@ public class SimulationWorker extends IdThread {
 		// Finished trace generation without any errors.
 		return Optional.empty();
 	}
+	
+	public final long getTraceCnt() {
+		return this.traceCnt + 1; // +1 to account the currently generated behavior. 
+	}
+	
+	public final StateVec getTrace() {
+		return stateTrace;
+	}
+
+	public void start(StateVec initStates) {
+		this.initStates = initStates;
+		this.start();
+	}
+	
+	public final RandomGenerator getRNG() {
+		return this.localRng;
+	}
 }
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/Simulator.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/Simulator.java
index cd1dfa77bb227c374db35849ded75760640fcb49..909be42ea91717505163c88c2fdce7b1a3d00668 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/Simulator.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/Simulator.java
@@ -6,15 +6,23 @@
 package tlc2.tool;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TimerTask;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.LongAdder;
+import java.util.stream.Collectors;
 
+import tla2sany.semantic.ExprNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
@@ -23,11 +31,13 @@ import tlc2.tool.SimulationWorker.SimulationWorkerError;
 import tlc2.tool.SimulationWorker.SimulationWorkerResult;
 import tlc2.tool.coverage.CostModelCreator;
 import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
 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.DotActionWriter;
 import tlc2.util.RandomGenerator;
 import tlc2.util.statistics.DummyBucketStatistics;
 import tlc2.value.IValue;
@@ -39,6 +49,7 @@ public class Simulator {
 
 	public static boolean EXPERIMENTAL_LIVENESS_SIMULATION = Boolean
 			.getBoolean(Simulator.class.getName() + ".experimentalLiveness");
+	public static boolean actionStats = Boolean.getBoolean(tlc2.tool.Simulator.class.getName() + ".actionStats");
 
 	/* Constructors */
 
@@ -54,7 +65,7 @@ public class Simulator {
 		// SZ Mar 5, 2009: removed it again because of the bug in simulator
 		// ToolIO.setUserDir(specDir);
 
-		this.tool = new FastTool(specDir, specFile, configFile, resolver);
+		this.tool = new FastTool(specDir, specFile, configFile, resolver, Tool.Mode.Simulation);
 
 		this.checkDeadlock = deadlock && tool.getModelConfig().getCheckDeadlock();
 		this.checkLiveness = !this.tool.livenessIsTrue();
@@ -65,15 +76,13 @@ public class Simulator {
 			this.traceDepth = traceDepth;
 		} else {
 			// this.actionTrace = new Action[0]; // SZ: never read locally
-			this.traceDepth = Long.MAX_VALUE;
+			this.traceDepth = Integer.MAX_VALUE;
 		}
 		this.traceFile = traceFile;
 		this.traceNum = traceNum;
 		this.rng = rng;
 		this.seed = seed;
 		this.aril = 0;
-		this.numWorkers = numWorkers;
-		this.workers = new ArrayList<>(numWorkers);
 		// Initialization for liveness checking
 		if (this.checkLiveness) {
 			if (EXPERIMENTAL_LIVENESS_SIMULATION) {
@@ -86,6 +95,14 @@ public class Simulator {
 			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, this.welfordM2AndMean));
+		}
+		
 		if (TLCGlobals.isCoverageEnabled()) {
         	CostModelCreator.create(this.tool);
         }
@@ -110,12 +127,13 @@ public class Simulator {
 	// concurrently, so we use a LongAdder to reduce potential contention.
 	private final LongAdder numOfGenStates = new LongAdder();
 	private final LongAdder numOfGenTraces = new LongAdder();
+	private final AtomicLong welfordM2AndMean = new AtomicLong();
 
 	// private Action[] actionTrace; // SZ: never read locally
 	private final String traceFile;
 
 	// The maximum length of a simulated trace.
-	private final long traceDepth;
+	private final int traceDepth;
 
 	// The maximum number of total traces to generate.
 	private final long traceNum;
@@ -126,16 +144,9 @@ public class Simulator {
 	private final RandomGenerator rng;
 	private final long seed;
 	private long aril;
-	private IValue[] localValues = new IValue[4];
-
-	// The set of all initial states for the given spec. This should be only be
-	// computed once and re-used whenever a new random trace is generated. This
-	// variable should not be written to concurrently, but is allowed to be read
-	// concurrently.
-	private StateVec initStates = new StateVec(0);
 
 	// Each simulation worker pushes their results onto this shared queue.
-	private BlockingQueue<SimulationWorkerResult> workerResultQueue = new LinkedBlockingQueue<>();
+	private final BlockingQueue<SimulationWorkerResult> workerResultQueue = new LinkedBlockingQueue<>();
 	
     /**
      * Timestamp of when simulation started.
@@ -174,26 +185,32 @@ public class Simulator {
    * @return an error code, or <code>EC.NO_ERROR</code> on success
 	 */
 	public int simulate() throws Exception {
+		final int res = this.tool.checkAssumptions();
+		if (res != EC.NO_ERROR) {
+			return res;
+		}
+		
 		TLCState curState = null;
 
+		// 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.
-			this.initStates = this.tool.getInitStates();
 
 			// This counter should always be initialized at zero.
 			assert (this.numOfGenStates.longValue() == 0);
-			this.numOfGenStates.add(this.initStates.size());
+			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 < this.initStates.size(); i++) {
-				curState = this.initStates.elementAt(i);
+			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)) {
@@ -225,7 +242,7 @@ public class Simulator {
 
 		// It appears deepNormalize brings the states into a canonical form to
 		// speed up equality checks.
-		this.initStates.deepNormalize();
+		initStates.deepNormalize();
 
 		//
 		// Start progress report thread.
@@ -240,13 +257,9 @@ public class Simulator {
 		
 		// Start up multiple simulation worker threads, each with their own unique seed.
 		final Set<Integer> runningWorkers = new HashSet<>();
-		for (int i = 0; i < this.numWorkers; i++) {			
-			final SimulationWorker worker = new SimulationWorker(i, this.tool, initStates, this.workerResultQueue,
-					this.rng.nextLong(), this.traceDepth, this.traceNum, this.checkDeadlock, this.traceFile,
-					this.liveCheck, this.numOfGenStates, this.numOfGenTraces);		
-
-			worker.start();
-			workers.add(worker);
+		for (int i = 0; i < this.workers.size(); i++) {
+			SimulationWorker worker = workers.get(i);
+			worker.start(initStates);
 			runningWorkers.add(i);
 		}
 
@@ -317,6 +330,19 @@ public class Simulator {
 		
 		// Shut down all workers.
 		this.shutdownAndJoinWorkers(workers);
+		
+		if (errorCode == EC.NO_ERROR) {
+			// see tlc2.tool.Worker.doPostCheckAssumption()
+			final ExprNode sn = (ExprNode) this.tool.getPostConditionSpec();
+			try {
+				if (sn != null && !this.tool.isValid(sn)) {
+					MP.printError(EC.TLC_ASSUMPTION_FALSE, sn.toString());
+				}
+			} catch (Exception e) {
+				// tool.isValid(sn) failed to evaluate...
+				MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, new String[] { sn.toString(), e.getMessage() });
+			}
+		}
 
 		// Do a final progress report.
 		report.isRunning = false;
@@ -329,13 +355,12 @@ public class Simulator {
 		return errorCode;
 	}
 
-
-	public final void printBehavior(final TLCRuntimeException exception, final TLCState state, final StateVec stateTrace) {
+	private final void printBehavior(final TLCRuntimeException exception, final TLCState state, final StateVec stateTrace) {
 		MP.printTLCRuntimeException(exception);
 		printBehavior(state, stateTrace);
 	}
 
-	public final void printBehavior(SimulationWorkerError error) {
+	private final void printBehavior(SimulationWorkerError error) {
 		printBehavior(error.errorCode, error.parameters, error.state, error.stateTrace);
 	}
 
@@ -343,16 +368,16 @@ public class Simulator {
 	 * 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(final int errorCode, final String[] parameters, final TLCState state, final StateVec stateTrace) {
+	private 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) {
+	private final void printBehavior(final TLCState state, final StateVec stateTrace) {
 		if (this.traceDepth == Long.MAX_VALUE) {
 			MP.printMessage(EC.TLC_ERROR_STATE);
-			StatePrinter.printState(state);
+			StatePrinter.printStandaloneErrorState(state);
 		} else {
 			if (!stateTrace.isLastElement(state)) {
 				// MAK 09/24/2019: this method is called with state being the stateTrace's
@@ -367,13 +392,24 @@ public class Simulator {
 			// especially useful for Error-Trace Explorer in the Toolbox.
 			TLCState lastState = null;
 			TLCStateInfo sinfo;
-			int cnt = 1;
+			int omitted = 0;
 			for (int i = 0; i < stateTrace.size(); i++) {
-				final TLCState curState = stateTrace.elementAt(i);
+				final TLCStateMutSimulation curState = (TLCStateMutSimulation) stateTrace.elementAt(i);
+				// Last state's successor is itself.
+				final TLCState sucState = stateTrace.elementAt(Math.min(i + 1, stateTrace.size() - 1));
 				if (lastState != null) {
-					sinfo = this.tool.getState(curState, lastState);
+					// Contrary to BFS/ModelChecker, simulation remembers the action (its id) during
+					// trace exploration to print the error-trace without re-evaluating the
+					// next-state relation for lastStates -> cusState (tool.getState(curState,
+					// lastState)) to determine the action.  This would fail for specs whose next-state
+					// relation is probabilistic (ie. TLC!RandomElement or Randomization.tla). In other
+					// words, tool.getState(curState,lastState) would return for some pairs of states.
+					sinfo = new TLCStateInfo(curState, tool.getActions()[curState.getActionId()].getLocation());
 				} else {
 					sinfo = new TLCStateInfo(curState, "<Initial predicate>");
+					StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(sinfo, sucState), lastState, curState.getLevel());
+					lastState = curState;
+					continue;
 				}
 				
 				// MAK 09/25/2019: It is possible for
@@ -381,46 +417,55 @@ public class Simulator {
 				// *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 has finite (sub-)sequence of stuttering steps.
+				// The reason is that simulateRandomTrace, with non-zero probability, selects
+				// a stuttering step as the current state's successor. Guarding against it
+				// would require to fingerprint states (i.e. check equality) for each selected
+				// successor state (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++);
+				// better readable - trace with only infinite stuttering at the end. This
+				// takes mostly care of the confusing Toolbox behavior where a trace with
+				// finite stuttering is silently reduced by breadth-first-search when trace
+				// expressions are evaluated (see https://github.com/tlaplus/tlaplus/issues/400#issuecomment-650418597).
+				if (TLCGlobals.printDiffsOnly && curState.fingerPrint() == lastState.fingerPrint()) {
+					omitted++;
 				} else {
-					assert Boolean.TRUE;
+					// print the state's actual level and not a monotonically increasing state
+					// number => Numbering will have gaps with difftrace.
+					StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(sinfo, sucState), lastState, curState.getLevel());
 				}
 				lastState = curState;
 			}
+			if (omitted > 0) {
+				assert TLCGlobals.printDiffsOnly;
+				MP.printMessage(EC.GENERAL, String.format(
+						"difftrace requested: Shortened behavior by omitting finite stuttering (%s states), which is an artifact of simulation mode.\n",
+						omitted));
+			}
 		}
 	}
 
 	public IValue getLocalValue(int idx) {
-		if (idx < this.localValues.length) {
-			return this.localValues[idx];
+		for (SimulationWorker w : workers) {
+			return w.getLocalValue(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;
+	public void setAllValues(int idx, IValue val) {
+		for (SimulationWorker w : workers) {
+			w.setLocalValue(idx, val);
 		}
-		this.localValues[idx] = val;
+	}
+
+	public List<IValue> getAllValues(int idx) {
+		return workers.stream().map(w -> w.getLocalValue(idx)).collect(Collectors.toList());
 	}
 
 	/**
 	 * Prints the summary
 	 */
-	public final void printSummary() {
+	private final void printSummary() {
 		this.reportCoverage();
 
 		/*
@@ -444,6 +489,10 @@ public class Simulator {
 		}
 	}
 
+	public final ITool getTool() {
+	    return this.tool;	
+	}
+	
 	/**
 	 * Reports progress information
 	 */
@@ -458,19 +507,101 @@ public class Simulator {
 					synchronized (this) {
 						this.wait(TLCGlobals.progressInterval);
 					}
-					MP.printMessage(EC.TLC_PROGRESS_SIMU, String.valueOf(numOfGenStates.longValue()));
+					final long genTrace = numOfGenTraces.longValue();
+					final long m2AndMean = welfordM2AndMean.get();
+					final long mean = m2AndMean & 0x00000000FFFFFFFFL; // could be int.
+					final long m2 = m2AndMean >>> 32;
+					MP.printMessage(EC.TLC_PROGRESS_SIMU, 
+							String.valueOf(numOfGenStates.longValue()),
+							String.valueOf(genTrace),
+							String.valueOf(mean),
+							String.valueOf(Math.round(m2 / (genTrace + 1d))), // Var(X),  +1 to prevent div-by-zero.
+							String.valueOf(Math.round(Math.sqrt(m2 / (genTrace + 1d))))); // SD, +1 to prevent div-by-zero.
 					if (count > 1) {
 						count--;
 					} else {
 						reportCoverage();
 						count = TLCGlobals.coverageInterval / TLCGlobals.progressInterval;
 					}
+					
+					writeActionFlowGraph();
 				}
 			} catch (Exception e) {
 				// SZ Jul 10, 2009: changed from error to bug
 				MP.printTLCBug(EC.TLC_REPORTER_DIED, null);
 			}
 		}
+
+		private void writeActionFlowGraph() throws IOException {
+			if (!actionStats) {
+				return;
+			}
+			// The number of actions is expected to be low (dozens commons and hundreds a
+			// rare). This is why the code below isn't optimized for performance.
+			final Action[] actions = Simulator.this.tool.getActions();
+			final int len = actions.length;
+			
+			// Clusters of actions that have the same context:
+			// CONSTANT Proc
+			// ...
+			// A(p) == p \in {...} /\ v' = 42...
+			// Next == \E p \in Proc : A(p)
+			final Map<String, Set<Integer>> clusters = new HashMap<>();
+			for (int i = 0; i < len; i++) {
+				final String con = actions[i].con.toString();
+				if (!clusters.containsKey(con)) {
+				   clusters.put(con, new HashSet<>());	
+				}
+				clusters.get(con).add(i);
+			}
+			
+			// Write clusters to dot file (override previous file).
+			final DotActionWriter dotActionWriter = new DotActionWriter(
+					Simulator.this.tool.getRootName() + "_actions.dot", "");
+			for (Entry<String, Set<Integer>> cluster : clusters.entrySet()) {
+				// key is a unique set of chars accepted/valid as a graphviz cluster id.
+				final String key = Integer.toString(Math.abs(cluster.getKey().hashCode()));
+				dotActionWriter.writeSubGraphStart(key, cluster.getKey().toString());
+
+				final Set<Integer> ids = cluster.getValue();
+				for (Integer id : ids) {
+					dotActionWriter.write(actions[id], id);
+				}
+				dotActionWriter.writeSubGraphEnd();
+			}					
+
+			// Element-wise sum the statistics from all workers.
+			long[][] aggregateActionStats = new long[len][len];
+			final List<SimulationWorker> workers = Simulator.this.workers;
+			for (SimulationWorker sw : workers) {
+				final long[][] s = sw.actionStats;
+				for (int i = 0; i < len; i++) {
+					for (int j = 0; j < len; j++) {
+						aggregateActionStats[i][j] += s[i][j];
+					}
+				}
+			}
+			
+			// Write stats to dot file as edges between the action vertices.
+			for (int i = 0; i < len; i++) {
+				for (int j = 0; j < len; j++) {
+					long l = aggregateActionStats[i][j];
+					if (l > 0L) {
+						// LogLog l (to keep the graph readable) and round to two decimal places (to not
+						// write a gazillion decimal places truncated by graphviz anyway).
+						final double loglogWeight = Math.log10(Math.log10(l+1)); // +1 to prevent negative inf.
+						dotActionWriter.write(actions[i], i, actions[j], j,
+								BigDecimal.valueOf(loglogWeight).setScale(2, RoundingMode.HALF_UP)
+										.doubleValue());
+					} else {
+						dotActionWriter.write(actions[i], i, actions[j], j);
+					}
+				}
+			}
+			
+			// Close dot file.
+			dotActionWriter.close();
+		}
 	}
 
 	public void stop() {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/StateVec.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/StateVec.java
index b35d9b534226806a9ffa5e452b143b2b15b544f6..6a56dc703b59cec8766b9e643a45c4d298497047 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/StateVec.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/StateVec.java
@@ -5,8 +5,12 @@
 
 package tlc2.tool;
 
+import java.util.LinkedList;
+
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.Value;
 import util.Assert;
 
 /*
@@ -37,6 +41,14 @@ public final class StateVec implements IStateFunctor, INextStateFunctor {
     }
   }
 
+  public StateVec(final StateVec other) {
+	this(other.size);
+	this.size = other.size;
+	for (int i = 0; i < v.length; i++) {
+		this.v[i] = other.elementAt(i);
+	}
+  }
+  
   private StateVec(TLCState v[]) {
     this.v = v;
     this.size = v.length;
@@ -178,4 +190,29 @@ public final class StateVec implements IStateFunctor, INextStateFunctor {
 	}
 	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()]);
+  }
+  
+  public final boolean hasStates() {
+	  return !isEmpty();
+  }
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLAPlusExecutor.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLAPlusExecutor.java
index 8647dde9374e6471230b1e9ead6d450ab269505f..70745fb089aad4124828dc313771b90191a5e5fd 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLAPlusExecutor.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLAPlusExecutor.java
@@ -31,8 +31,10 @@ import java.util.concurrent.locks.ReentrantLock;
 
 import tla2sany.semantic.SymbolNode;
 import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
 import tlc2.util.Context;
 import tlc2.value.IValue;
+import tlc2.value.impl.Value;
 import util.SimpleFilenameToStream;
 
 public class TLAPlusExecutor {
@@ -75,7 +77,7 @@ public class TLAPlusExecutor {
 	private TLCState state;
 
 	public TLAPlusExecutor(String spec, String config) {
-		this.tool = new FastTool(spec, config, new SimpleFilenameToStream());
+		this.tool = new FastTool(spec, config, new SimpleFilenameToStream(), Tool.Mode.Executor);
 
 		// Initialize the TLA+ executor by generating the initial state.
 		this.state = this.tool.getInitStates().first();
@@ -125,4 +127,8 @@ public class TLAPlusExecutor {
 	public ReentrantLock getLock() {
 		return lock;
 	}
+	
+	public Value getConstant(final String string) {
+		return (Value) this.tool.getSpecProcessor().getDefns().get(string);
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCState.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCState.java
index 24477f02a55ce43b92659829da819af2c93adfc6..77b37ebae42946dc17f1cee514184ca33bf51370 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCState.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCState.java
@@ -96,8 +96,19 @@ public abstract class TLCState implements Cloneable, Serializable {
   public final OpDeclNode[] getVars() {
 	  return vars;
   }
+  
+  public final String[] getVarsAsStrings() {
+	  String[] res = new String[vars.length];
+	  for (int i = 0; i < vars.length; i++) {
+		res[i] = vars[i].getName().toString();
+	  }
+	  return res;
+  }
 
   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());
 	  }
@@ -124,4 +135,14 @@ public abstract class TLCState implements Cloneable, Serializable {
   public void setCallable(Callable<?> cl) {
 	  // no-op - see TLAPlusExecutorState
   }
+
+	public int getActionId() {
+		  // no-op - see TLCStateMutSimulation
+		return 0;
+	}
+
+	public void setActionId(int actionId) {
+		  // no-op - see TLCStateMutSimulation
+	}
+
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateInfo.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateInfo.java
index b2dc8216438a6335a7de146f98cc30acf389dff7..505a4c272539041702b07d910566b6be7d0ac2ba 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateInfo.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateInfo.java
@@ -8,7 +8,7 @@ package tlc2.tool;
 public class TLCStateInfo {
   public TLCStateInfo predecessorState;
   public long stateNumber;
-  public TLCState state;
+  public final TLCState state;
   public Object info;
   public Long fp;
 
@@ -23,12 +23,24 @@ public class TLCStateInfo {
     this.state = s;
     this.info = info;
   }
+  
+  public TLCStateInfo(TLCState state, int stateOrdinal) {
+	  this.state = state;
+	  this.stateNumber = stateOrdinal;
+	  this.info = "";
+  }
 
   public TLCStateInfo(TLCState s, String info, int stateNum, long fp) {
 	  this(s, info);
 	  stateNumber = stateNum;
 	  this.fp = fp;
   }
+  
+  public TLCStateInfo(TLCState s, TLCStateInfo info) {
+	  this(s, info.info);
+	  this.stateNumber = info.stateNumber;
+	  this.fp = info.fp;
+  }
 
   public final long fingerPrint() {
 	  if (fp == null) {
@@ -55,4 +67,8 @@ public class TLCStateInfo {
   public int hashCode() {
 	  return this.state.hashCode();
   }
+
+  public TLCState getOriginalState() {
+	return state;
+  }
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateMutSimulation.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateMutSimulation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f56df8113a94aef03b1daf0701027dff1568a134
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCStateMutSimulation.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 Mon 30 Apr 2007 at 15:30:01 PST by lamport
+//      modified on Wed Dec  5 23:18:37 PST 2001 by yuanyu
+
+package tlc2.tool;
+
+import java.io.IOException;
+import java.io.Serializable;
+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;
+
+/**
+ * Attention! Copy of TLCStateMut except for getter/setter and field action id.
+ */
+public final class TLCStateMutSimulation extends TLCState implements Cloneable, Serializable {
+  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;
+
+  private TLCStateMutSimulation(IValue[] vals) { this.values = vals; }
+  
+  public static void setVariables(OpDeclNode[] variables) 
+  {
+      vars = variables;
+      IValue[] vals = new IValue[vars.length];
+      Empty = new TLCStateMutSimulation(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 setTool(ITool tool) {
+    mytool = tool;
+    viewMap = tool.getViewSpec();
+    perms = tool.getSymmetryPerms();
+  }
+
+  public final TLCState createEmpty() {
+	  IValue[] vals = new IValue[vars.length];
+    return new TLCStateMutSimulation(vals);
+  }
+
+  //TODO equals without hashcode!
+  public final boolean equals(Object obj) {
+    if (obj instanceof TLCStateMutSimulation) {
+      TLCStateMutSimulation state = (TLCStateMutSimulation)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("TLCStateMut.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 TLCStateMutSimulation(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 TLCStateMutSimulation(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();
+			}
+			TLCStateMutSimulation state = this;
+			if (minVals != this.values) {
+				state = new TLCStateMutSimulation(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();
+    TLCStateMutSimulation lstate = (TLCStateMutSimulation)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();
+  }
+
+    //*********//
+
+	private int actionId;
+
+	public int getActionId() {
+		return actionId;
+	}
+
+	public void setActionId(int actionId) {
+		this.actionId = actionId;
+	}
+	
+    //*********//
+	
+	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;
+	}
+
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCTrace.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCTrace.java
index cb58807a3dcfd9bc1e4d9c35769cca695d9826be..95eb910efc0d7dd4b6b2affec5d17629df3c9cef 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCTrace.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TLCTrace.java
@@ -22,15 +22,20 @@ import tlc2.output.StatePrinter;
 import tlc2.util.BufferedRandomAccessFile;
 import tlc2.util.LongVec;
 import tlc2.value.RandomEnumerableValues;
+import tlc2.value.ValueOutputStream;
+import tlc2.value.impl.RecordValue;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.Value;
+import util.Assert;
 import util.FileUtil;
 
 public class TLCTrace {
 
 	static final String EXT = ".st";
-	private static String filename;
+	protected static String filename;
 	private final BufferedRandomAccessFile raf;
 	private long lastPtr;
-	private TraceApp tool;
+	protected TraceApp tool;
 
 	public TLCTrace(String metadir, String specFile, TraceApp tool) throws IOException {
 		filename = metadir + FileUtil.separator + specFile + EXT;
@@ -302,7 +307,7 @@ public class TLCTrace {
 		if (len > 0) {
 			if (sinfo == null) {
 				// Recreate initial state from its fingerprint.
-				final long fp = fps.elementAt(fps.size() - 1);
+				final long fp = fps.elementAt(len - 1);
 				sinfo = this.tool.getState(fp);
 			}
 			// Recover successor states from its predecessor and its fingerprint.
@@ -350,13 +355,21 @@ public class TLCTrace {
 			// 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);
-			TLCStateInfo s1Info = new TLCStateInfo(s1);
-			StatePrinter.printState(s1Info);
-			trace.add(s1Info);
-			if (s2 != null) {
+			if (s2 == null) {
+				TLCStateInfo s1Info = new TLCStateInfo(s1);
+			    StatePrinter.printInvariantViolationStateTraceState(new TLCStateInfo(s1));
+				trace.add(s1Info);
+			} else {
+				// Print initial state
+				TLCStateInfo s1Info = this.tool.evalAlias(new TLCStateInfo(s1), s2);
+				StatePrinter.printInvariantViolationStateTraceState(s1Info, s1, 1);
+				
 				// Create TLCStateInfo instance to include corresponding action in output.
-				TLCStateInfo s2Info = this.tool.getState(s2, s1);
-				StatePrinter.printState(s2Info, s1, 2);
+				TLCStateInfo state = this.tool.getState(s2, s1);
+				
+				// Print successor state.
+				TLCStateInfo s2Info = this.tool.evalAlias(state, s2);
+				StatePrinter.printInvariantViolationStateTraceState(s2Info, s1, 2);
 				trace.add(s2Info);
 			}
 			OutputCollector.setTrace(trace);
@@ -364,11 +377,12 @@ public class TLCTrace {
 		}
 
 		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);
+		while (idx < prefix.length - 1) {
+			StatePrinter.printInvariantViolationStateTraceState(this.tool.evalAlias(prefix[idx], prefix[idx + 1].state), lastState, idx + 1);
 			lastState = prefix[idx].state;
 			trace.add(prefix[idx]);
 			idx++;
@@ -387,19 +401,22 @@ public class TLCTrace {
 				System.exit(1);
 			}
 		} else {
-			TLCState s0 = prefix[prefix.length - 1].state;
-			sinfo = this.tool.getState(s1.fingerPrint(), s0);
+			TLCStateInfo s0 = prefix[prefix.length - 1];
+			StatePrinter.printInvariantViolationStateTraceState(this.tool.evalAlias(s0, s1), lastState, ++idx);
+			
+			sinfo = this.tool.getState(s1.fingerPrint(), s0.state);
 			if (sinfo == null) {
 				MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
 				MP.printError(EC.TLC_BUG, "4");
-				StatePrinter.printState(s1);
+				StatePrinter.printStandaloneErrorState(s1);
 				System.exit(1);
 			}
 		}
 		if (s2 == null) {
 			lastState = null;
 		}
-		StatePrinter.printState(sinfo, lastState, ++idx);
+		sinfo = this.tool.evalAlias(sinfo, s2 == null ? sinfo.state : s2);
+		StatePrinter.printInvariantViolationStateTraceState(sinfo, lastState, ++idx);
 		lastState = sinfo.state;
 		trace.add(sinfo);
 
@@ -410,10 +427,11 @@ public class TLCTrace {
 			if (sinfo == null) {
 				MP.printError(EC.TLC_FAILED_TO_RECOVER_INIT);
 				MP.printError(EC.TLC_BUG, "5");
-				StatePrinter.printState(s2);
+				StatePrinter.printStandaloneErrorState(s2);
 				System.exit(1);
 			}
-			StatePrinter.printState(sinfo, null, ++idx);
+			sinfo = this.tool.evalAlias(sinfo, s2);
+			StatePrinter.printInvariantViolationStateTraceState(sinfo, null, ++idx);
 			trace.add(sinfo);
 		}
 		OutputCollector.setTrace(trace);
@@ -438,7 +456,7 @@ public class TLCTrace {
 		TLCStateInfo[] prefix = this.getTrace(this.lastPtr, false);
 		int idx = 0;
 		while (idx < prefix.length) {
-			StatePrinter.printState(prefix[idx], lastState, idx + 1);
+			StatePrinter.printInvariantViolationStateTraceState(prefix[idx], lastState, idx + 1);
 			lastState = prefix[idx].state;
 			idx++;
 		}
@@ -528,4 +546,24 @@ public class TLCTrace {
 		}
 	}
 
+	// Add *dead* TLCTrace#writeBehavior to serialize (long) behaviors to disk from
+	// which they can be read efficiently with IOUtils!IODeserialize.
+	// See https://github.com/tlaplus/tlaplus/issues/481 for context.
+	public static void writeBehavior(final File file, final TLCState state, final StateVec stateTrace) {
+		try {
+			final ValueOutputStream vos = new ValueOutputStream(file, true);
+
+			final Value[] v = new Value[stateTrace.size()];
+			for (int i = 0; i < stateTrace.size(); i++) {
+				v[i] = new RecordValue(stateTrace.elementAt(i));
+			}
+			
+			// Do not normalize TupleValue because normalization depends on the actual
+			// UniqueString#internTable.
+			new TupleValue(v).write(vos);
+			vos.close();
+		} catch (IOException e) {
+			Assert.fail(EC.SYSTEM_DISK_IO_ERROR_FOR_FILE, file.getName());
+		}
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/TraceApp.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/TraceApp.java
index d3f01eb59cd8768305290c18ecfa88bb599ba36d..ffbdf53b4ea3865a81fd9e6d32ea063f7494dcac 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/TraceApp.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/TraceApp.java
@@ -16,5 +16,7 @@ public interface TraceApp {
   /* Reconstruct the info for the transition from s to s1. */
   public TLCStateInfo getState(TLCState s1, TLCState s);
 
+  public TLCStateInfo evalAlias(TLCStateInfo current, TLCState successor);
+
 }
 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/Worker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/Worker.java
index c06f9afa0756acd639a22f9c15cf3b8612ef72af..bf8d9962b3c37d19dbd850dbf4604f75d7f27287 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/Worker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/Worker.java
@@ -10,6 +10,7 @@ import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
 
+import tla2sany.semantic.ExprNode;
 import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
@@ -82,9 +83,13 @@ public final class Worker extends IdThread implements IWorker, INextStateFunctor
 				curState = this.squeue.sDequeue();
 				if (curState == null) {
 					synchronized (this.tlc) {
-						this.tlc.setDone();
+						if(!this.tlc.setDone()) {
+							doPostConditionCheck();
+						}
 						this.tlc.notify();
 					}
+					//TODO: finishAll not inside the synchronized block above, while
+					//inside in a similar construct below (Throwable catch)?!
 					this.squeue.finishAll();
 					return;
 				}
@@ -98,7 +103,7 @@ public final class Worker extends IdThread implements IWorker, INextStateFunctor
 				final long preNext = this.statesGenerated;
 				try {
 					this.tool.getNextStates(this, curState);
-				} catch (TLCRuntimeException e) {
+				} catch (TLCRuntimeException | EvalException e) {
 					// The next-state relation couldn't be evaluated.
 					this.tlc.doNextFailed(curState, null, e);
 				}
@@ -461,4 +466,28 @@ public final class Worker extends IdThread implements IWorker, INextStateFunctor
 		}
         return false;
 	}
+
+	// User request: http://discuss.tlapl.us/msg03658.html
+	private final void doPostConditionCheck() {
+		final ExprNode sn = (ExprNode) this.tool.getPostConditionSpec();
+		try {
+			if (sn != null && !this.tool.isValid(sn)) {
+				// It's not an assumption because the expression doesn't appear inside
+				// an ASSUME, but good enough for this prototype.
+				MP.printError(EC.TLC_ASSUMPTION_FALSE, sn.toString());
+				// Terminate with a non-zero exit value.
+				this.tlc.setError(false, EC.TLC_ASSUMPTION_FALSE);
+			}
+		} catch (Exception e) {
+			// tool.isValid(sn) failed to evaluate...
+			MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR, new String[] { sn.toString(), e.getMessage() });
+			this.tlc.setError(true, EC.TLC_ASSUMPTION_EVALUATION_ERROR);
+		}
+		// The PostCheckAssumption/PostCondition cannot be stated as an ordinary invariant
+		// with the help of TLCSet/Get because the invariant will only be evaluated for
+		// distinct states, but we want it to be evaluated after state-space exploration
+		// finished.  Hacking away with TLCGet("queue") = 0 doesn't work because the queue
+		// can be empty during the evaluation of the next-state relation when a worker dequeues
+		// the last state S, that has more successor states.
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/ActionWrapper.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/ActionWrapper.java
index ead40c50e5947345682f5c02c0a32472b4e85edf..ba13a828fb8cc0848e2f7a3f36e8f345b2c70c88 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/ActionWrapper.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/ActionWrapper.java
@@ -36,7 +36,7 @@ import tlc2.tool.Action;
 public final class ActionWrapper extends CostModelNode {
 
 	public enum Relation {
-		INIT, NEXT, PROP;
+		INIT, NEXT, PROP, CONSTRAINT;
 	}
 	
 	private final Action action;
@@ -146,6 +146,10 @@ public final class ActionWrapper extends CostModelNode {
 			// 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 if (relation == Relation.CONSTRAINT) {
+			MP.printMessage(EC.TLC_COVERAGE_CONSTRAINT,
+					new String[] { printLocation(), String.valueOf(this.secondary.getCount()),
+							String.valueOf(getEvalCount() + this.secondary.getCount()) });
 		} else {
 			MP.printMessage(EC.TLC_COVERAGE_NEXT, new String[] { printLocation(),
 					String.valueOf(this.secondary.getCount()), String.valueOf(getEvalCount()) });
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/CostModelCreator.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/CostModelCreator.java
index 7e60b8ac951cc5897cd858aeea01732cb8f133cf..5334722c58a0d9948386e4096cc028b7ecbfe31f 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/CostModelCreator.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/coverage/CostModelCreator.java
@@ -394,6 +394,23 @@ public class CostModelCreator extends ExplorerVisitor {
 			invariant.cm = collector.getCM(invariant, Relation.PROP);
 		}
 		
+		// action constraints
+		final ExprNode[] actionConstraints = tool.getActionConstraints();
+		for (ExprNode exprNode : actionConstraints) {
+			final OpDefNode odn = (OpDefNode) exprNode.getToolObject(tool.getId());
+			final Action act = new Action(exprNode, Context.Empty, odn);
+			act.cm = collector.getCM(act, Relation.CONSTRAINT);
+			exprNode.setToolObject(tool.getId(), act);
+		}
+		// state constraints
+		final ExprNode[] modelConstraints = tool.getModelConstraints();
+		for (ExprNode exprNode : modelConstraints) {
+			final OpDefNode odn = (OpDefNode) exprNode.getToolObject(tool.getId());
+			final Action act = new Action(exprNode, Context.Empty, odn);
+			act.cm = collector.getCM(act, Relation.CONSTRAINT);
+			exprNode.setToolObject(tool.getId(), act);
+		}
+		
         // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602
         if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) {
     		for (Action impliedInits : tool.getImpliedInits()) {
@@ -440,7 +457,20 @@ public class CostModelCreator extends ExplorerVisitor {
         for (Action invariant : tool.getInvariants()) {
         	//TODO May need to be ordered similar to next-state actions above.
         	invariant.cm.report();
-		}	
+		}
+        
+		// action constraints
+		final ExprNode[] actionConstraints = tool.getActionConstraints();
+		for (ExprNode exprNode : actionConstraints) {
+			final Action act = (Action) exprNode.getToolObject(tool.getId());
+			act.cm.report();
+		}
+		// state constraints
+		final ExprNode[] modelConstraints = tool.getModelConstraints();
+		for (ExprNode exprNode : modelConstraints) {
+			final Action act = (Action) exprNode.getToolObject(tool.getId());
+			act.cm.report();
+		}
         
         // https://github.com/tlaplus/tlaplus/issues/413#issuecomment-577304602
         if (Boolean.getBoolean(CostModelCreator.class.getName() + ".implied")) {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/distributed/TLCApp.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/distributed/TLCApp.java
index 7e2bce787f2552c5c06000015e48b267431e916e..1dde38cb4897638aca8b065ac2be706a4ddf4a22 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/distributed/TLCApp.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/distributed/TLCApp.java
@@ -256,6 +256,10 @@ public class TLCApp extends DistApp {
 	public TLCStateInfo getState(TLCState s1, TLCState s) {
 		return this.tool.getState(s1, s);
 	}
+	
+	public TLCStateInfo evalAlias(TLCStateInfo current, TLCState successor) {
+		return this.tool.evalAlias(current, successor);
+	}
 
 	/* (non-Javadoc)
 	 * @see tlc2.tool.distributed.DistApp#setCallStack()
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla b/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla
index 9505db8a30cc7e216caa18fc2e17e737534e4f14..e47e9b25d6ba2e9496175e1f0dc06c3247a7de0c 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OpenAddressing.tla
@@ -1,3 +1,5 @@
+\* Java implementation at ../OffHeapDiskFPSet.java or https://github.com/tlaplus/tlaplus/blob/master/tlatools/org.lamport.tlatools/src/tlc2/tool/fp/OffHeapDiskFPSet.java
+
 \begin{ppcal}
 -------------------------- MODULE OpenAddressing --------------------------
 EXTENDS Sequences, FiniteSets, Integers, TLC
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/AliasTLCStateInfo.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/AliasTLCStateInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..21861e30e7a69e66ba774e1d48df2b172db977c3
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/AliasTLCStateInfo.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.impl;
+
+import tlc2.tool.TLCState;
+import tlc2.tool.TLCStateInfo;
+
+public class AliasTLCStateInfo extends TLCStateInfo {
+
+	private final TLCState originalState;
+
+	public AliasTLCStateInfo(TLCState alias, TLCStateInfo current) {
+		super(alias, current);
+		originalState = current.state;
+	}
+
+	@Override
+	public TLCState getOriginalState() {
+		return originalState;
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/FastTool.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/FastTool.java
index 89b71cf2b584b808e2d0e393b0ea0a5d9fc4c5f7..2109ca4eb14bcfca08c3f053dc8451312fa4a5b6 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/FastTool.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/FastTool.java
@@ -47,10 +47,18 @@ public final class FastTool extends Tool {
 		super(mainFile, configFile, resolver);
 	}
 
+	public FastTool(String mainFile, String configFile, FilenameToStream resolver, Mode mode) {
+		super(mainFile, configFile, resolver, mode);
+	}
+	
 	public FastTool(String specDir, String specFile, String configFile, FilenameToStream fts) {
 		super(specDir, specFile, configFile, fts);
 	}
 
+	public FastTool(String specDir, String specFile, String configFile, FilenameToStream fts, Mode mode) {
+		super(specDir, specFile, configFile, fts, mode);
+	}
+
 	// 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
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/ModelConfig.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/ModelConfig.java
index 01e061447e9f0f514bc1422493867fa6fdc19e19..67976884f7ba337caff3aa858838591703cd3054 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/ModelConfig.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/ModelConfig.java
@@ -7,8 +7,12 @@ package tlc2.tool.impl;
 
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Hashtable;
+import java.util.List;
+import java.util.stream.Collectors;
 
 import tla2sany.parser.SimpleCharStream;
 import tla2sany.parser.TLAplusParserConstants;
@@ -29,6 +33,7 @@ import tlc2.value.impl.Value;
 import tlc2.value.impl.ValueVec;
 import util.FileUtil;
 import util.FilenameToStream;
+import util.MonolithSpecExtractor;
 import util.SimpleFilenameToStream;
 import util.TLAConstants;
 
@@ -44,7 +49,8 @@ import util.TLAConstants;
  * @author Yuan Yu, Leslie Lamport
  */
 public class ModelConfig implements ValueConstants, Serializable {
-    // keywords of the configuration file
+    // 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";
@@ -60,9 +66,9 @@ public class ModelConfig implements ValueConstants, Serializable {
     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 String Alias = "ALIAS";
+    private static final String PostCondition = "POSTCONDITION";
+    public static final String CheckDeadlock = "CHECK_DEADLOCK";
 
     private static final long serialVersionUID = 1L;
 
@@ -70,8 +76,8 @@ public class ModelConfig implements ValueConstants, Serializable {
      * 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, CheckDeadlock };
+            ActionConstraints, Invariant, Invariants, Init, Next, View, Symmetry, Spec, Prop, Props, Alias,
+            PostCondition, CheckDeadlock };
 
     private Hashtable configTbl;
     private Hashtable<String, String> overrides;
@@ -80,6 +86,7 @@ public class ModelConfig implements ValueConstants, Serializable {
     private Hashtable modOverrides;
     private String configFileName;
     private FilenameToStream resolver; // resolver for the file
+    private List<String> rawConstants;
 
     /**
      * Creates a new model config handle
@@ -123,13 +130,15 @@ public class ModelConfig implements ValueConstants, Serializable {
         temp = new Vect<>();
         this.configTbl.put(Prop, temp);
         this.configTbl.put(Props, temp);
-        this.configTbl.put(Type, "");
-        this.configTbl.put(TypeConstraint, "");
-
+        this.configTbl.put(Alias, "");
+        this.configTbl.put(PostCondition, "");
+        this.configTbl.put(CheckDeadlock, "undef");
+        
         this.modConstants = new Hashtable<>();
         this.modOverrides = new Hashtable<>();
         this.overrides = new Hashtable<>();
         this.overridesReverseMap = new Hashtable<>();
+        this.rawConstants = new ArrayList<>();
     }
 
     /**
@@ -145,15 +154,21 @@ public class ModelConfig implements ValueConstants, Serializable {
         try
         {
             // SZ 23.02.2009: separated file resolution from stream retrieval
-            FileInputStream fis = FileUtil.newFIS(resolver.resolve(this.configFileName, false));
+            InputStream fis = FileUtil.newFIS(resolver.resolve(this.configFileName, false));
             if (fis == null)
             {
                 throw new ConfigFileException(EC.CFG_ERROR_READING_FILE, new String[] { this.configFileName,
                         "File not found." });
             }
+            if (this.configFileName.endsWith(TLAConstants.Files.TLA_EXTENSION)) {
+				fis = MonolithSpecExtractor.config(fis,
+						// strip ".tla" from this.configFileName.
+						this.configFileName.replace(TLAConstants.Files.TLA_EXTENSION, ""));
+            }
             SimpleCharStream scs = new SimpleCharStream(fis, 1, 1);
             TLAplusParserTokenManager tmgr = new TLAplusParserTokenManager(scs, 2);
 
+        	final List<StringBuffer> rawConstants = new ArrayList<StringBuffer>();
             Token tt = getNextToken(tmgr);
             while (tt.kind != TLAplusParserConstants.EOF)
             {
@@ -225,36 +240,38 @@ public class ModelConfig implements ValueConstants, Serializable {
                                 Symmetry });
                     }
                     tt = getNextToken(tmgr);
-                } else if (tval.equals(Type))
+                } else if (tval.equals(Alias))
                 {
                     tt = getNextToken(tmgr);
                     if (tt.kind == TLAplusParserConstants.EOF)
                     {
-                        throw new ConfigFileException(EC.CFG_MISSING_ID, new String[] { String.valueOf(loc), Type });
+                        throw new ConfigFileException(EC.CFG_MISSING_ID, new String[] { String.valueOf(loc), Alias });
                     }
-                    String old = (String) this.configTbl.put(Type, tt.image);
+                    String old = (String) this.configTbl.put(Alias, tt.image);
                     if (old.length() != 0)
                     {
-                        throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Type });
+                        throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), Alias });
                     }
                     tt = getNextToken(tmgr);
-                } else if (tval.equals(TypeConstraint))
+                } else if (tval.equals(PostCondition))
                 {
                     tt = getNextToken(tmgr);
                     if (tt.kind == TLAplusParserConstants.EOF)
                     {
                         throw new ConfigFileException(EC.CFG_MISSING_ID, new String[] { String.valueOf(loc),
-                                TypeConstraint });
+                                PostCondition });
                     }
-                    String old = (String) this.configTbl.put(TypeConstraint, tt.image);
+                    String old = (String) this.configTbl.put(PostCondition, tt.image);
                     if (old.length() != 0)
                     {
                         throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc),
-                                TypeConstraint });
+                                PostCondition });
                     }
                     tt = getNextToken(tmgr);
                 } else if (tval.equals(Constant) || tval.equals(Constants))
                 {
+                	StringBuffer buf = new StringBuffer(tval);
+                	rawConstants.add(buf);
                     while ((tt = getNextToken(tmgr)).kind != TLAplusParserConstants.EOF)
                     {
                         /* Exit this while loop if the next token is something like "CONSTANT"
@@ -262,18 +279,20 @@ public class ModelConfig implements ValueConstants, Serializable {
                          */
                         if (this.configTbl.get(tt.image) != null)
                             break;
+                        
+                        buf.append("\n").append(tt.image).append(" ");
                         /* Token tt should be the first token in an expression of the form
                          * id <- ...  or id = ... .  In the current implementation, id is the
                          * token tt.  The following code was modified on 30 July 2009
                          * to allow id to be something like frob!bar!glitch, fixing Bug44.
                          */
                         String lhs = tt.image;
-                        tt = getNextToken(tmgr);
+                        tt = getNextToken(tmgr, buf);
                         while (tt.image.equals("!"))
                         {
-                            tt = getNextToken(tmgr);
+                            tt = getNextToken(tmgr, buf);
                             lhs = lhs + "!" + tt.image;
-                            tt = getNextToken(tmgr);
+                            tt = getNextToken(tmgr, buf);
                         }
                         Vect line = new Vect();
                         line.addElement(lhs);
@@ -282,24 +301,24 @@ public class ModelConfig implements ValueConstants, Serializable {
                         // tt = getNextToken(tmgr);
                         if (tt.image.equals("<-"))
                         {
-                            tt = getNextToken(tmgr);
+                            tt = getNextToken(tmgr, buf);
                             if (tt.image.equals("["))
                             {
                                 // This is a module override:
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                                 if (tt.kind == TLAplusParserConstants.EOF)
                                 {
                                     throw new ConfigFileException(EC.CFG_EXPECT_ID, new String[] {
                                             String.valueOf(scs.getBeginLine()), "<-[" });
                                 }
                                 String modName = tt.image;
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                                 if (!tt.image.equals("]"))
                                 {
                                     throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
                                             String.valueOf(scs.getBeginLine()), "]" });
                                 }
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                                 if (tt.kind == TLAplusParserConstants.EOF)
                                 {
                                     throw new ConfigFileException(EC.CFG_EXPECT_ID, new String[] {
@@ -330,10 +349,10 @@ public class ModelConfig implements ValueConstants, Serializable {
                             {
                                 while (true)
                                 {
-                                    tt = getNextToken(tmgr);
-                                    IValue arg = this.parseValue(tt, scs, tmgr);
+                                    tt = getNextToken(tmgr, buf);
+                                    IValue arg = this.parseValue(tt, scs, tmgr, buf);
                                     line.addElement(arg);
-                                    tt = getNextToken(tmgr);
+                                    tt = getNextToken(tmgr, buf);
                                     if (!tt.image.equals(","))
                                         break;
                                 }
@@ -341,32 +360,32 @@ public class ModelConfig implements ValueConstants, Serializable {
                                 {
                                     throw new ConfigFileException(EC.CFG_GENERAL, new String[] { String.valueOf(loc) });
                                 }
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                             }
                             if (!tt.image.equals("="))
                             {
                                 throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
                                         String.valueOf(scs.getBeginLine()), "= or <-" });
                             }
-                            tt = getNextToken(tmgr);
+                            tt = getNextToken(tmgr, buf);
                             if (tt.image.equals("["))
                             {
                                 // This is a module specific override:
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                                 if (tt.kind == TLAplusParserConstants.EOF)
                                 {
                                     throw new ConfigFileException(EC.CFG_EXPECT_ID, new String[] {
                                             String.valueOf(scs.getBeginLine()), "=[" });
                                 }
                                 String modName = tt.image;
-                                tt = getNextToken(tmgr);
+                                tt = getNextToken(tmgr, buf);
                                 if (!tt.image.equals("]"))
                                 {
                                     throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
                                             String.valueOf(scs.getBeginLine()), "]" });
                                 }
-                                tt = getNextToken(tmgr);
-                                line.addElement(this.parseValue(tt, scs, tmgr));
+                                tt = getNextToken(tmgr, buf);
+                                line.addElement(this.parseValue(tt, scs, tmgr, buf));
                                 Vect mConsts = (Vect) this.modConstants.get(modName);
                                 if (mConsts == null)
                                 {
@@ -377,7 +396,7 @@ public class ModelConfig implements ValueConstants, Serializable {
                             } else
                             {
                                 // This is a main module override:
-                                line.addElement(this.parseValue(tt, scs, tmgr));
+                                line.addElement(this.parseValue(tt, scs, tmgr, buf));
                                 constants.addElement(line);
                             }
                         }
@@ -429,7 +448,7 @@ public class ModelConfig implements ValueConstants, Serializable {
                         throw new ConfigFileException(EC.CFG_EXPECTED_SYMBOL, new String[] {
                             String.valueOf(scs.getBeginLine()), "TRUE or FALSE" });
                     }
-                    if (previous != null)
+                    if (previous != "undef")
                     {
                         throw new ConfigFileException(EC.CFG_TWICE_KEYWORD, new String[] { String.valueOf(loc), CheckDeadlock });
                     }
@@ -440,6 +459,7 @@ public class ModelConfig implements ValueConstants, Serializable {
                             String.valueOf(scs.getBeginLine()), "a keyword" });
                 }
             }
+            this.rawConstants = rawConstants.stream().map(buf -> buf.toString()).collect(Collectors.toList());
         } catch (IOException e)
         {
             throw new ConfigFileException(EC.CFG_ERROR_READING_FILE,
@@ -450,7 +470,7 @@ public class ModelConfig implements ValueConstants, Serializable {
     /**
      * Parses a value (number, string, boolean and set)
      */
-    private Value parseValue(Token tt, SimpleCharStream scs, TLAplusParserTokenManager tmgr) throws IOException
+    private Value parseValue(Token tt, SimpleCharStream scs, TLAplusParserTokenManager tmgr, final StringBuffer buf) throws IOException
     {
         if (tt.kind == TLAplusParserConstants.NUMBER_LITERAL)
         {
@@ -469,17 +489,17 @@ public class ModelConfig implements ValueConstants, Serializable {
         } else if (tt.image.equals("{"))
         {
             ValueVec elems = new ValueVec();
-            tt = getNextToken(tmgr);
+            tt = getNextToken(tmgr, buf);
             if (!tt.image.equals("}"))
             {
                 while (true)
                 {
-                	Value elem = this.parseValue(tt, scs, tmgr);
+                	Value elem = this.parseValue(tt, scs, tmgr, buf);
                     elems.addElement(elem);
-                    tt = getNextToken(tmgr);
+                    tt = getNextToken(tmgr, buf);
                     if (!tt.image.equals(","))
                         break;
-                    tt = getNextToken(tmgr);
+                    tt = getNextToken(tmgr, buf);
                 }
             }
             if (!tt.image.equals("}"))
@@ -501,7 +521,7 @@ public class ModelConfig implements ValueConstants, Serializable {
      * @param tmgr
      * @return
      */
-    public static Token getNextToken(TLAplusParserTokenManager tmgr)
+    private static Token getNextToken(TLAplusParserTokenManager tmgr)
     {
         try
         {
@@ -513,6 +533,28 @@ public class ModelConfig implements ValueConstants, Serializable {
             return tt;
         }
     }
+    private static Token getNextToken(TLAplusParserTokenManager tmgr, StringBuffer buf)
+    {
+        try
+        {
+            Token nextToken = tmgr.getNextToken();
+            buf.append(nextToken.image).append(" ");
+			return nextToken;
+        } catch (TokenMgrError e)
+        {
+            Token tt = new Token();
+            tt.kind = TLAplusParserConstants.EOF;
+            return tt;
+        }
+    }
+
+    /**
+     * @return All CONSTANT or CONSTANTS statements as they appear in the config file.
+     */
+    public synchronized final List<String> getRawConstants()
+    {
+        return this.rawConstants;
+    }
 
     public synchronized final Vect getConstants()
     {
@@ -589,20 +631,21 @@ public class ModelConfig implements ValueConstants, Serializable {
         return (Vect) this.configTbl.get(Prop);
     }
 
-    public synchronized final String getType()
+    public synchronized final String getAlias()
     {
-        return (String) this.configTbl.get(Type);
+        return (String) this.configTbl.get(Alias);
     }
 
-    public synchronized final String getTypeConstraint()
+    public synchronized final String getPostCondition()
     {
-        return (String) this.configTbl.get(TypeConstraint);
+        return (String) this.configTbl.get(PostCondition);
     }
 
     public synchronized final boolean getCheckDeadlock()
     {
-    	if (this.configTbl.containsKey(CheckDeadlock)) {
-    		return (boolean) this.configTbl.get(CheckDeadlock);
+    	Object object = this.configTbl.get(CheckDeadlock);
+    	if (object instanceof Boolean) {
+    		return (boolean) object;
     	}
     	return true;
     }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Spec.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Spec.java
index e3b13d034e80db1a62850354ab7cb97ad8007218..976f6e51c2e36212bd06bd70bf4057b5fe97a91e 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Spec.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Spec.java
@@ -36,6 +36,7 @@ import tlc2.tool.BuiltInOPs;
 import tlc2.tool.Defns;
 import tlc2.tool.TLCState;
 import tlc2.tool.ToolGlobals;
+import tlc2.tool.impl.Tool.Mode;
 import tlc2.util.Context;
 import tlc2.util.ObjLongTable;
 import tlc2.util.Vect;
@@ -44,7 +45,7 @@ import tlc2.value.impl.LazyValue;
 import tlc2.value.impl.ModelValue;
 import util.Assert;
 import util.FilenameToStream;
-import util.TLAConstants;
+import util.MonolithSpecExtractor;
 import util.UniqueString;
 
 // Note that we use all of the {@code default} defined functionality in our
@@ -79,7 +80,8 @@ abstract class Spec
     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) {
+	public Spec(final String specDir, final String specFile, final String configFile, final FilenameToStream resolver,
+			Mode mode) {
         this.specDir = specDir;
         this.rootFile = specFile;
         this.defns = new Defns();
@@ -90,11 +92,11 @@ abstract class Spec
         // 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 = new ModelConfig(MonolithSpecExtractor.getConfig(configFile), resolver);
         this.config.parse();
         ModelValue.setValues(); // called after seeing all model values
 
-        specProcessor = new SpecProcessor(getRootName(), resolver, toolId, defns, config, this, this, tlaClass);
+        specProcessor = new SpecProcessor(getRootName(), resolver, toolId, defns, config, this, this, tlaClass, mode);
         
         this.unprocessedDefns = specProcessor.getUnprocessedDefns();
     }
@@ -202,36 +204,38 @@ abstract class Spec
         return def.getBody();
     }
 
-    /* Get the type declaration for the state variables. */
-    public final SemanticNode getTypeSpec()
+    /* Get the alias declaration for the state variables. */
+    public final SemanticNode getAliasSpec()
     {
-        String name = this.config.getType();
+        String name = this.config.getAlias();
         if (name.length() == 0)
         {
             Assert.fail(EC.TLC_CONFIG_NO_STATE_TYPE);
         }
 
+        // A true constant-level alias such as such as [ x |-> "foo" ] will be evaluated
+        // eagerly and type be an instance of RecordValue.  It would be good to return a
+        // proper warning.
         Object type = this.defns.get(name);
         if (type == null)
         {
-            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type", name });
+            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "alias", name });
         }
         if (!(type instanceof OpDefNode))
         {
-            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type", name });
+            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "alias", name });
         }
         OpDefNode def = (OpDefNode) type;
         if (def.getArity() != 0)
         {
-            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type", name });
+            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "alias", name });
         }
         return def.getBody();
     }
 
-    /* Get the type declaration for the state variables. */
-    public final SemanticNode getTypeConstraintSpec()
+    public final SemanticNode getPostConditionSpec()
     {
-        String name = this.config.getTypeConstraint();
+        String name = this.config.getPostCondition();
         if (name.length() == 0)
         {
             return null;
@@ -240,16 +244,16 @@ abstract class Spec
         Object type = this.defns.get(name);
         if (type == null)
         {
-            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "type constraint", name });
+            Assert.fail(EC.TLC_CONFIG_SPECIFIED_NOT_DEFINED, new String[] { "post assumption", name });
         }
         if (!(type instanceof OpDefNode))
         {
-            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "type constraint", name });
+            Assert.fail(EC.TLC_CONFIG_ID_MUST_NOT_BE_CONSTANT, new String[] { "post assumption", name });
         }
         OpDefNode def = (OpDefNode) type;
         if (def.getArity() != 0)
         {
-            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "type constraint", name });
+            Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "post assumption", name });
 
         }
         return def.getBody();
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/SpecProcessor.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/SpecProcessor.java
index 172150f14d0aec289b870a10d5dfc69ec3a1d1ec..6374bbc30a7257abf2b6711fb8f95175c96b9751 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/SpecProcessor.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/SpecProcessor.java
@@ -25,6 +25,7 @@
  ******************************************************************************/
 package tlc2.tool.impl;
 
+import java.io.File;
 import java.io.Serializable;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -75,7 +76,9 @@ import tlc2.tool.EvalException;
 import tlc2.tool.Specs;
 import tlc2.tool.TLAPlusExecutorState;
 import tlc2.tool.TLCStateMut;
+import tlc2.tool.TLCStateMutSimulation;
 import tlc2.tool.ToolGlobals;
+import tlc2.tool.impl.Tool.Mode;
 import tlc2.util.Context;
 import tlc2.util.List;
 import tlc2.util.Vect;
@@ -146,7 +149,7 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
     
 	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) {
+			final TLAClass tlaClass, Mode mode) {
 		super();
 		this.rootFile = rootFile;
 		this.resolver = resolver;
@@ -162,7 +165,7 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
 
 		// Parse and process this spec.
 		// It takes care of all overrides.
-		processSpec();
+		processSpec(mode);
 
 		snapshot = defns.snapshot();
 
@@ -309,9 +312,10 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
      * 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.
+     * @param mode 
      */
     // SZ Feb 20, 2009: added support for existing specObj
-    private final void processSpec()
+    private final void processSpec(final Mode mode)
     {
 
         // construct new specification object, if the
@@ -570,120 +574,126 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
 		// 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, ...).
+		// the complete classpath). To load user-provided index classes, pass the
+        // -Dtlc2.overrides.TLCOverrides property with a list of index classes
+        // separated by the system's path separator (":", ";").  If no property is given,
+        // the default is to load the first class on the classpath with name tlc2.overrides.TLCOverrides
+        // that implements tlc2.overrides.ITLCOverrides.  This is usually the tlc2.overrides.TLCOverrides
+        // provided by the CommunityModules.
         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 String tlcOverrides = System.getProperty("tlc2.overrides.TLCOverrides", "tlc2.overrides.TLCOverrides");
+		for (String ovrde : tlcOverrides.split(File.pathSeparator)) {
+			final Class<?> idx = this.tlaClass.loadClass(ovrde);
+			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 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,
+							
+							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 LOOP;
+			                    
+			                    // continue with next method (don't try to also load Execution annotation below).
+			                    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,
+							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 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;
+			                    
+			                    // continue with next method (don't try to also load Execution annotation below).
+			                    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 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);
 							}
-
-							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;
 				}
-			} catch (InstantiationException | IllegalAccessException e) {
-				// TODO Specific error code.
-				Assert.fail(EC.GENERAL);
-				return;
-			}
-        }
+	        }
+		}
         
 
         Set<String> overriden = new HashSet<String>();
@@ -709,12 +719,16 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
             }
         }
 
-        // set variables to the static filed in the state
-        if (hasCallableValue) {
-        	TLAPlusExecutorState.setVariables(this.variablesNodes);
-        } else {
-            TLCStateMut.setVariables(this.variablesNodes);
-        }
+		// set variables to the static filed in the state
+		if (mode == Mode.Simulation) {
+			TLCStateMutSimulation.setVariables(this.variablesNodes);
+		} else if (hasCallableValue) {
+			assert mode == Mode.Executor;
+			TLAPlusExecutorState.setVariables(this.variablesNodes);
+		} else {
+			assert mode == Mode.MC;
+			TLCStateMut.setVariables(this.variablesNodes);
+		}
 
         // Apply config file overrides to operator definitions:
         for (int i = 0; i < rootOpDefs.length; i++)
@@ -1411,7 +1425,13 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
 	            {
 	                Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "action constraint", name });
 	            }
-	            this.actionConstraints[idx++] = def.getBody();
+	            final ExprNode body = def.getBody();
+				// Remember OpDefNode of body because CostModelCreator needs it to correctly
+				// report state statistics (CostModelCreator#create will later replace it
+	            // with an Action instance).
+	            assert body.getToolObject(toolId) == null;
+	            body.setToolObject(toolId, def);
+	            this.actionConstraints[idx++] = body;
 	        } else if (constr != null)
 	        {
 	            if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val)
@@ -1451,7 +1471,13 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
 	            {
 	                Assert.fail(EC.TLC_CONFIG_ID_REQUIRES_NO_ARG, new String[] { "constraint", name });
 	            }
-	            this.modelConstraints[idx++] = def.getBody();
+	            final ExprNode body = def.getBody();
+				// Remember OpDefNode of body because CostModelCreator needs it to correctly
+				// report state statistics (CostModelCreator#create will later replace it
+	            // with an Action instance).
+	            assert body.getToolObject(toolId) == null;
+	            body.setToolObject(toolId, def);
+				this.modelConstraints[idx++] = body;
 	        } else if (constr != null)
 	        {
 	            if (!(constr instanceof IBoolValue) || !((BoolValue) constr).val)
@@ -1791,4 +1817,8 @@ public class SpecProcessor implements ValueConstants, ToolGlobals {
 	public Defns getUnprocessedDefns() {
 		return snapshot;
 	}
+	
+	public Defns getDefns() {
+		return defns;
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Tool.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Tool.java
index 9f3cc4d6d9c71ac456387b483bb0e171c6ee60b6..3d1a9990a2a63424407d47371359ddcf4c41cd07 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Tool.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/impl/Tool.java
@@ -30,6 +30,7 @@ import tla2sany.semantic.SymbolNode;
 import tla2sany.semantic.ThmOrAssumpDefNode;
 import tlc2.output.EC;
 import tlc2.output.MP;
+import tlc2.output.OutputCollector;
 import tlc2.tool.Action;
 import tlc2.tool.BuiltInOPs;
 import tlc2.tool.EvalControl;
@@ -40,10 +41,12 @@ import tlc2.tool.INextStateFunctor;
 import tlc2.tool.IStateFunctor;
 import tlc2.tool.ITool;
 import tlc2.tool.StateVec;
+import tlc2.tool.TLAPlusExecutorState;
 import tlc2.tool.TLCState;
 import tlc2.tool.TLCStateFun;
 import tlc2.tool.TLCStateInfo;
 import tlc2.tool.TLCStateMut;
+import tlc2.tool.TLCStateMutSimulation;
 import tlc2.tool.ToolGlobals;
 import tlc2.tool.coverage.CostModel;
 import tlc2.util.Context;
@@ -58,6 +61,7 @@ import tlc2.value.Values;
 import tlc2.value.impl.Applicable;
 import tlc2.value.impl.BoolValue;
 import tlc2.value.impl.Enumerable;
+import tlc2.value.impl.Enumerable.Ordering;
 import tlc2.value.impl.EvaluatingValue;
 import tlc2.value.impl.FcnLambdaValue;
 import tlc2.value.impl.FcnParams;
@@ -88,6 +92,7 @@ import tlc2.value.impl.ValueEnumeration;
 import tlc2.value.impl.ValueExcept;
 import tlc2.value.impl.ValueVec;
 import util.Assert;
+import util.Assert.TLCRuntimeException;
 import util.FilenameToStream;
 import util.TLAConstants;
 import util.UniqueString;
@@ -104,6 +109,29 @@ public abstract class Tool
     extends Spec
     implements ValueConstants, ToolGlobals, ITool
 {
+
+  	/*
+	 * Prototype, do *not* activate when checking safety or liveness!!!:
+	 * For simulation that is not meant as a substitute of exhaustive checking for too
+	 * large models, it can be useful to generate behaviors as quickly as possible,
+	 * i.e. without checking all successor states along the states of the behavior.
+	 * This flag activates the code path that efficiently generate only a single 
+	 * successor state during simulation.  It does not let the user parameterize
+	 * the code with a particular distribution, but instead draws from the uniform
+	 * distribution.  
+	 * 
+	 * In its current form, it only handles non-determinism expressed with opcode
+	 * OPCODE_be (bounded exist), i.e. (which simply happened to be the primary
+	 * expression that we encountered in the SWIM spec during this work):
+	 * 
+	 * VARIABLE x
+	 * \E n \in S: x' = n
+	 */
+  private static final boolean PROBABLISTIC = Boolean.getBoolean(Tool.class.getName() + ".probabilistic");
+
+  public enum Mode {
+	  Simulation, MC, Executor;
+  }
 	
   public static final Value[] EmptyArgs = new Value[0];
 
@@ -121,17 +149,38 @@ public abstract class Tool
 	  this(new File(specFile), specFile, configFile, resolver);
   }
 
+  public Tool(String specFile, String configFile, FilenameToStream resolver, Mode mode) {
+	  this(new File(specFile), specFile, configFile, resolver, mode);
+  }
+  
   private Tool(File specDir, String specFile, String configFile, FilenameToStream resolver)
   {
 	  this(specDir.isAbsolute() ? specDir.getParent() : "", specFile, configFile, resolver);
   }
   
+  private Tool(File specDir, String specFile, String configFile, FilenameToStream resolver, Mode mode)
+  {
+	  this(specDir.isAbsolute() ? specDir.getParent() : "", specFile, configFile, resolver, mode);
+  }
+  
   public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver)
   {
-      super(specDir, specFile, configFile, resolver);
+	  this(specDir, specFile, configFile, resolver, Mode.MC);
+  }
+  
+  public Tool(String specDir, String specFile, String configFile, FilenameToStream resolver, Mode mode)
+  {
+      super(specDir, specFile, configFile, resolver, mode);
 
-      // Initialize state.
-      TLCStateMut.setTool(this);
+		// set variables to the static filed in the state
+		if (mode == Mode.Simulation) {
+			TLCStateMutSimulation.setTool(this);
+		} else if (mode == Mode.Executor) {
+			TLAPlusExecutorState.setTool(this);
+		} else {
+			// Initialize state.
+			TLCStateMut.setTool(this);
+		}
       
 		Action next = this.getNextStateSpec();
 		if (next == null) {
@@ -761,6 +810,7 @@ public abstract class Tool
     StateVec nss = new StateVec(0);
     this.getNextStates(action, action.pred, acts, ctx, state, s1, nss, action.cm);
     if (coverage) { action.cm.incInvocations(nss.size()); }
+    if (PROBABLISTIC && nss.size() > 1) {System.err.println("Simulator generated more than one next state");}
     return nss;
   }
   
@@ -1038,19 +1088,48 @@ public abstract class Tool
 	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);
-	    }
+		if (PROBABLISTIC) {
+			// probabilistic (return after a state has been generated, ordered is randomized)
+			final tlc2.tool.SimulationWorker simWorker = (tlc2.tool.SimulationWorker) Thread.currentThread();
+			int index = (int) Math.floor(simWorker.getRNG().nextDouble() * alen);
+			final int p = simWorker.getRNG().nextPrime();
+		    for (int i = 0; i < alen; i++) {
+			      resState = this.getNextStates(action, args[index], acts, c, s0, resState, nss, cm);
+				  if (nss.hasStates()) {
+						return resState;
+				  }
+				  index = (index + p) % alen;
+			}
+		} else {
+		    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);
+	    
+	    if (PROBABLISTIC) {
+		    // probabilistic (return after a state has been generated, ordered is randomized)
+			final ContextEnumerator Enum = this.contexts(Ordering.RANDOMIZED, 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);
+				if (nss.hasStates()) {
+					return resState;
+				}
+		    }
+	    } else {
+	    	// non-deterministically generate successor states (potentially many)
+	    	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
@@ -1132,6 +1211,11 @@ public abstract class Tool
 	case OPCODE_case:   // Case
 	  {
 	    SemanticNode other = null;
+		if (PROBABLISTIC) {
+			// See Bounded exists above!
+			throw new UnsupportedOperationException(
+							"Probabilistic evaluation of next-state relation not implemented for CASE yet.");
+		}
 	    for (int i = 0; i < alen; i++) {
 	      OpApplNode pair = (OpApplNode)args[i];
 	      ExprOrOpArgNode[] pairArgs = pair.getArgs();
@@ -1201,6 +1285,13 @@ public abstract class Tool
 	          Assert.fail("In computing next states, the right side of \\IN" +
 	                      " is not enumerable.\n" + pred);
 	        }
+	        
+			if (PROBABLISTIC) {
+				// See Bounded exists above!
+				throw new UnsupportedOperationException(
+								"Probabilistic evaluation of next-state relation not implemented for \\in yet.");
+			}
+
 	        ValueEnumeration Enum = ((Enumerable)rval).elements();
 	        Value elem;
 	        while ((elem = Enum.nextElement()) != null) {
@@ -1381,7 +1472,67 @@ public abstract class Tool
   }
     
   /* eval */
+  public TLCState evalAlias(TLCState current, TLCState successor) {
+		if ("".equals(this.config.getAlias())) {
+			return current;
+		}
+		// see getState(..)
+		IdThread.setCurrentState(current);
 
+		try {
+			final TLCState alias = eval(getAliasSpec(), Context.Empty, current, successor, EvalControl.Clear).toState();
+			if (alias != null) {
+				return alias;
+			}
+		} catch (EvalException | TLCRuntimeException e) {
+			// Fall back to original state if eval fails.
+			return current;
+		}
+		
+		return current;
+  }
+
+  public TLCStateInfo evalAlias(TLCStateInfo current, TLCState successor) {
+		if ("".equals(this.config.getAlias())) {
+			return current;
+		}
+		
+		// see getState(..)
+		IdThread.setCurrentState(current.state);
+
+		try {
+			final TLCState alias = eval(getAliasSpec(), Context.Empty, current.state, successor, EvalControl.Clear).toState();
+			if (alias != null) {
+				return new AliasTLCStateInfo(alias, current);
+			}
+		} catch (EvalException | TLCRuntimeException e) {
+			// Fall back to original state if eval fails.
+			return current;
+			// TODO We have to somehow communicate this exception back to the user.
+			// Unfortunately, the alias cannot be validated by SpecProcess (unless pure
+			// constant expression who are too simple to be used in trace expressions).
+			// Throwing the exception would be possible, but pretty annoying if TLC fails
+			// to print an error trace because of a bogus alias after hours of model
+			// checking (this is the very reason why the code falls back to return the 
+			// original/current state).  Printing the exception to stdout/stderr here
+			// would mess with the Toolbox's parsing that reads stdout back in.  It would
+			// also look bad because we would print the error on every evaluation of the
+			// alias and it's conceivable that -in most cases- evaluation would fail for
+			// all evaluations.  This suggests that we have to defer reporting of evaluation
+			// and runtime exception until after the error-trace has been printed. If
+			// evaluation only failed for some invocations of evalAlias, the user will
+			// be able to figure out the ones that failed by looking at the trace.  This
+			// state should not be kept in Tool, because it doesn't know how to group
+			// sequences of evalAlias invocations.
+			// We could avoid keeping state entirely, if the exception was attached as an
+			// "auxiliary" variable to the TLCStateInfo and printed as part of the error
+			// trace.  The error trace would look strange, but it appears to be the best
+			// compromise, especially if only some of the evaluations fail.
+		}
+		
+		return current;
+  }
+  
   /* Special version of eval for state expressions. */
   @Override
   public final IValue eval(SemanticNode expr, Context c, TLCState s0, CostModel cm) {
@@ -2421,11 +2572,21 @@ public abstract class Tool
   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);
+      final CostModel cm = coverage ? ((Action) constrs[i].getToolObject(toolId)).cm : CostModel.DO_NOT_RECORD;
+      IValue bval = this.eval(constrs[i], Context.Empty, state, cm);
       if (!(bval instanceof BoolValue)) {
         Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
       }
-      if (!((BoolValue)bval).val) return false;
+      if (!((BoolValue)bval).val) {
+  		  if (coverage) {
+  			  cm.incInvocations();
+		  }
+    	  return false;
+      } else {
+  		  if (coverage) {
+  			  cm.incSecondary();
+		  }
+      }
     }
     return true;
   }
@@ -2435,11 +2596,21 @@ public abstract class Tool
   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);
+      final CostModel cm = coverage ? ((Action) constrs[i].getToolObject(toolId)).cm : CostModel.DO_NOT_RECORD;
+      Value bval = this.eval(constrs[i], Context.Empty, s1, s2, EvalControl.Clear, cm);
       if (!(bval instanceof BoolValue)) {
         Assert.fail(EC.TLC_EXPECTED_VALUE, new String[]{"boolean", constrs[i].toString()});
       }
-      if (!((BoolValue)bval).val) return false;
+      if (!((BoolValue)bval).val) {
+  		  if (coverage) {
+			  cm.incInvocations();
+		  }
+    	  return false;
+      } else {
+  		  if (coverage) {
+  			  cm.incSecondary();
+		  }
+      }
     }
     return true;
   }
@@ -3097,6 +3268,31 @@ public abstract class Tool
     return ((BoolValue)val).val;
   }
 
+  @Override
+  public final int checkAssumptions() {
+      final ExprNode[] assumps = getAssumptions();
+      final boolean[] isAxiom = getAssumptionIsAxiom();
+      int assumptionsError = EC.NO_ERROR;
+      for (int i = 0; i < assumps.length; i++)
+      {
+          try
+          {
+              if ((!isAxiom[i]) && !isValid(assumps[i]))
+              {
+                  OutputCollector.addViolatedAssumption(assumps[i]);
+                  assumptionsError = MP.printError(EC.TLC_ASSUMPTION_FALSE, assumps[i].toString());
+              }
+          } catch (final Exception e)
+          {
+              // Assert.printStack(e);
+              OutputCollector.addViolatedAssumption(assumps[i]);
+              assumptionsError = MP.printError(EC.TLC_ASSUMPTION_EVALUATION_ERROR,
+                      new String[] { assumps[i].toString(), e.getMessage() });
+          }
+      }
+      return assumptionsError;
+  }
+  
     /* Reconstruct the initial state whose fingerprint is fp. */
 	@Override
 	public final TLCStateInfo getState(final long fp) {
@@ -3444,43 +3640,48 @@ public abstract class Tool
           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);
+    return contexts(Ordering.NORMALIZED, appl, c, s0, s1, control, cm);
   }
 
+	private final ContextEnumerator contexts(Ordering ordering, 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(ordering);
+	      }
+	      else {
+	        for (int j = 0; j < farg.length; j++) {
+	          vars[idx] = farg[j];
+	          enums[idx++] = ((Enumerable)boundSet).elements(ordering);
+	        }
+	      }
+	    }
+	    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 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java
index ee3ea670da4b674559f6fd077dc659b2a8546fe3..269ff80cce1abd30671a0eb0e76c1d060096d075 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/ILivenessStateWriter.java
@@ -34,5 +34,5 @@ public interface ILivenessStateWriter extends IStateWriter {
 
 	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);
+	void writeState(TLCState state, TBGraphNode tableauNode, TLCState successor, TBGraphNode tableauNodeSuccessor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visualization);
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveCheck1.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveCheck1.java
index 27a711604be93a68279d2dc52967e6b31526a26e..be5a04a4dbc9af59553003a4d93cfcff071672ce 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveCheck1.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveCheck1.java
@@ -7,7 +7,6 @@ package tlc2.tool.liveness;
 
 import java.io.IOException;
 
-import tlc2.TLCGlobals;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.output.StatePrinter;
@@ -654,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.printInvariantViolationStateTraceState(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;
@@ -675,20 +675,19 @@ public class LiveCheck1 implements ILiveCheck {
 				if (sinfo == null) {
 					throw new EvalException(EC.TLC_FAILED_TO_RECOVER_NEXT);
 				}
-				StatePrinter.printState(sinfo, lastState, ++stateNum);
+				StatePrinter.printInvariantViolationStateTraceState(sinfo, lastState, ++stateNum);
 				lastState = sinfo.state;
 			}
 		}
 		if (node.stateFP == lastState.fingerPrint()) {
 			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);
 		}
 	}
 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveWorker.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveWorker.java
index 21f23c196f76b94ad51f4a501f4cdc6aacb59a37..e88aa380affb10c7835d7c7412f276aad0d6801b 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveWorker.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/liveness/LiveWorker.java
@@ -869,8 +869,8 @@ public class LiveWorker implements Callable<Boolean> {
 				}
 
 				// Print the prefix in reverse order of previous loop:
-				for (int i = 0; i < states.size(); i++) {
-					StatePrinter.printState(states.get(i));
+				for (int i = 0; i < states.size() - 1; i++) {
+					StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(states.get(i), states.get(i + 1).state));
 				}
 				return states;
 			}
@@ -902,7 +902,11 @@ public class LiveWorker implements Callable<Boolean> {
 		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());
+			long fp = cycleStack.popLong();
+			if (postfix.isEmpty() || postfix.lastElement() != fp) {
+				// See comment 4723xdf below.  This here just a minor optimization.
+				postfix.addElement(fp);
+			}
 			cycleStack.popInt(); // ignore tableau idx. The tableau idx is
 									// irrelevant as <<fpA, tidx1>> and <<fpA,
 									// tidx2>> both map to the same state in the
@@ -926,29 +930,36 @@ public class LiveWorker implements Callable<Boolean> {
 		
 		/*
 		 * 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.
+		 * of the SCC has been printed. Now, print cycleState and 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);
+		
+		// 4723xdf:
+		// 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 (postfix.isEmpty()) {
+			StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(cycleState, cycleState.state));
+		} else {
+			postfix.pack().removeLastIf(cycleState.fingerPrint());
+			
+			for (int i = postfix.size() - 1; i >= 0; i--) {
+				final long curFP = postfix.elementAt(i);
+				TLCStateInfo sucinfo = tool.getState(curFP, sinfo);
+				StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(sinfo, sucinfo.state));
+				sinfo = sucinfo;
 			}
+			StatePrinter.printInvariantViolationStateTraceState(tool.evalAlias(sinfo, cycleState.state));
 		}
 
 		/* All error trace states have been printed (prefix + cycleStack +
@@ -960,6 +971,9 @@ public class LiveWorker implements Callable<Boolean> {
 		if (sinfo.fingerPrint() == cycleState.fingerPrint()) {
 			StatePrinter.printStutteringState(stateNumber);
 		} else {
+			// The new sinfo.state is equivalent to cycleState after getState(..). The
+			// sinfo.info has the name of the action that closes the loop of the lasso/takes
+			// us back to cycleState.
 			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
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/DotActionWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/util/DotActionWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..bf0f796b1789f9516324e2810f63ba4bfac3ca9a
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/DotActionWriter.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * 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.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import tlc2.tool.Action;
+import util.FileUtil;
+
+/**
+ * Writes the given action 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 DotActionWriter {
+
+    protected final PrintWriter writer;
+    protected final String fname;
+	
+	public DotActionWriter(final String fname, final String strict) throws IOException {
+        this.fname = fname;
+        this.writer = new PrintWriter(FileUtil.newBFOS(fname));
+		this.writer.append(strict + "digraph ActionGraph {\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
+        
+		// Spread out state nodes a bit more.
+        this.writer.append("nodesep=0.35;\n");
+
+		this.writer.flush();
+	}
+
+	public synchronized void write(final Action action, final int id) {
+		// Marker the state as an initial state by using a filled style.
+		this.writer.append(Integer.toString(id));
+		this.writer.append(" [label=\"");
+		this.writer.append(action2dot(action, id));
+		this.writer.append("\"]");
+		this.writer.append("\n");
+	}
+
+	public synchronized void write(Action from, final int fromId, Action to, final int toId) {
+		write(from, fromId, to, toId, 0d);
+	}
+
+	public synchronized void write(Action from, final int fromId, Action to, final int toId, final double weight) {
+		// Write the transition edge.
+		this.writer.append(Integer.toString(fromId));
+		this.writer.append(" -> ");
+		this.writer.append(Integer.toString(toId));
+		// Add the transition edge label.
+		if (weight == 0d) {
+			this.writer.append("[color=\"green\",style=dotted]");
+		} else {
+			// TODO don't increase penwidth (contributes to a spaghetti ball of lines) but
+			// use heatmap approach like in ModuleCoverageInforamtion.
+			this.writer.append(String.format("[penwidth=%s]", Double.toString(weight)));
+		}
+		this.writer.append(";\n");
+	}
+
+	protected static String action2dot(final Action action, final int id) {
+		return action.getName().toString();
+	}
+
+	public void close() {
+		this.writer.append("}");
+		this.writer.close();
+	}
+
+	public void writeSubGraphStart(final String key, final String label) {
+		this.writer.append(String.format("subgraph cluster_%s {\ncolor=\"white\"\nlabel=\"%s\"\n", key, label));
+	}
+
+	public void writeSubGraphEnd() {
+		this.writer.append("}\n");
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/DotStateWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/util/DotStateWriter.java
index 8cb2ca08a3ae87941d0285aa04292035b7736074..429b540ea763881440f69404dab86e1086a4cdcf 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/DotStateWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/DotStateWriter.java
@@ -159,11 +159,11 @@ public class DotStateWriter extends StateWriter {
 	/* (non-Javadoc)
 	 * @see tlc2.util.StateWriter#writeState(tlc2.tool.TLCState, tlc2.tool.TLCState, boolean)
 	 */
-	public synchronized void writeState(TLCState state, TLCState successor, boolean successorStateIsNew) {
+	public 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)
+    public void writeState(final TLCState state, final TLCState successor, final boolean successorStateIsNew, Action action)
     {
 		writeState(state, successor, null, 0, 0, successorStateIsNew, Visualization.DEFAULT, action);
     }
@@ -171,21 +171,21 @@ public class DotStateWriter extends StateWriter {
 	/* (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) {
+	public 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) {
+	public 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,
+	private 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());
 		
@@ -295,7 +295,7 @@ public class DotStateWriter extends StateWriter {
 		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, action,
+			String str = String.format("%s [label=\"%s\",fillcolor=%d]", action.replaceAll("!", ":"), action,
 					this.actionToColors.get(action));
 			sb.append(str);
 			sb.append("\n");
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/IStateWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/util/IStateWriter.java
index b57111c3cb31b8591f1d8bd804ee56b9abb7d8e6..2bd7ff11481c4d2ec3e9405a54e63ac5b9dea4e9 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/IStateWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/IStateWriter.java
@@ -54,11 +54,11 @@ public interface IStateWriter {
 	
 	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, boolean successorStateIsNew, Visualization visualization);
 	
 	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 writeState(TLCState state, TLCState successor, BitVector actionChecks, int from, int length, boolean successorStateIsNew, Visualization visualization);
 	
 	void close();
 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/LongVec.java b/tlatools/org.lamport.tlatools/src/tlc2/util/LongVec.java
index 44706322da05c66ff1f53474b3bebbb3bc28bf6c..6294dca2e1d5bbdaf2101a6ed0a6a95b2cd46c10 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/LongVec.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/LongVec.java
@@ -67,6 +67,10 @@ public class LongVec implements Cloneable, Serializable {
         return "Index: "+index+", Size: "+elementCount;
     }
 
+	public final boolean isEmpty() {
+		return this.elementCount == 0;
+	}
+
 	public final int size() {
 		return this.elementCount;
 	}
@@ -155,4 +159,30 @@ public class LongVec implements Cloneable, Serializable {
 			throw e;
 		}
 	}
+
+	/** 
+	 * Remove *consecutive* duplicates:
+	 * [1,2,2,1,1,3] -> [1,2,1,3]
+	 */
+	public LongVec pack() {
+		// so far only used to while printing a liveness error trace and thus not
+		// performance critical. Once performance matters, we should do it in-place.
+		final LongVec filtered = new LongVec(size());
+		for (int i = 0; i < elementCount; i++) {
+			long x = elementData[i];
+			if (filtered.elementCount == 0 || filtered.lastElement() != x) {
+				filtered.addElement(x);
+			}
+		}
+		this.elementCount = filtered.elementCount;
+		this.elementData = filtered.elementData;
+		return this;
+	}
+
+	public LongVec removeLastIf(long x) {
+		if (this.elementCount > 0 && this.lastElement() == x) {
+			this.elementCount = this.elementCount - 1;
+		}
+		return this;
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/NoopStateWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/util/NoopStateWriter.java
index 4d3dce4cdea66bf803c7b04fc41fd9983d854db1..936914cd2114bcd71348fb5c6639664f2c735790 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/NoopStateWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/NoopStateWriter.java
@@ -71,7 +71,7 @@ public final class NoopStateWriter implements IStateWriter {
 	 * @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) {
+			Visualization visualization) {
 		// noop
 	}
 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/RandomGenerator.java b/tlatools/org.lamport.tlatools/src/tlc2/util/RandomGenerator.java
index 75e89305ae9523ae0d6ed4f720cf7633f31b4d93..26d12db2a175f79c1da9d39cebcfaf7d982ca4a5 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/RandomGenerator.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/RandomGenerator.java
@@ -226,5 +226,12 @@ public class RandomGenerator extends Random {
     }
     return primes[index];
   }
-  
+
+	public static int nextPrime(Random r) {
+		int index = primes.length;
+		while (index == primes.length) {
+			index = (int) Math.floor(r.nextDouble() * index);
+		}
+		return primes[index];
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/util/StateWriter.java b/tlatools/org.lamport.tlatools/src/tlc2/util/StateWriter.java
index 3efc5a0754bf1efc43998448b0e7b2c35aa9c8d2..3fa221aae26781b7b74b78f616c35245cc9cf9da 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/util/StateWriter.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/util/StateWriter.java
@@ -103,7 +103,7 @@ public class StateWriter implements IStateWriter
 	 * @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) {
+			Visualization visualization) {
     	if (successorStateIsNew) {
     		this.writeState(state);
     	}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/IValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/IValue.java
index 30e7a4addfc2052935bc77e8b80301130db20f94..9cd58be1a375d13483f1a24b2a7a6609779c599b 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/IValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/IValue.java
@@ -28,6 +28,7 @@ package tlc2.value;
 import java.io.IOException;
 
 import tla2sany.semantic.SemanticNode;
+import tlc2.tool.TLCState;
 import tlc2.tool.coverage.CostModel;
 import tlc2.value.impl.BoolValue;
 import tlc2.value.impl.IntValue;
@@ -136,4 +137,5 @@ public interface IValue extends Comparable<Object> {
 		return true;
 	}
 
+	TLCState toState();
 }
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/ValueInputStream.java b/tlatools/org.lamport.tlatools/src/tlc2/value/ValueInputStream.java
index a2a80d4624be158c50e6b641a3beebb908f02326..6873034217a1980771c3d3d64e1515243af8759d 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/ValueInputStream.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/ValueInputStream.java
@@ -5,6 +5,7 @@ package tlc2.value;
 import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Map;
 
 import tlc2.TLCGlobals;
@@ -27,13 +28,18 @@ public final class ValueInputStream implements ValueConstants, IValueInputStream
 
   private final BufferedDataInputStream dis;
   private final HandleTable handles;
-
-  public ValueInputStream(File file, final boolean compressed) throws IOException 
+  
+  public ValueInputStream(InputStream in) throws IOException 
   {
       // SZ Feb 24, 2009: FileUtil refactoring
-    this.dis = FileUtil.newBdFIS(compressed, file);
+    this.dis = new BufferedDataInputStream(in);
     this.handles = new HandleTable();
   }
+
+  public ValueInputStream(File file, final boolean compressed) throws IOException 
+  {
+	  this(FileUtil.newBdFIS(compressed, file));
+  }
   
   public ValueInputStream(File file) throws IOException 
   {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/ValueOutputStream.java b/tlatools/org.lamport.tlatools/src/tlc2/value/ValueOutputStream.java
index f4b56b4da9fbfa2f3bc350de0f72d64a9cd74295..ffaa36e9d5f1e362d3fcc5def2bbf4f4f8aa3117 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/ValueOutputStream.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/ValueOutputStream.java
@@ -22,12 +22,16 @@ public final class ValueOutputStream implements IValueOutputStream {
   }
 
   public ValueOutputStream(File file, final boolean compress) throws IOException {
+	  this(new FileOutputStream(file), compress);
+  }
+  
+  public ValueOutputStream(final OutputStream out, final boolean compress) throws IOException {
     if (compress) {
-      OutputStream os = new GZIPOutputStream(new FileOutputStream(file));
+      OutputStream os = new GZIPOutputStream(out);
       this.dos = new BufferedDataOutputStream(os);
     }
     else {
-      this.dos = new BufferedDataOutputStream(file);
+      this.dos = new BufferedDataOutputStream(out);
     }
     this.handles = new HandleTable();
   }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Enumerable.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Enumerable.java
index 2622b6a8f93c05bee0189c7ab298c99932edee0b..22739aa89b7a16df0126df697449b6bc797d96c8 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Enumerable.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Enumerable.java
@@ -15,7 +15,14 @@ public interface Enumerable extends IValue {
 		 * The normalized order is the order of elements when a Value gets
 		 * fingerprinted (@see {@link Value#fingerPrint(long)}.
 		 */
-		NORMALIZED
+		NORMALIZED,
+		/**
+		 * A randomized ordering of the elements returned by this enumerator. Contrary
+		 * to the other orderings, the enumerator is *not* guaranteed to enumerate all
+		 * elements but only enumerates all of them with increasing probability
+		 * eventually.
+		 */
+		RANDOMIZED
     }
 	
   @Override
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/EnumerableValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/EnumerableValue.java
index 86586301a1e8549df487709e53bd3c1d62ed2e42..2312e1fe153f50d81acbec255f904a77716a1454 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/EnumerableValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/EnumerableValue.java
@@ -27,9 +27,16 @@
 
 package tlc2.value.impl;
 
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.math3.primes.Primes;
 
 import tlc2.tool.FingerprintException;
+import tlc2.util.RandomGenerator;
 import tlc2.value.RandomEnumerableValues;
 
 public abstract class EnumerableValue extends Value implements Enumerable {
@@ -75,6 +82,8 @@ public abstract class EnumerableValue extends Value implements Enumerable {
 			if (enumerated != null) {
 				return ((Enumerable) enumerated.normalize()).elements();
 			}
+		} else if (ordering == Ordering.RANDOMIZED) {
+			return elements(size());
 		}
 		return elements();
 	}
@@ -104,40 +113,59 @@ public abstract class EnumerableValue extends Value implements Enumerable {
 
 	abstract class SubsetEnumerator implements ValueEnumeration {
 
-		protected final long x;
-		protected final int a;
+		// n is the upper bound for the range for which nextIndex returns values.
 		protected final int n;
+
+		// k is the number of times nextIndex can be called.
 		protected final int k;
+		// i counts the number of calls.
 		protected int i;
+		
+		// The seed, X, index, ...
+		private int index; // X_i or seed
+		// Multiplier (long because intermediate values in nextIndex can exceed Int.MAX_VALUE)
+		protected long a;
+		// Modulo
+		private int m;
+		// Increment
+		private int c;
 
 		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 {
+			if (n <= 0) {
+				// For n < 1, hasNext is always going to return false.
+				this.n = 0;
 				this.k = 0;
-				this.a = 0; // RANDOM.nextInt(0) causes IllegalArgumentException.
+				return;
 			}
+			
+			this.n = n;
+			this.k = k;
+
+			// Calculating optimal parameters for the given n is expensive! We assume that
+			// we will only have to calculate parameters for a small number of ns per
+			// model-checker run.
+			int[] vals = MULTIPLIERS.computeIfAbsent(n, j -> computeOptimalMandA(j));
+			this.m = vals[0];
+			this.a = vals[1];
+			
+			final Random random = RandomEnumerableValues.get();
+			this.index = random.nextInt(n);
+			// Choose a prime for c that is guaranteed to be co-prime with m. We randomly
+			// choose a prime number on every invocation to better approximate a uniform
+			// distribution for repeated evaluation of Randomization!RandomSubset(k, S) with
+			// k and S fixed to some values.  Since TLC cheats and evaluates RandomSubset
+			// simply by generating k indices in 0..|S|, repeatedly evaluating RandomSubset
+			// will reveal a bias because -even though the individual indices are uniform-
+			// the *sequences* of indices generated are not uniformly distributed. A more
+			// costly implementation that's based on generating the indices of all ksubsets
+			// of S and SubsetValue#getUnrank(k) would be robust against a static increment
+			// c.  For S = 1..20 and k = 3, this implementation is roughly 2.5x better in
+			// approximating a uniform distribution yet also 2.5x slower.
+			this.c = RandomGenerator.nextPrime(random);
 		}
 
 		@Override
@@ -158,14 +186,101 @@ public abstract class EnumerableValue extends Value implements Enumerable {
 				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;
+			do {
+				index = (int) ((this.a * index + this.c) % this.m);
+			} while (index >= this.n);
+			i++;
+			assert 0 <= index && index < this.n;
 			return index;
 		}
 
 		@Override
 		public abstract Value nextElement();
 	}
+	
+	// Consider bootstrapping the parameters for the upper range of Integers (where
+	// prime factorization becomes more expensive)?
+	private static Map<Integer, int[]> MULTIPLIERS = new ConcurrentHashMap<>();
+
+	// https://en.wikipedia.org/wiki/Linear_congruential_generator#c_%E2%89%A0_0
+	// When c # 0, correctly chosen parameters allow a period equal to m, for all seed values. This will occur iff:
+	// m and c are relatively prime,
+	// a-1 is divisible by all prime factors of m
+	// a-1 is divisible by 4 if m is divisible by 4.
+	static int[] computeOptimalMandA(int n) {
+		if (n < 9) {
+			// while loop will increment n to 9 for all values lower than 9 anyway.
+			n = 9;
+		}
+
+		// Prime factorization is expensive!!! As a minor optimization, we could in-line
+		// primeFactor and use counters and track the product while looping instead of
+		// storing all primes in a list, comparing its size to the set, and calculating
+		// the product of the set.  However, I don't want to spend the time to extract
+		// Apache Commons Math's primeFactors implementation.
+		List<Integer> primeFactors = Primes.primeFactors(n);
+		while (n % 4 == 0 || new HashSet<>(primeFactors).size() == primeFactors.size()) {
+			n = n + 1;
+			primeFactors = Primes.primeFactors(n);
+		}
+
+		int a = 1;
+		for (Integer prime : new HashSet<>(primeFactors)) {
+			a *= prime;
+		}
+		a += 1;
+		
+		// Unfortunately, Java doesn't have tuples/pairs.
+		return new int[] {n, a};
+	}
 }
+
+/*
+---- CONFIG ksubsets_random_subset ----
+CONSTANTS 
+    Elements={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}
+    Limit=1000
+SPECIFICATION   Spec
+====
+
+------------------------------ MODULE ksubsets_random_subset ------------------------------
+EXTENDS Naturals, Randomization, FiniteSets, TLC
+
+CONSTANT Elements,
+         Limit
+
+VARIABLES counts,
+          total
+
+vars == <<counts, total >>
+
+AddSubset ==
+    /\ total < Limit 
+    /\ \E ss \in { RandomSubset(3, Elements) } :
+        /\ IF ss \in DOMAIN counts
+            THEN counts' = [counts EXCEPT ![ss] = @ + 1]
+            ELSE counts' = counts @@ (ss :> 1)
+        /\ total' = total + 1
+
+PrintDist ==
+    /\ total = Limit
+    /\ total' = Limit + 1
+    /\ UNCHANGED <<counts>>
+    /\ \A ss \in DOMAIN counts : PrintT(<<total, ss, counts[ss]>>)
+    /\ PrintT(<<"RESULT", Cardinality(DOMAIN counts)>>)
+
+Init == 
+    /\ counts = [ss \in {} |-> 0]
+    /\ total = 0
+
+Next ==
+    \/ AddSubset
+    \/ PrintDist
+
+Spec == Init /\ [][Next]_vars  
+
+=============================================================================
+\* Modification History
+\* Last modified Tue Oct 27 17:24:00 CET 2020 by jvanlightly
+\* Created Tue Oct 27 09:55:35 CET 2020 by jvanlightly
+*/
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnLambdaValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnLambdaValue.java
index 364c0e89130796b6250c7ae7505a23cc5ca72d1e..3ad2364d64b2c80961e49dedac8b8fdd62083d05 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnLambdaValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnLambdaValue.java
@@ -243,7 +243,7 @@ public class FcnLambdaValue extends Value implements Applicable, IFcnLambdaValue
                 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");
+                        Values.ppr(elems[argn].toString()) + "\nwhich is not in the function's domain " + this.getDomain().toString() +".\n");
                 }
                 c1 = c1.cons(ids[j], elems[argn++]);
               }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnRcdValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnRcdValue.java
index b8c18ab21d7f6fa86631c40377f931931fb13e61..2eb26935a77a01d75b3dd0f805634b8850e6ee52 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnRcdValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/FcnRcdValue.java
@@ -12,6 +12,7 @@ import java.util.Map;
 
 import tlc2.tool.EvalControl;
 import tlc2.tool.FingerprintException;
+import tlc2.tool.TLCState;
 import tlc2.tool.coverage.CostModel;
 import tlc2.util.FP64;
 import tlc2.value.IFcnRcdValue;
@@ -572,8 +573,17 @@ public class FcnRcdValue extends Value implements Applicable, IFcnRcdValue {
       if (coverage) {cm.incSecondary(this.values.length);}
       return new RecordValue(vars, this.values, this.isNormalized(), cm);
   }
+  
+  @Override
+  public TLCState toState() {
+	  final Value rcd = toRcd();
+	  if (rcd != null) {
+		  return rcd.toState();
+	  }
+	  return super.toState();
+  }
 
-  	@Override
+    @Override
 	public final Value toFcnRcd() {
 		return this;
 	}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/IntervalValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/IntervalValue.java
index f6b47041fa28d3baa84b87fd5633c9cbeb083f34..55ebb6e2b6a5b56cf50b2e8b32defd7a97b99916 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/IntervalValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/IntervalValue.java
@@ -13,6 +13,7 @@ import tlc2.util.FP64;
 import tlc2.value.IMVPerm;
 import tlc2.value.IValue;
 import tlc2.value.IValueOutputStream;
+import tlc2.value.RandomEnumerableValues;
 import tlc2.value.Values;
 import util.Assert;
 
@@ -380,4 +381,10 @@ implements Enumerable, Reducible {
 			}
 		};
 	}
+
+	public Value randomElement() {
+	     int sz = size();
+	     int index = (int) Math.floor(RandomEnumerableValues.get().nextDouble() * sz);
+	     return elementAt(index);
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/KSubsetValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/KSubsetValue.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb790285382014a2c3f5b61c3038dfc784683393
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/KSubsetValue.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * 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 tlc2.tool.coverage.CostModel;
+
+public class KSubsetValue extends SubsetValue {
+
+	private final int k;
+
+	public KSubsetValue(int k, Value set) {
+		super(set);
+		this.k = k;
+	}
+
+	public KSubsetValue(int k, Value set, CostModel cm) {
+		super(set, cm);
+		this.k = k;
+	}
+
+	@Override
+	public ValueEnumeration elements() {
+		// Remember k as a member and return SubsetValue's kElement enumerator here.
+		return kElements(k);
+	}
+
+	@Override
+	public ValueEnumeration elements(Ordering ordering) {
+		if (ordering == Ordering.RANDOMIZED) {
+			return new RandomSubsetGenerator(k);
+		}
+		return super.elements(ordering);
+	}
+}
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/RecordValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/RecordValue.java
index 18279c7558fc97045cee77a0a37f0691ade1ff92..f704da8873d5bf28cf176a0cb48d222cef23c8cc 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/RecordValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/RecordValue.java
@@ -10,11 +10,14 @@ import java.io.EOFException;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
+import java.util.Set;
 
 import tla2sany.semantic.OpDeclNode;
+import tla2sany.semantic.SymbolNode;
 import tlc2.output.EC;
 import tlc2.output.MP;
 import tlc2.tool.FingerprintException;
+import tlc2.tool.StateVec;
 import tlc2.tool.TLCState;
 import tlc2.tool.coverage.CostModel;
 import tlc2.util.FP64;
@@ -232,8 +235,7 @@ public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0],
   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()) + ".");
+        Assert.fail("Attempted to access record by a non-string argument: " + Values.ppr(arg.toString()));
       }
       UniqueString name = ((StringValue)arg).getVal();
       int rlen = this.names.length;
@@ -242,8 +244,8 @@ public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0],
           return this.values[i];
         }
       }
-      Assert.fail("Attempted to apply the record\n" + Values.ppr(this.toString()) +
-      "\nto nonexistent record field " + name + ".");
+      Assert.fail("Attempted to access nonexistent field '" + name +
+          "' of record\n" + Values.ppr(this.toString()));
       return null;    // make compiler happy
     }
     catch (RuntimeException | OutOfMemoryError e) {
@@ -271,8 +273,7 @@ public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0],
   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()) + ".");
+        Assert.fail("Attempted to access record by a non-string argument: " + Values.ppr(arg.toString()));
       }
       UniqueString name = ((StringValue)arg).getVal();
       int rlen = this.names.length;
@@ -591,17 +592,130 @@ public static final RecordValue EmptyRcd = new RecordValue(new UniqueString[0],
 	}
 
 	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]);
+			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 new PrintTLCState(this, state);
+		}
+
+		private static final class PrintTLCState extends TLCState {
+
+			private final RecordValue rcd;
+			private final TLCState state;
+
+			public PrintTLCState(RecordValue recordValue, final TLCState state) {
+				this.rcd = recordValue;
+				this.state = state;
+			}
+
+			@Override
+			public String toString() {
+				final StringBuffer result = new StringBuffer();
+				int vlen = rcd.names.length;
+				if (vlen == 1) {
+					result.append(rcd.names[0].toString());
+					result.append(" = ");
+					result.append(Values.ppr(rcd.values[0]));
+					result.append("\n");
+				} else {
+					for (int i = 0; i < vlen; i++) {
+						UniqueString key = rcd.names[i];
+						result.append("/\\ ");
+						result.append(key.toString());
+						result.append(" = ");
+						result.append(Values.ppr(rcd.values[i]));
+						result.append("\n");
+					}
+				}
+				return result.toString();
+			}
+
+			@Override
+			public int hashCode() {
+				return this.state.hashCode();
+			}
+
+			@Override
+			public boolean equals(Object obj) {
+				return this.state.equals(obj);
+			}
+
+			@Override
+			public long fingerPrint() {
+				return this.state.fingerPrint();
+			}
+
+			@Override
+			public boolean allAssigned() {
+				return this.state.allAssigned();
+			}
+
+			@Override
+			public String toString(TLCState lastState) {
+				return this.state.toString(lastState);
+			}
+
+			@Override
+			public TLCState bind(UniqueString name, IValue value) {
+				return this.state.bind(name, value);
+			}
+
+			@Override
+			public TLCState bind(SymbolNode id, IValue value) {
+				return this.state.bind(id, value);
+			}
+
+			@Override
+			public TLCState unbind(UniqueString name) {
+				return this.state.unbind(name);
+			}
+
+			@Override
+			public IValue lookup(UniqueString var) {
+				return this.state.lookup(var);
+			}
+
+			@Override
+			public boolean containsKey(UniqueString var) {
+				return this.state.containsKey(var);
+			}
+
+			@Override
+			public TLCState copy() {
+				return this.state.copy();
+			}
+
+			@Override
+			public TLCState deepCopy() {
+				return this.state.deepCopy();
+			}
+
+			@Override
+			public StateVec addToVec(StateVec states) {
+				return this.state.addToVec(states);
+			}
+
+			@Override
+			public void deepNormalize() {
+				this.state.deepNormalize();
+			}
+
+			@Override
+			public Set<OpDeclNode> getUnassigned() {
+				return this.state.getUnassigned();
+			}
+
+			@Override
+			public TLCState createEmpty() {
+				return this.state.createEmpty();
+			}
 		}
-		return state;
-	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SetEnumValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SetEnumValue.java
index 0d0419ddeb2969bbcb1d88799a1de6d58f775c71..a5eea2f50e0eae26f2447a7e81553011ab6e2d09 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SetEnumValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SetEnumValue.java
@@ -294,6 +294,15 @@ public static final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, tr
   public final Value toSetEnum() {
 	  return this;
   }
+  
+  // Unclear if overriding Value#toTuple would cause regressions (test suite
+  // doesn't reveal one, but let's be safe.
+  public final Value toTupleValue() {
+	  // Remove duplicates.
+	  this.normalize();
+	  // Order of elements left undefined (implementation detail).
+	  return new TupleValue(this.elems.toArray());
+  }
 
   @Override
   public final boolean isDefined() {
@@ -443,7 +452,7 @@ public static final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, tr
     int index = 0;
 
     public Enumerator() {
-      normalize();
+    	normalize();
     }
 
     @Override
@@ -472,6 +481,14 @@ public static final SetEnumValue DummyEnum = new SetEnumValue((ValueVec)null, tr
     	return new SetEnumValue(vec, false, cm);
 	}
 
+	@Override
+	public ValueEnumeration elements(Ordering ordering) {
+		if (ordering == Ordering.RANDOMIZED) {
+			return elements(size());
+		}
+		return super.elements(ordering);
+	}
+
 	@Override
 	public ValueEnumeration elements(final int k) {
 		normalize();
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.java
index c3e3d1776a155673ec5feffbca08468520903666..feb1e979ab68b5f56c859704717e85e37c6ffc92 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.java
@@ -683,17 +683,34 @@ public class SubsetValue extends EnumerableValue implements Enumerable {
 			this.elems.sort(true);
 			return this;
 		}
+		
+		@Override
+		public SetEnumValue asSet() {
+			final ValueVec vv = new ValueVec(numKSubsetElems);
+			Value elem;
+			while ((elem = nextElement()) != null) {
+				vv.addElement(elem);
+			}
+			return new SetEnumValue(vv, false);
+		}
+	}
+
+	public final Value kSubset(int k) {
+		return kElements(k).asSet();
 	}
 	
 	@Override
 	public ValueEnumeration elements(final Ordering ordering) {
+		if (ordering == Ordering.RANDOMIZED) {
+			return ((SetEnumValue) toSetEnum()).elements(ordering);
+		}
 		// Use elementsNormalized regardless of requested ordering. Even for ordering
 		// UNDEFINED, elementsNormalized is fastest.
 		return elements();
 	}
 
   @Override
-  public final ValueEnumeration elements() {
+  public ValueEnumeration elements() {
     try {
       if (this.pset == null || this.pset == SetEnumValue.DummyEnum) {
     	  // See note on SetEnumValue#convert for SubsetValue wrt
@@ -866,4 +883,84 @@ public class SubsetValue extends EnumerableValue implements Enumerable {
 			return numOfPicks;
 		}
 	}
+
+	// This enumerator violates the expected behavior of ValueEnumeration to
+	// terminate once all elements have been enumerated, which implies that it also
+	// returns duplicates.  Its advantage over the other, stateful enumerators -that
+	// guarantee termination- is that it is very cheap.  If we ever need this, a
+	// terminating enumerator can be implemented with SubsetValue#getUnrank combined
+	// with EnumerableValue.SubsetValueEnumerator, i.e. optimally parameterizing an
+	// LCG with period m = NcK (n choose k) where NcK = n!/k!(n-k)! to pseudo-randomly
+	// generate all values (indices) in the range [0, NcK)) and generating the 
+	// subset for each index with Unrank#subsetAt.
+	// ASSUME tlc2.tool.impl.Tool.PROBABLISTIC = TRUE
+	public class RandomSubsetGenerator implements ValueEnumeration {
+		
+		private final int k;
+		private final Random random;
+		private final SetEnumValue s;
+		private int n;
+
+		public RandomSubsetGenerator(final int k) {
+			this.k = k;
+			
+			this.s = (SetEnumValue) set.toSetEnum();
+			this.s.normalize();
+			this.n = this.s.elems.size();
+
+			if (k < 0 || k > n) {
+				throw new IllegalArgumentException(String.format("k=%s and n=%s", k, n));
+			}
+			
+			this.random = RandomEnumerableValues.get();
+		}
+		
+		@Override
+		public void reset() {
+			// This enumerator is stateless and, thus, reset is a no-op.
+		}
+
+		@Override
+		public Value nextElement() {
+			// This is Algorithm S introduced in volume 2 "Seminumerical Algorithms" in
+			// Knuth's TAOCP. It iterates over the elements in S once while gradually
+			// increasing the probability p of including an element at the current index
+			// in the result. Compare with reservoir sampling discussed in section 3.4.2 of
+			// TAOCP.
+			// For more sophisticated algorithms see:
+			// https://dl.acm.org/doi/10.1145/358105.893
+			// https://dl.acm.org/doi/10.1145/23002.23003
+			// https://dl.acm.org/doi/10.1145/3147.3165
+
+			final ValueVec vec = new ValueVec(k);
+
+            for (int t = 0; t < n; t++) {
+				final double p = (k - vec.size()) / ((n - t) * 1d);
+
+				if (random.nextDouble() <= p) {
+					vec.addElement(s.elems.elementAt(t));
+				}
+				
+				if (vec.size() == k) {
+					break;
+				}
+			}
+
+			// This variant reduces the number of calls to the random number generator by
+			// allocating a bit set to remember the previously drawn elements.
+//			final BitSet bs = new BitSet(n);
+//			while (vec.size() < k) {
+//				final int i = random.nextInt(n);
+//				if (!bs.get(i)) {
+//					bs.set(i);
+//					vec.addElement(s.elems.elementAt(i));
+//				}
+//			}
+
+			// This assertion holds because even with an imaginary Random#nextDouble that
+			// returns 1 the probability p of the last k values will be 1.
+            assert vec.size() == k;
+			return new SetEnumValue(vec, false);
+		}
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.tla b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.tla
index d87d33e83d9e7f80782c17354762326bd719635b..3f2afed3b09245d8ac6c5bbb05246a37421b8711 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.tla
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/SubsetValue.tla
@@ -104,9 +104,10 @@ ASSUME /\ \A nn \in N: nn \in Nat
 -----------------------------------------------------------------------------
 \* Invariant and helpers:
 
+\* SeqOfSeqsToSetOfSets(<< <<1>>, <<1,2>>, <<2,3,3>> >>) evals to {{1}, {1, 2}, {2, 3}}
 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
+                                 Image(f) == ImageApply(f, LAMBDA x : x) \* Lambda is just identity
                              IN ImageApply(seq, Image)
 
 (* The set S of all k-subsets ks (Cardinality(ks) = k) for the range 0 to n - 1. 
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/TupleValue.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/TupleValue.java
index d0020e6098ce1d91712967e84a13971c7d931242..1433abd734e997d0f3144f2eaddc1205e185b200 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/TupleValue.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/TupleValue.java
@@ -126,12 +126,12 @@ public class TupleValue extends Value implements Applicable, ITupleValue {
   public final Value apply(Value arg, int control) {
     try {
       if (!(arg instanceof IntValue)) {
-        Assert.fail("Attempted to apply tuple to a non-integer argument.");
+        Assert.fail("Attempted to access tuple at a non integral index: " + Values.ppr(arg.toString()));
       }
       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.");
+        Assert.fail("Attempted to access index " + idx + " of tuple\n"
+            + Values.ppr(this.toString()) + "\nwhich is out of bounds.");
       }
       return (Value) this.elems[idx-1];
     }
@@ -145,7 +145,7 @@ public class TupleValue extends Value implements Applicable, ITupleValue {
   public final Value apply(Value[] args, int control) {
     try {
       if (args.length != 1) {
-        Assert.fail("Attetmpted to apply tuple with wrong number of arguments.");
+        Assert.fail("Attempted to access tuple with " + args.length + " arguments when it expects 1.");
       }
       return this.apply(args[0], EvalControl.Clear);
     }
@@ -159,8 +159,7 @@ public class TupleValue extends Value implements Applicable, ITupleValue {
   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()) + ".");
+        Assert.fail("Attempted to access tuple at a non integral index: " + Values.ppr(arg.toString()));
       }
       int idx = ((IntValue)arg).val;
       if (idx > 0 && idx <= this.elems.length) {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Value.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Value.java
index e9b07185d7ff6591f77fa7f4152c5265f8a766bb..dfffab4cd0c50e383ce356dd5b06c21f59fe3127 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Value.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/Value.java
@@ -12,6 +12,7 @@ import java.io.Serializable;
 import tla2sany.semantic.SemanticNode;
 import tlc2.TLCGlobals;
 import tlc2.tool.FingerprintException;
+import tlc2.tool.TLCState;
 import tlc2.tool.coverage.CostModel;
 import tlc2.util.FP64;
 import tlc2.value.IMVPerm;
@@ -318,6 +319,10 @@ public abstract class Value implements ValueConstants, Serializable, IValue {
 	  return null;
   }
   
+  public TLCState toState() {
+	  return null;
+  }
+  
   /* The string representation of this value */
   @Override
   public final String toString() {
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/ValueEnumeration.java b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/ValueEnumeration.java
index fc4d77947c3c249bb37c3fa89aebe4d09e5d9c96..dd3662e7265e779c44c7bf9a637928f91346c47e 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/value/impl/ValueEnumeration.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/value/impl/ValueEnumeration.java
@@ -31,4 +31,13 @@ public interface ValueEnumeration {
 			action.accept(elem);
 		}
 	}
+	
+	default SetEnumValue asSet() {
+		final ValueVec vv = new ValueVec();
+		Value elem;
+		while ((elem = nextElement()) != null) {
+			vv.addElement(elem);
+		}
+		return new SetEnumValue(vv, false);
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/util/FileUtil.java b/tlatools/org.lamport.tlatools/src/util/FileUtil.java
index 401984fb906e7e686996c08a9cd7082def0adb6a..4fdcb6fb6725a568b185969c93a7ead4486c6e31 100644
--- a/tlatools/org.lamport.tlatools/src/util/FileUtil.java
+++ b/tlatools/org.lamport.tlatools/src/util/FileUtil.java
@@ -485,9 +485,44 @@ public class FileUtil
         return new DataOutputStream(new FileOutputStream(new File(filename)));
     }
 
-
-
-
-
-
+	public static File createTempFile(final String fileName) {
+		final File file;
+		// Create the temp file in Java's temp dir unless TLC's metaDir has been set. The
+		// latter won't be the case when SANY is invoked directly or during the early
+		// startup phase of TLC.
+		if (TLCGlobals.metaDir != null) {
+			file = new File(TLCGlobals.metaDir + separatorChar + fileName);
+		} else {
+			final String tDir = System.getProperty("java.io.tmpdir");
+			file = new File(tDir + separatorChar + fileName);
+		}
+		// Let's get rid of the file when TLC terminates.
+		file.deleteOnExit();
+		return file;
+	}
+	
+	
+	/**
+	 * 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)
+	 * 
+	 * @return the count of bytes copied
+	 */
+	public 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;
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/src/util/MonolithSpecExtractor.java b/tlatools/org.lamport.tlatools/src/util/MonolithSpecExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..597d3ccdce17964f98ff1c9b391bc875830fd0da
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/src/util/MonolithSpecExtractor.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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 util;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+
+public class MonolithSpecExtractor {
+
+	public static String getConfig(final String configFile) {
+		if (configFile.endsWith(TLAConstants.Files.TLA_EXTENSION)) {
+			return configFile;
+		}
+		return configFile + TLAConstants.Files.CONFIG_EXTENSION;
+	}
+	
+	// config and module are almost identical except for what they return.
+	// The config is a plain (Java) InputStream, but the module has to be a
+	// TLA+ *NamedInputStream*, which is a wrapper and not a subclass of
+	// InputStream. Method config also filters out the config start and end markers
+	// while module keeps them. Other than that, they both loop over the input line
+	// by line and extract the lines in between the start and end marker.
+	
+	public static InputStream config(final InputStream in, final String configName) throws IOException {
+		try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
+			String config = "";
+
+			String line = "";
+			while ((line = reader.readLine()) != null) {
+				if (!config.isEmpty() && line.matches("====.*")) {
+					break;
+				}
+				if (config.isEmpty() && line.matches("-----*\\s*CONFIG\\s+" + configName + "\\s*-----*")) {
+					config += " "; // activate.
+					continue; // skip to next line/don't include marker.
+				}
+				if (!config.isEmpty()) {
+					config += line + "\n";
+				}
+			}
+			return new ByteArrayInputStream(config.trim().getBytes(Charset.forName("UTF-8")));
+		}
+	}
+	
+	public static NamedInputStream module(final File in, final String moduleName)
+			throws IOException {
+		final File out = FileUtil.createTempFile(moduleName + TLAConstants.Files.TLA_EXTENSION);
+		final PrintWriter pw = new PrintWriter(new FileWriter(out));
+		try (BufferedReader reader = new BufferedReader(new FileReader(in))) {
+			boolean active = false;
+
+			String line = "";
+			while ((line = reader.readLine()) != null) {
+				if (active && line.matches("====.*")) {
+					pw.println(line); // include end marker.
+					break;
+				}
+				if (!active && line.matches("-----*\\s*MODULE\\s+" + moduleName + "\\s*----*")) {
+					active = true;
+					pw.println(line); // include start marker.
+					continue;
+				}
+				if (active) {
+					pw.println(line);
+				}
+			}
+			pw.close();
+			if (!active) {
+				// Not found in uber-spec.
+				return null;
+			}
+			return new NamedInputStream(out.getName(), moduleName, out);
+		}
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/util/TLAConstants.java b/tlatools/org.lamport.tlatools/src/util/TLAConstants.java
index a8a4aaa1cae79d4bdd00da8a50ab447be472bf3a..32542d005e2346d38bc5ff1c134b30e2f9e46b50 100644
--- a/tlatools/org.lamport.tlatools/src/util/TLAConstants.java
+++ b/tlatools/org.lamport.tlatools/src/util/TLAConstants.java
@@ -47,6 +47,7 @@ public final class TLAConstants {
 	    public static final String TRUE = "TRUE";
 	    public static final String UNION = "\\union";
 	    public static final String VARIABLE = "VARIABLE";
+	    public static final String ENABLED = "ENABLED";
 	}
 	
 	public final class LoggingAtoms {
@@ -65,13 +66,16 @@ public final class TLAConstants {
 		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 POST_CONDITION_SCHEME = "postcondition";
+		public static final String ALIAS_SCHEME = "alias";
 		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 TRACE_EXPRESSION_MODULE_NAME = "TTrace";
+	    public static final String ERROR_STATES_MODULE_NAME = "TraceDef";
 	    public static final String EXPLORATION_MODULE_NAME = "TE";
 		public static final String ACTION = "_TEAction";
 		public static final String POSITION = "_TEPosition";
@@ -95,6 +99,8 @@ public final class TLAConstants {
 	     * The tuple of ordered sub-action names representing the trace states. 
 	     */
 	    public static final String TRACE_EXPLORE_ACTION_CONSTRAINT = "traceExploreActionConstraint";
+	    public static final String SPEC_TE_TRACE_EXPRESSION = "TraceExpression";
+	    public static final String SPEC_TE_TTRACE_EXPRESSION = "TTraceExpression";
 	}
 
 	
@@ -115,7 +121,8 @@ public final class TLAConstants {
     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 COMMENT_NS = "\\*";
+    public static final String COMMENT = COMMENT_NS + SPACE;
     public static final String ATTRIBUTE = "@";
     public static final String COLON = ":";
     public static final String EMPTY_STRING = "";
@@ -135,6 +142,7 @@ public final class TLAConstants {
     public static final String R_PAREN = ")";
     public static final String L_SQUARE_BRACKET = "[";
     public static final String R_SQUARE_BRACKET = "]";
+    public static final String FALSE = "FALSE";
 
     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;
diff --git a/tlatools/org.lamport.tlatools/test-model/Alias.tla b/tlatools/org.lamport.tlatools/test-model/Alias.tla
new file mode 100644
index 0000000000000000000000000000000000000000..e44db49f37b2f11ad10c8588940b7560cebc8ed9
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/Alias.tla
@@ -0,0 +1,119 @@
+---- CONFIG Alias ----
+SPECIFICATION FairSpec
+INVARIANT Inv
+PROPERTY Prop
+ALIAS Alias
+======================
+
+---- MODULE Alias ----
+EXTENDS Integers, TLC, Sequences
+
+VARIABLES x, y
+vars == <<x,y>>
+
+Next == /\ y' = ~y
+        /\ \/ /\ x < 4
+              /\ x' = x + 1
+           \/ /\ x = 4
+              /\ x' = 1
+
+Spec == x = 1 /\ y = FALSE /\ [][Next]_vars
+
+FairSpec == Spec /\ WF_vars(Next)
+
+Inv == x < 4
+
+Prop == <>(x > 4)
+
+Animation(e1,e2) == "e1: " \o ToString(e1) \o " e2: " \o ToString(e2)
+
+\* An expression that is evaluated on the pair of states
+\* s and t and printed in place of s in an error trace.
+\* Useful to:
+\* - filter out variables (not shown below)
+\* - rename variables
+\* - map variables (e.g. animation)
+\* - additional trace expressions
+\* TLC ignores the original state if the evaluation of
+\* Alias fails.
+Alias == [y |-> y, \* x and y reordered.
+          x |-> x, 
+          a |-> x' - x, 
+          b |-> x' = x,
+          anim |-> Animation(x, y), \* Animation
+          te |-> ENABLED Next]      \* Trace Expression
+
+=======================
+\* FairSpec => []Inv
+Error: Invariant Inv is violated.
+Error: The behavior up to this point is:
+State 1: <Initial predicate>
+/\ y = FALSE
+/\ x = 1
+/\ a = 1
+/\ b = FALSE
+
+State 2: <Action line 11, col 34 to line 11, col 46 of module Alias>
+/\ y = TRUE
+/\ x = 2
+/\ a = 1
+/\ b = FALSE
+
+State 3: <Action line 11, col 34 to line 11, col 46 of module Alias>
+/\ y = FALSE
+/\ x = 3
+/\ a = 1
+/\ b = FALSE
+
+State 4: <Action line 11, col 34 to line 11, col 46 of module Alias>
+/\ y = TRUE
+/\ x = 4
+/\ a = 0     \* Stuttering
+/\ b = TRUE  \* Stuttering
+
+-------------------------------
+\* Spec => Prop
+Error: Temporal properties were violated.
+Error: The following behavior constitutes a counter-example:
+
+State 1: <Initial predicate>
+/\ y = FALSE
+/\ x = 1
+/\ a = 0     \* Stuttering
+/\ b = TRUE  \* Stuttering
+
+State 2: Stuttering
+
+-------------------------------
+\* FairSpec => Prop
+Error: Temporal properties were violated.
+Error: The following behavior constitutes a counter-example:
+
+State 1: <Initial predicate>
+/\ y = FALSE
+/\ x = 1
+/\ a = 1
+/\ b = FALSE
+
+State 2: <Next line 17, col 9 to line 21, col 23 of module Alias>
+/\ y = TRUE
+/\ x = 2
+/\ a = 1
+/\ b = FALSE
+
+State 3: <Next line 17, col 9 to line 21, col 23 of module Alias>
+/\ y = FALSE
+/\ x = 3
+/\ a = 1
+/\ b = FALSE
+
+State 4: <Next line 17, col 9 to line 21, col 23 of module Alias>
+/\ y = TRUE
+/\ x = 4
+/\ a = -3      \* State 1 is successor
+/\ b = FALSE   \* State 1 is successor
+
+Back to state 1: <Next line 17, col 9 to line 21, col 23 of module Alias>
+
+-------------------------------
+
diff --git a/tlatools/org.lamport.tlatools/test-model/AliasLasso.cfg b/tlatools/org.lamport.tlatools/test-model/AliasLasso.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..88de5650a1132243dcce6c9e231772ba9912ff96
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/AliasLasso.cfg
@@ -0,0 +1,3 @@
+SPECIFICATION FairSpec
+PROPERTY Prop
+ALIAS Alias
diff --git a/tlatools/org.lamport.tlatools/test-model/AliasStuttering.cfg b/tlatools/org.lamport.tlatools/test-model/AliasStuttering.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e20cfff3ac1555b7995ca1639d21823e92b840e6
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/AliasStuttering.cfg
@@ -0,0 +1,3 @@
+SPECIFICATION Spec
+PROPERTY Prop
+ALIAS Alias
diff --git a/tlatools/org.lamport.tlatools/test-model/DieHardTLA.tla b/tlatools/org.lamport.tlatools/test-model/DieHardTLA.tla
index 78d44d483e7540b4de6476503dd9a8c316c66eba..60fb35b1609479ef885dab870bca3ca71bd79922 100644
--- a/tlatools/org.lamport.tlatools/test-model/DieHardTLA.tla
+++ b/tlatools/org.lamport.tlatools/test-model/DieHardTLA.tla
@@ -1,5 +1,5 @@
 ------------------------------ MODULE DieHardTLA ------------------------------
-EXTENDS Integers
+EXTENDS Integers, TLC
 
 VARIABLES small, big   
           
@@ -8,12 +8,16 @@ TypeOK == /\ small \in 0..3
 
 Init == /\ big   = 0 
         /\ small = 0
+        /\ TLCSet(42, 0)
+
 
 FillSmall == /\ small' = 3 
              /\ big'   = big
+             /\ TLCSet(42, TLCGet(42) + 3)
 
 FillBig == /\ big'   = 5 
            /\ small' = small
+             /\ TLCSet(42, TLCGet(42) + 5)
 
 EmptySmall == /\ small' = 0 
               /\ big'   = big
@@ -40,5 +44,23 @@ Next == \/ FillSmall
         \/ SmallToBig    
         \/ BigToSmall    
         
-Spec == Init /\ [][Next]_<<small, big>>        
+Spec == Init /\ [][Next]_<<small, big>>   
+
+\* Active because of TYPE_CONSTRAINT in corresponding .cfg file.
+PostCondition ==
+  \* Evaluating the spec takes 97 states of which 16 are distinct.  The
+  \* diameter of the spec is 8.
+  /\ TLCGet("generated") = 97
+  /\ TLCGet("distinct") = 16
+  /\ TLCGet("diameter") = 8
+  \* Total gallons of water used by TLC to check the spec is 128. Not sure
+  \* if this is considered an acceptable ecological footprint. 
+  /\ \/ TLCGet(42) = 128
+     \/ Print(TLCGet(42), FALSE)
+  \* On average, for each distinct state of the spec, TLC pours 8 gallons
+  \* of water from the fountain. To check if TLC correctly reports the 
+  \* violationg of this property, we expect it to be 23.
+  /\ \/ (TLCGet(42) \div TLCGet("distinct")) = 23
+     \/ Print(TLCGet(42) \div TLCGet("distinct"), FALSE)
+
 =============================================================================
diff --git a/tlatools/org.lamport.tlatools/test-model/DieHardTLAPA.cfg b/tlatools/org.lamport.tlatools/test-model/DieHardTLAPA.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..c615e886a9b4dd903956b98e54f1300750710a6e
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/DieHardTLAPA.cfg
@@ -0,0 +1,4 @@
+SPECIFICATION
+Spec
+POSTCONDITION
+PostCondition
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/test-model/Github358.tla b/tlatools/org.lamport.tlatools/test-model/Github358.tla
new file mode 100644
index 0000000000000000000000000000000000000000..6ff29f99fc11a89475e8cc951347f1fd56c87c33
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/Github358.tla
@@ -0,0 +1,7 @@
+------------------------------ MODULE Github358 ----------------------------- 
+(*--algorithm Github358
+begin
+    A::
+      skip;
+end algorithm; *)
+=============================================================================
diff --git a/tlatools/org.lamport.tlatools/test-model/Github461.cfg b/tlatools/org.lamport.tlatools/test-model/Github461.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..e5a3896997d57e27da8ca619e152212920e137d1
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/Github461.cfg
@@ -0,0 +1,4 @@
+INIT
+Init
+NEXT
+Next
diff --git a/tlatools/org.lamport.tlatools/test-model/Github461.tla b/tlatools/org.lamport.tlatools/test-model/Github461.tla
new file mode 100644
index 0000000000000000000000000000000000000000..58beab5b7e764bbfe3bbae7bf902923eec77a7b8
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test-model/MonolithSpec.tla b/tlatools/org.lamport.tlatools/test-model/MonolithSpec.tla
new file mode 100644
index 0000000000000000000000000000000000000000..c55a0e4b69a0d5e1c878b1e6c622d6342e2bcde3
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/MonolithSpec.tla
@@ -0,0 +1,199 @@
+------------- CONFIG  MonolithSpec  ---------
+
+CONSTANT
+N <- Const
+
+SPECIFICATION
+Spec
+
+INVARIANT
+TypeOK
+Inv
+===================
+
+---- MODULE MonolithSpec ----
+EXTENDS EWD840, TLC
+
+Const == 
+2
+----
+=============================================================================
+
+------------------------------- MODULE Mod4711 ------------------------------
+Color == {"white", "black"}
+=============================================================================
+
+------------------------------- MODULE Mod4712 ------------------------------
+Bar == TRUE
+=============================================================================
+
+------------------------------- MODULE EWD840 -------------------------------
+(***************************************************************************)
+(* TLA+ specification of an algorithm for distributed termination          *)
+(* detection on a ring, due to Dijkstra, published as EWD 840:             *)
+(* Derivation of a termination detection algorithm for distributed         *)
+(* computations (with W.H.J.Feijen and A.J.M. van Gasteren).               *)
+(***************************************************************************)
+EXTENDS Naturals, Mod4711
+
+INSTANCE Mod4712
+ASSUME Bar
+
+CONSTANT N
+ASSUME NAssumption == N \in Nat \ {0}
+
+VARIABLES active, color, tpos, tcolor
+
+Nodes == 0 .. N-1
+
+TypeOK ==
+  /\ active \in [Nodes -> BOOLEAN]    \* status of nodes (active or passive)
+  /\ color \in [Nodes -> Color]       \* color of nodes
+  /\ tpos \in Nodes                   \* token position
+  /\ tcolor \in Color                 \* token color
+
+(***************************************************************************)
+(* Initially the token is black. The other variables may take any          *)
+(* "type-correct" values.                                                  *)
+(***************************************************************************)
+Init ==
+  /\ active \in [Nodes -> BOOLEAN]
+  /\ color \in [Nodes -> Color]
+  /\ tpos \in Nodes
+  /\ tcolor = "black"
+
+(***************************************************************************)
+(* Node 0 may initiate a probe when it has the token and when either it is *)
+(* black or the token is black. It passes a white token to node N-1 and    *)
+(* paints itself white.                                                    *)
+(***************************************************************************)
+InitiateProbe ==
+  /\ tpos = 0
+  /\ tcolor = "black" \/ color[0] = "black"
+  /\ tpos' = N-1
+  /\ tcolor' = "white"
+  /\ active' = active
+  /\ color' = [color EXCEPT ![0] = "white"]
+
+(***************************************************************************)
+(* A node i different from 0 that possesses the token may pass it to node  *)
+(* i-1 under the following circumstances:                                  *)
+(*   - node i is inactive or                                               *)
+(*   - node i is colored black or                                          *)
+(*   - the token is black.                                                 *)
+(* Note that the last two conditions will result in an inconclusive round, *)
+(* since the token will be black. The token will be stained if node i is   *)
+(* black, otherwise its color is unchanged. Node i will be made white.     *)
+(***************************************************************************)
+PassToken(i) == 
+  /\ tpos = i
+  /\ ~ active[i] \/ color[i] = "black" \/ tcolor = "black"
+  /\ tpos' = i-1
+  /\ tcolor' = IF color[i] = "black" THEN "black" ELSE tcolor
+  /\ active' = active
+  /\ color' = [color EXCEPT ![i] = "white"]
+
+(***************************************************************************)
+(* token passing actions controlled by the termination detection algorithm *)
+(***************************************************************************)
+System == InitiateProbe \/ \E i \in Nodes \ {0} : PassToken(i)
+
+(***************************************************************************)
+(* An active node i may activate another node j by sending it a message.   *)
+(* If j>i (hence activation goes against the direction of the token being  *)
+(* passed), then node i becomes black.                                     *)
+(***************************************************************************)
+SendMsg(i) ==
+  /\ active[i]
+  /\ \E j \in Nodes \ {i} :
+        /\ active' = [active EXCEPT ![j] = TRUE]
+        /\ color' = [color EXCEPT ![i] = IF j>i THEN "black" ELSE @]
+  /\ UNCHANGED <<tpos, tcolor>>
+
+(***************************************************************************)
+(* Any active node may become inactive at any moment.                      *)
+(***************************************************************************)
+Deactivate(i) ==
+  /\ active[i]
+  /\ active' = [active EXCEPT ![i] = FALSE]
+  /\ UNCHANGED <<color, tpos, tcolor>>
+
+(***************************************************************************)
+(* actions performed by the underlying algorithm                           *)
+(***************************************************************************)
+Environment == \E i \in Nodes : SendMsg(i) \/ Deactivate(i)
+
+(***************************************************************************)
+(* next-state relation: disjunction of above actions                       *)
+(***************************************************************************)
+Next == System \/ Environment
+
+vars == <<active, color, tpos, tcolor>>
+
+Spec == Init /\ [][Next]_vars /\ WF_vars(System)
+
+-----------------------------------------------------------------------------
+
+(***************************************************************************)
+(* Non-properties, useful for validating the specification with TLC.       *)
+(***************************************************************************)
+TokenAlwaysBlack == tcolor = "black"
+
+NeverChangeColor == [][ UNCHANGED color ]_vars
+
+(***************************************************************************)
+(* Main safety property: if there is a white token at node 0 then every    *)
+(* node is inactive.                                                       *)
+(***************************************************************************)
+terminationDetected ==
+  /\ tpos = 0 /\ tcolor = "white"
+  /\ color[0] = "white" /\ ~ active[0]
+
+TerminationDetection ==
+  terminationDetected => \A i \in Nodes : ~ active[i]
+
+(***************************************************************************)
+(* Liveness property: termination is eventually detected.                  *)
+(***************************************************************************)
+Liveness ==
+  (\A i \in Nodes : ~ active[i]) ~> terminationDetected
+
+(***************************************************************************)
+(* The following property asserts that when every process always           *)
+(* eventually terminates then eventually termination will be detected.     *)
+(* It does not hold since processes can wake up each other.                *)
+(***************************************************************************)
+FalseLiveness ==
+  (\A i \in Nodes : []<> ~ active[i]) ~> terminationDetected
+
+(***************************************************************************)
+(* The following property says that eventually all nodes will terminate    *)
+(* assuming that from some point onwards no messages are sent. It is       *)
+(* not supposed to hold: any node may indefinitely perform local           *)
+(* computations. However, this property is verified if the fairness        *)
+(* condition WF_vars(Next) is used instead of only WF_vars(System) that    *)
+(* requires fairness of the actions controlled by termination detection.   *)
+(***************************************************************************)
+SpecWFNext == Init /\ [][Next]_vars /\ WF_vars(Next)
+AllNodesTerminateIfNoMessages ==
+  <>[][\A i \in Nodes : ~ SendMsg(i)]_vars => <>(\A i \in Nodes : ~ active[i])
+
+(***************************************************************************)
+(* Dijkstra's inductive invariant                                          *)
+(***************************************************************************)
+Inv == 
+  \/ P0:: \A i \in Nodes : tpos < i => ~ active[i]
+  \/ P1:: \E j \in 0 .. tpos : color[j] = "black"
+  \/ P2:: tcolor = "black"
+
+(***************************************************************************)
+(* Use the following specification to let TLC check that the predicate     *)
+(* TypeOK /\ Inv is inductive for EWD 840: verify that it is an            *)
+(* (ordinary) invariant of a specification obtained by replacing the       *)
+(* initial condition by that conjunction.                                  *)
+(***************************************************************************)
+CheckInductiveSpec == TypeOK /\ Inv /\ [][Next]_vars
+=============================================================================
+\* Modification History
+\* Last modified Tue Jun 28 18:17:45 CEST 2016 by merz
+\* Created Mon Sep 09 11:33:10 CEST 2013 by merz
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..f2fa932d5faaac5af250ec8b177dfd48ec1827aa
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDeadlockTest.tla
@@ -0,0 +1,20 @@
+---------- MODULE TESpecDeadlockTest -----------
+
+EXTENDS Naturals
+
+VARIABLES x, y
+
+Init ==
+    /\ x = 0
+    /\ y = FALSE
+
+Next ==
+    /\ x <= 3
+    /\ x' = x + 1
+    /\ y' = ~y
+
+Spec ==
+    /\ Init
+    /\ [][Next]_<<x, y>>
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8e6f101ad4fc5327ef7ccba4adf688f4ea86f65b
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.cfg
@@ -0,0 +1,2 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..294cc195bd668295b2740590c34ab134fbafedf7
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecDependencyTest.tla
@@ -0,0 +1,19 @@
+---------- MODULE TESpecDependencyTest -----------
+
+EXTENDS Naturals, DoesNotExist
+
+VARIABLE x
+
+Init == x = 0
+
+Next ==
+    \/  /\ x < 3
+        /\ x' = x + 1
+    \/  /\ x >= 3
+        /\ UNCHANGED x
+
+Spec ==
+    /\ Init
+    /\ [][Next]_<<x>>
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecEqAliasSafetyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecEqAliasSafetyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a6d42f72a4684f06db9d269e84f93b44e5dd98b0
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecEqAliasSafetyTest.cfg
@@ -0,0 +1,4 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+INVARIANT Safety
+ALIAS EqAlias
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecLassoTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecLassoTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..dac58d0edc4c25aafdc0e631f386ed3d7387ac68
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecLassoTest.cfg
@@ -0,0 +1,3 @@
+CONSTANT Limit = 3
+SPECIFICATION LiveSpec
+PROPERTY Liveness
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecMapAliasSafetyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecMapAliasSafetyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b20c4cab508f90b300f0b3a855311c1916fb43bf
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecMapAliasSafetyTest.cfg
@@ -0,0 +1,4 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+INVARIANT Safety
+ALIAS MapAlias
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8e6f101ad4fc5327ef7ccba4adf688f4ea86f65b
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.cfg
@@ -0,0 +1,2 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..cfa05d92812b75153321e75dc1b23b4cf9beea73
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecNoErrorTest.tla
@@ -0,0 +1,19 @@
+---------- MODULE TESpecNoErrorTest -----------
+
+EXTENDS Naturals
+
+VARIABLE x
+
+Init == x = 0
+
+Next ==
+    \/  /\ x < 3
+        /\ x' = x + 1
+    \/  /\ x >= 3
+        /\ UNCHANGED x
+
+Spec ==
+    /\ Init
+    /\ [][Next]_<<x>>
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..4d07be4bdd383dc53e46a5cbb701afa9dba3c30d
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..2e3bba0f1c99e35c324687f837f988465b252daf
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecPlusCalTest.tla
@@ -0,0 +1,31 @@
+---------- MODULE TESpecPlusCalTest -----------
+
+EXTENDS Naturals, TLC
+
+(* --algorithm Steps
+variables x = 1, y = FALSE;
+begin
+    while TRUE do
+        x := x + 1;
+        y := ~y;
+        assert x < 5;
+    end while;
+end algorithm; *)
+\* BEGIN TRANSLATION (chksum(pcal) = "2f1a7a64" /\ chksum(tla) = "93b619e3")
+VARIABLES x, y
+
+vars == << x, y >>
+
+Init == (* Global variables *)
+        /\ x = 1
+        /\ y = FALSE
+
+Next == /\ x' = x + 1
+        /\ y' = ~y
+        /\ Assert(x' < 5, "Failure of assertion at line 11, column 9.")
+
+Spec == Init /\ [][Next]_vars
+
+\* END TRANSLATION 
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8e6f101ad4fc5327ef7ccba4adf688f4ea86f65b
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.cfg
@@ -0,0 +1,2 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..b58cc2d90777f5dcb15d58f0cb341b196c18694c
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecRuntimeErrorTest.tla
@@ -0,0 +1,19 @@
+---------- MODULE TESpecRuntimeErrorTest -----------
+
+EXTENDS Naturals
+
+VARIABLE x
+
+Init == x = [val |-> 0]
+
+Next ==
+    \/  /\ x.val < 3
+        /\ x' = [val |-> x.val + 1]
+    \/  /\ x.val >= 3
+        /\ x' = [val2 |-> x.val + 1]
+
+Spec ==
+    /\ Init
+    /\ [][Next]_<<x>>
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSafetyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSafetyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b3bc389653ecd0dc3a37e4dd4123feaede40fe83
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSafetyTest.cfg
@@ -0,0 +1,3 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+INVARIANT Safety
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecStutteringTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecStutteringTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d005a3b1f3da96e9870b1bf999a705fcf9467644
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecStutteringTest.cfg
@@ -0,0 +1,3 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+PROPERTY Liveness
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSubAliasSafetyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSubAliasSafetyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0206922363937b88d162e6079c656412c8aeb958
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSubAliasSafetyTest.cfg
@@ -0,0 +1,4 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+INVARIANT Safety
+ALIAS SubsetAlias
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSuperAliasSafetyTest.cfg b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSuperAliasSafetyTest.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..7bcf9cfb188cbe9754f5c985d9adf711ac90c6f2
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecSuperAliasSafetyTest.cfg
@@ -0,0 +1,4 @@
+CONSTANT Limit = 3
+SPECIFICATION Spec
+INVARIANT Safety
+ALIAS SupersetAlias
diff --git a/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecTest.tla b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecTest.tla
new file mode 100644
index 0000000000000000000000000000000000000000..47938e71d8cbe07a2f2847a1bd9e62add86e16ee
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/TESpecTest/TESpecTest.tla
@@ -0,0 +1,59 @@
+---------- MODULE TESpecTest -----------
+
+EXTENDS Naturals
+
+CONSTANT Limit
+
+ASSUME Limit \in Nat
+
+VARIABLES x, y
+
+Safety ==
+    /\ x /= Limit
+
+Liveness ==
+    /\ <>(x = Limit)
+
+Init ==
+    /\ x = 0
+    /\ y = FALSE
+
+Increment ==
+    /\ x' = x + 1
+    /\ y' = ~y
+
+Decrement ==
+    /\ x > 0
+    /\ x' = x - 1
+    /\ y' = ~y
+
+Next ==
+    \/ Increment
+    \/ Decrement
+
+Spec ==
+    /\ Init
+    /\ [][Next]_<<x, y>>
+
+LiveSpec ==
+    /\ Init
+    /\ [][Next]_<<x, y>>
+    /\ WF_<<x, y>>(Increment)
+
+EqAlias ==
+    [x |-> x,
+    y |-> y]
+
+MapAlias ==
+    [x |-> x + 1,
+    y |-> ~y]
+
+SupersetAlias ==
+    [x |-> x,
+    y |-> y,
+    z |-> ~y]
+
+SubsetAlias ==
+    [x |-> x]
+
+==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/Test2.cfg b/tlatools/org.lamport.tlatools/test-model/Test2.cfg
index 81a6daf14e2af3f6791d1ba40216e25ff51f737a..90bf38485d50166ac843f95317c4f67206ca2f86 100644
--- a/tlatools/org.lamport.tlatools/test-model/Test2.cfg
+++ b/tlatools/org.lamport.tlatools/test-model/Test2.cfg
@@ -4,4 +4,6 @@ Spec
 \* PROPERTY definition
 PROPERTY
 Prop1
+POSTCONDITION
+PostCondition
 \* Generated on Wed Mar 25 21:34:22 CET 2015
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/test-model/Test2.tla b/tlatools/org.lamport.tlatools/test-model/Test2.tla
index 97e8c69e993212bc12fe78badb86499958de9833..c5283204ddb50fa2e4b9f2a3e09886c0a4b203b3 100644
--- a/tlatools/org.lamport.tlatools/test-model/Test2.tla
+++ b/tlatools/org.lamport.tlatools/test-model/Test2.tla
@@ -2,14 +2,21 @@
 
 (* This spec originates from an email conversation between Leslie and Yuan in 2009 *)
 
-EXTENDS Naturals
+EXTENDS Naturals, TLC
 VARIABLE x
 
-Init == x=0
+Init == x=0 /\ TLCSet(42, 0)
 
-Next == x' = (x+1)%5
+Next == x' = (x+1)%5 /\ TLCSet(42, TLCGet(42)+1)
 
 Spec == Init /\ [][Next]_x /\ WF_x(Next)
 
 Prop1 == []<>(x=1)
+
+PostCondition ==
+    TLCGet(42) = 1050
+
+PostConditionViolated ==
+    TLCGet(42) = 0
+
 ==============================================
diff --git a/tlatools/org.lamport.tlatools/test-model/Test2PostCondition.cfg b/tlatools/org.lamport.tlatools/test-model/Test2PostCondition.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..8d6ed26cd01c516cedae4305d768125d330ec90d
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/Test2PostCondition.cfg
@@ -0,0 +1,9 @@
+\* SPECIFICATION definition
+SPECIFICATION
+Spec
+\* PROPERTY definition
+PROPERTY
+Prop1
+POSTCONDITION
+PostConditionViolated
+\* Generated on Wed Mar 25 21:34:22 CET 2015
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/test-model/Test4.cfg b/tlatools/org.lamport.tlatools/test-model/Test4.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..81bbfad60f51f7a0061d092d12270f050a4ec618
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test-model/Test4.cfg
@@ -0,0 +1 @@
+SPECIFICATION Spec
\ No newline at end of file
diff --git a/tlatools/org.lamport.tlatools/test-model/Test4.tla b/tlatools/org.lamport.tlatools/test-model/Test4.tla
new file mode 100644
index 0000000000000000000000000000000000000000..15affe77f9f9ac7f57fda1f6555a7aff3df30cec
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test-model/simulation/NQSpec/NQSpec.tla b/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/NQSpec.tla
index df9696352aff3633975edc6a91e70401dc27feca..7affcc75a616acf5e1f1fd441f5eb4fbafc2da0d 100644
--- a/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/NQSpec.tla
+++ b/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/NQSpec.tla
@@ -1,5 +1,5 @@
 ------------------------------- MODULE NQSpec -------------------------------
-EXTENDS Integers, Sequences
+EXTENDS Integers, Sequences, TLC
 
 CONSTANTS EnQers, DeQers, Data, InitData, Ids, Busy, NoData
 
@@ -25,7 +25,9 @@ TypeOK == /\ enq \in [EnQers -> Data \cup {Done}]
           /\ after \in [elts -> SUBSET doneElts]
           /\ adding \in [EnQers -> Elements \cup {NotAnElement}]
 
-Init == /\ enq = [e \in EnQers |-> Done]
+Init ==         
+        /\ TLCSet(42, 1)
+        /\ enq = [e \in EnQers |-> Done]
         /\ deq = [d \in DeQers |-> InitData]
         /\ after = << >>
         /\ adding = [e \in EnQers |-> NotAnElement]
diff --git a/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/QSpec.tla b/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/QSpec.tla
index ad2b1b3b8f3e06053d29ae5eeb5624781c125e67..4d3a7962b5121e4e4d08f855a99c682921f1fad8 100644
--- a/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/QSpec.tla
+++ b/tlatools/org.lamport.tlatools/test-model/simulation/NQSpec/QSpec.tla
@@ -56,7 +56,9 @@ EndDeq(d) == /\ deq[d] = Busy
              /\ deq' = [deq EXCEPT ![d] = deqInner[d]]
              /\ UNCHANGED <<enq, queue, enqInner, deqInner>>
              
-Next == \/ \E e \in EnQers : BeginEnq(e) \/ DoEnq(e) \/ EndEnq(e)
+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
diff --git a/tlatools/org.lamport.tlatools/test/pcal/DivergenceTest.java b/tlatools/org.lamport.tlatools/test/pcal/DivergenceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6db76aebaafc8ba75f24406a93fcb53dfb75a97
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test/pcal/Github358.java b/tlatools/org.lamport.tlatools/test/pcal/Github358.java
new file mode 100644
index 0000000000000000000000000000000000000000..c7851681e2c19132e359b4d1a64518eacdd5d602
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test/pcal/ValidatorPerformance.java b/tlatools/org.lamport.tlatools/test/pcal/ValidatorPerformance.java
deleted file mode 100644
index a84a6900004255dadcc1d9b9ed5f129a85fe32ee..0000000000000000000000000000000000000000
--- a/tlatools/org.lamport.tlatools/test/pcal/ValidatorPerformance.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package pcal;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import tlc2.tool.CommonTestCase;
-
-public class ValidatorPerformance {
-	private static final String SPEC_FILE = CommonTestCase.BASE_PATH + "PlusCalOptions.tla";
-	
-	@Test
-	public void testTimings() throws IOException {
-		final long trimmedStart = System.currentTimeMillis();
-		for (int i = 0; i < 1000; i++) {
-			try (final FileInputStream fis = new FileInputStream(SPEC_FILE)) {
-				Validator.validate(fis);
-			}
-		}
-		final long trimmedEnd = System.currentTimeMillis();
-
-		Validator.PRE_TRIM_VALIDATION_CONTENT = false;
-		final long untrimmedStart = System.currentTimeMillis();
-		for (int i = 0; i < 1000; i++) {
-			try (final FileInputStream fis = new FileInputStream(SPEC_FILE)) {
-				Validator.validate(fis);
-			}
-		}
-		final long untrimmedEnd = System.currentTimeMillis();
-
-		final long trimmedDelta = trimmedEnd - trimmedStart;
-		final long untrimmedDelta = untrimmedEnd - untrimmedStart;
-		System.out.println("Trimmed run time was " + trimmedDelta + " and untrimmed was " + untrimmedDelta);
-		Assert.assertTrue("Untrimmed processing time (" + untrimmedDelta +") ran faster than trimmed (" + trimmedDelta + ").",
-						  (trimmedDelta < untrimmedDelta));
-	}
-}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/REPLTest.java b/tlatools/org.lamport.tlatools/test/tlc2/REPLTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3743336be271b8f97275cda66a04af7d22c83542
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/REPLTest.java
@@ -0,0 +1,54 @@
+package tlc2;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class REPLTest {
+
+    @Test
+    public void testProcessInput() throws IOException {
+        Path tempDir = Files.createTempDirectory("repltest");
+        final REPL repl = new REPL(tempDir);
+        String res;
+
+        // Numeric expressions.
+        res = repl.processInput("2+2");
+        assertEquals("4", res);
+        res = repl.processInput("4-2");
+        assertEquals("2", res);
+        res = repl.processInput("10 \\div 2");
+        assertEquals("5", res);
+
+        // Set expressions.
+        res = repl.processInput("{1,2} \\X {3,4}");
+        assertEquals("{<<1, 3>>, <<1, 4>>, <<2, 3>>, <<2, 4>>}", res);
+        res = repl.processInput("{1,2} \\cup {3,4}");
+        assertEquals("{1, 2, 3, 4}", res);
+        res = repl.processInput("{1,2} \\cap {2,3}");
+        assertEquals("{2}", res);
+
+        // Tuple expressions.
+        res = repl.processInput("Append(<<1,2>>, 3)");
+        assertEquals("<<1, 2, 3>>", res);
+        res = repl.processInput("Append(3, <<1,2>>)"); // error.
+        assertEquals("", res);
+        res = repl.processInput("Tail(<<1,2,3>>)");
+        assertEquals("<<2, 3>>", res);
+        res = repl.processInput("Head(<<1,2,3>>)");
+        assertEquals("1", res);
+        res = repl.processInput("<<1,2>> \\o <<3>>");
+        assertEquals("<<1, 2, 3>>", res);
+
+        // Other invalid expressions.
+        res = repl.processInput("invalid");
+        assertEquals("", res);
+        res = repl.processInput("123abc");
+        assertEquals("", res);
+    }
+
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TestMPRecorder.java b/tlatools/org.lamport.tlatools/test/tlc2/TestMPRecorder.java
index 54dc1c6be99d516b45b8cfac45eac2c0fcdbedb5..13e5fb9acdaf400dc2fe199cad32d2f68dbfb905 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/TestMPRecorder.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TestMPRecorder.java
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
 
 import tlc2.output.EC;
 
-public class TestMPRecorder extends tlc2.output.MPRecorder {
+public class TestMPRecorder implements tlc2.output.IMessagePrinterRecorder {
 	private final Map<Integer, List<Object>> records = new HashMap<Integer, List<Object>>();
 	
 	public void record(int code, Object... objects) {
@@ -147,8 +147,10 @@ public class TestMPRecorder extends tlc2.output.MPRecorder {
 		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));
+		final List<Object> con = getRecordsOrDefault(EC.TLC_COVERAGE_CONSTRAINT, new ArrayList<>(0));
 		init.addAll(next);
 		init.addAll(prop);
+		init.addAll(con);
 
 		return init.stream().map(o -> (String[]) o).map(a -> new Coverage(a)).filter(Coverage::isAction)
 				.collect(Collectors.toList());
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecDeadlockTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecDeadlockTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..25d295d6c6acd6756ae1c2e416d4d61d0e6baf76
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecDeadlockTest.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import tla2sany.semantic.ExternalModuleTable;
+import tlc2.output.EC;
+import tlc2.tool.Action;
+import tlc2.tool.StateVec;
+import tlc2.tool.impl.SpecProcessor;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Vect;
+import tlc2.value.IValue;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.RecordValue;
+import util.TLAConstants;
+import util.UniqueString;
+
+public class TraceExpressionSpecDeadlockTest extends TraceExpressionSpecTest {
+
+	private static final String TE_SPEC_TEST = "TESpecDeadlockTest";
+
+	public TraceExpressionSpecDeadlockTest() {
+		super(TE_SPEC_TEST, "TESpecDeadlockTest.cfg", "-modelcheck", EC.ExitStatus.VIOLATION_DEADLOCK);
+	}
+
+	@Override
+	protected boolean checkDeadLock() {
+		return true;
+	}
+
+	@Override
+	protected void doTest(final Tool tool, final String id) {
+		final SpecProcessor specProcessor = tool.getSpecProcessor();
+
+		Action[] actions = tool.getActions();
+		assertEquals(1, actions.length);
+
+		// Assert that one invariant exists.
+		final Action[] invariants = specProcessor.getInvariants();
+		assertEquals(1, invariants.length);
+
+		// Assert there exists one init-predicate
+		Vect<Action> initPred = specProcessor.getInitPred();
+		assertEquals(1, initPred.size());
+
+		// Assert there exists a next-state relation
+		assertNotNull(specProcessor.getNextPred());
+
+		// Assert the trace
+		StateVec sv = tool.getInitStates();
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		Map<UniqueString, IValue> vals = sv.first().getVals();
+		assertEquals(IntValue.gen(0), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(1), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValTrue, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(2), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(3), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValTrue, vals.get(UniqueString.of("y")));
+
+		assertNotNull(tool.getModelConfig().getAlias());
+
+		//TODO: We want the spec to deadlock and not violate an artificial invariant.
+//		assertTrue(tool.getModelConfig().getCheckDeadlock());
+		
+		// Assert that all three sub-modules exist
+		final ExternalModuleTable moduleTbl = specProcessor.getModuleTbl();
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TE_SPEC_TEST)));
+		assertNotNull(moduleTbl.getModuleNode(
+				UniqueString.of(TE_SPEC_TEST + "_" + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME)));
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME)));
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecLassoTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecLassoTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bc956f94adda11beacd433b4df1b3954103341c
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecLassoTest.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Map;
+
+import tla2sany.semantic.ExternalModuleTable;
+import tlc2.output.EC;
+import tlc2.tool.Action;
+import tlc2.tool.StateVec;
+import tlc2.tool.TLCState;
+import tlc2.tool.impl.SpecProcessor;
+import tlc2.tool.impl.Tool;
+import tlc2.tool.liveness.LiveCheck1;
+import tlc2.tool.liveness.LiveException;
+import tlc2.util.SetOfStates;
+import tlc2.util.Vect;
+import tlc2.value.IValue;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import util.TLAConstants;
+import util.UniqueString;
+
+public class TraceExpressionSpecLassoTest extends TraceExpressionSpecTest {
+
+	private static final String TE_SPEC_TEST = "TESpecTest";
+
+	public TraceExpressionSpecLassoTest() {
+		super(TE_SPEC_TEST, "TESpecLassoTest.cfg", "-modelcheck", EC.ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	protected double getLivenessThreshold() {
+		return TLCGlobals.livenessThreshold; // don't change the default
+	}
+
+	@Override
+	protected void doTest(final Tool tool, final String id) throws Exception {
+		final SpecProcessor specProcessor = tool.getSpecProcessor();
+
+		Action[] actions = tool.getActions();
+		assertEquals(1, actions.length);
+
+		assertEquals(0, specProcessor.getInvariants().length);
+		
+		// Assert that one property exists.
+		final Action[] property = specProcessor.getImpliedTemporals();
+		assertEquals(1, property.length);
+
+		// Assert there exists one init-predicate
+		Vect<Action> initPred = specProcessor.getInitPred();
+		assertEquals(1, initPred.size());
+
+		// Assert there exists a next-state relation
+		assertNotNull(specProcessor.getNextPred());
+		
+		assertNotNull(tool.getModelConfig().getAlias());
+		assertFalse(tool.getModelConfig().getCheckDeadlock());
+		
+		// Assert that all three sub-modules exist
+		final ExternalModuleTable moduleTbl = specProcessor.getModuleTbl();
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TE_SPEC_TEST)));
+		assertNotNull(moduleTbl.getModuleNode(
+				UniqueString.of(TE_SPEC_TEST + "_" + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME)));
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME)));
+
+		final LiveCheck1 lc = new LiveCheck1(tool);
+		lc.init(tool, tool.getActions(), "states");
+		
+		// Assert the trace
+		StateVec sv = tool.getInitStates();
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		Map<UniqueString, IValue> vals = sv.first().getVals();
+		assertEquals(IntValue.gen(0), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+		lc.addInitState(tool, sv.first(), sv.first().fingerPrint());
+		TLCState cur = sv.first();
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(1), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValTrue, vals.get(UniqueString.of("y")));
+
+		SetOfStates nextStates = new SetOfStates(1);
+		nextStates.put(sv.first());
+		lc.addNextState(tool, cur, cur.fingerPrint(), nextStates);
+		cur = sv.first();
+		
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(0), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+
+		nextStates = new SetOfStates(1);
+		nextStates.put(sv.first());
+		lc.addNextState(tool, cur, cur.fingerPrint(), nextStates);
+		
+		try {
+			lc.finalCheck(tool);
+		} catch (LiveException e) {
+			return;
+		}
+		fail();
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecRuntimeTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecRuntimeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fc210400cd54a432ff03a12b3e70b26ffe3fa3bf
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecRuntimeTest.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import tla2sany.semantic.ExternalModuleTable;
+import tlc2.output.EC;
+import tlc2.tool.Action;
+import tlc2.tool.StateVec;
+import tlc2.tool.impl.SpecProcessor;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Vect;
+import tlc2.value.IValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.RecordValue;
+import util.TLAConstants;
+import util.UniqueString;
+
+public class TraceExpressionSpecRuntimeTest extends TraceExpressionSpecTest {
+
+	private static final String TE_SPEC_TEST = "TESpecRuntimeErrorTest";
+
+	public TraceExpressionSpecRuntimeTest() {
+		super(TE_SPEC_TEST, "TESpecRuntimeErrorTest.cfg", "-modelcheck", EC.ExitStatus.FAILURE_SPEC_EVAL);
+	}
+
+	@Override
+	protected void doTest(final Tool tool, final String id) {
+		final SpecProcessor specProcessor = tool.getSpecProcessor();
+
+		Action[] actions = tool.getActions();
+		assertEquals(1, actions.length);
+
+		// Assert that one invariant exists.
+		final Action[] invariants = specProcessor.getInvariants();
+		assertEquals(1, invariants.length);
+
+		// Assert there exists one init-predicate
+		Vect<Action> initPred = specProcessor.getInitPred();
+		assertEquals(1, initPred.size());
+
+		// Assert there exists a next-state relation
+		assertNotNull(specProcessor.getNextPred());
+
+		// Assert the trace
+		StateVec sv = tool.getInitStates();
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		Map<UniqueString, IValue> vals = sv.first().getVals();
+		assertEquals(new RecordValue(UniqueString.of("val"), IntValue.gen(0)), vals.get(UniqueString.of("x")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(new RecordValue(UniqueString.of("val"), IntValue.gen(1)), vals.get(UniqueString.of("x")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(new RecordValue(UniqueString.of("val"), IntValue.gen(2)), vals.get(UniqueString.of("x")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(new RecordValue(UniqueString.of("val"), IntValue.gen(3)), vals.get(UniqueString.of("x")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertFalse(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(new RecordValue(UniqueString.of("val2"), IntValue.gen(4)), vals.get(UniqueString.of("x")));
+
+		assertNotNull(tool.getModelConfig().getAlias());
+		assertFalse(tool.getModelConfig().getCheckDeadlock());
+		
+		// Assert that all three sub-modules exist
+		final ExternalModuleTable moduleTbl = specProcessor.getModuleTbl();
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TE_SPEC_TEST)));
+		assertNotNull(moduleTbl.getModuleNode(
+				UniqueString.of(TE_SPEC_TEST + "_" + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME)));
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME)));
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyBFSTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyBFSTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b68b5b5832c7b721432b972760c4956d852d4b9
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyBFSTest.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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;
+
+public class TraceExpressionSpecSafetyBFSTest extends TraceExpressionSpecSafetyTest {
+
+	public TraceExpressionSpecSafetyBFSTest() {
+		super("-modelcheck");
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/src/tlc2/output/MPRecorder.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetySimTest.java
similarity index 85%
rename from tlatools/org.lamport.tlatools/src/tlc2/output/MPRecorder.java
rename to tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetySimTest.java
index 4c5fd938a4aba70f4331b448eba99efd74686c17..b6bff1af7676957871bf3335bed595c583cfbe9a 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/output/MPRecorder.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetySimTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015 Microsoft Research. All rights reserved. 
+ * Copyright (c) 2020 Microsoft Research. All rights reserved. 
  *
  * The MIT License (MIT)
  * 
@@ -23,12 +23,11 @@
  * Contributors:
  *   Markus Alexander Kuppe - initial API and implementation
  ******************************************************************************/
+package tlc2;
 
-package tlc2.output;
+public class TraceExpressionSpecSafetySimTest extends TraceExpressionSpecSafetyTest {
 
-public class MPRecorder {
-
-	public void record(int code, Object... objects) {
-		// No op, subclasses may overwrite
+	public TraceExpressionSpecSafetySimTest() {
+		super("-simulate");
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..65e9e5feba02e24f241ca9572ff3bb6ad24f30b6
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecSafetyTest.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+
+import tla2sany.semantic.ExternalModuleTable;
+import tlc2.output.EC;
+import tlc2.tool.Action;
+import tlc2.tool.StateVec;
+import tlc2.tool.impl.SpecProcessor;
+import tlc2.tool.impl.Tool;
+import tlc2.util.Vect;
+import tlc2.value.IValue;
+import tlc2.value.impl.BoolValue;
+import tlc2.value.impl.IntValue;
+import util.TLAConstants;
+import util.UniqueString;
+
+public abstract class TraceExpressionSpecSafetyTest extends TraceExpressionSpecTest {
+
+	private static final String TE_SPEC_TEST = "TESpecTest";
+
+	public TraceExpressionSpecSafetyTest(final String mode) {
+		super(TE_SPEC_TEST, "TESpecSafetyTest.cfg", mode, EC.ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Override
+	protected void doTest(final Tool tool, final String id) {
+		final SpecProcessor specProcessor = tool.getSpecProcessor();
+
+		Action[] actions = tool.getActions();
+		assertEquals(1, actions.length);
+
+		// Assert that one invariant exists.
+		final Action[] invariants = specProcessor.getInvariants();
+		assertEquals(1, invariants.length);
+
+		// Assert there exists one init-predicate
+		Vect<Action> initPred = specProcessor.getInitPred();
+		assertEquals(1, initPred.size());
+
+		// Assert there exists a next-state relation
+		assertNotNull(specProcessor.getNextPred());
+
+		// Assert the trace
+		StateVec sv = tool.getInitStates();
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		Map<UniqueString, IValue> vals = sv.first().getVals();
+		assertEquals(IntValue.gen(0), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(1), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValTrue, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertTrue(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(2), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValFalse, vals.get(UniqueString.of("y")));
+
+		sv = tool.getNextStates(actions[0], sv.first());
+		assertEquals(1, sv.size());
+		assertTrue(tool.isGoodState(sv.first()));
+		assertTrue(tool.isInModel(sv.first()));
+		assertFalse(tool.isValid(invariants[0], sv.first()));
+		vals = sv.first().getVals();
+		assertEquals(IntValue.gen(3), vals.get(UniqueString.of("x")));
+		assertEquals(BoolValue.ValTrue, vals.get(UniqueString.of("y")));
+
+		assertNotNull(tool.getModelConfig().getAlias());
+		assertFalse(tool.getModelConfig().getCheckDeadlock());
+		
+		// Assert that all three sub-modules exist
+		final ExternalModuleTable moduleTbl = specProcessor.getModuleTbl();
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TE_SPEC_TEST)));
+		assertNotNull(moduleTbl.getModuleNode(
+				UniqueString.of(TE_SPEC_TEST + "_" + TLAConstants.TraceExplore.EXPLORATION_MODULE_NAME)));
+		assertNotNull(moduleTbl.getModuleNode(UniqueString.of(TLAConstants.TraceExplore.TRACE_EXPRESSION_MODULE_NAME
+				+ TLAConstants.TraceExplore.ERROR_STATES_MODULE_NAME)));
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecTest.java b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fcce9030c59845d5983491a1969a0b003a69309
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/TraceExpressionSpecTest.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;
+
+import java.util.Date;
+
+import org.junit.Test;
+
+import tlc2.tool.impl.FastTool;
+import tlc2.tool.impl.Tool;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+import util.FilenameToStream;
+import util.SimpleFilenameToStream;
+
+public abstract class TraceExpressionSpecTest extends ModelCheckerTestCase {
+
+	private static final String OUTPUT_PATH = "states"; // Rest of tests dump stuff here.
+
+	public TraceExpressionSpecTest(final String specName, final String configName, String mode, int exitStatus) {
+		super(specName, "TESpecTest",
+				new String[] { "-generateSpecTE", "-config", configName, "-teSpecOutDir", OUTPUT_PATH, mode },
+				exitStatus);
+	}
+
+	protected FilenameToStream getResolver() {
+		return new SimpleFilenameToStream(OUTPUT_PATH);
+	}
+
+	@Override
+	protected boolean doCoverage() {
+		return false;
+	}
+
+	@Override
+	protected boolean doDump() {
+		return false;
+	}
+
+	@Override
+	protected boolean noGenerateSpec() {
+		return false;
+	}
+
+	@Test
+	public void testSpec() throws Exception {
+		final Date timestamp = new Date(tlc.getStartTime());
+		final String traceSpecName = TraceExplorationSpec.deriveTESpecModuleName(spec, timestamp);
+		doTest(new FastTool(traceSpecName, traceSpecName, tlc.getResolver()), TraceExplorationSpec.teModuleId(timestamp));
+	}
+
+	protected abstract void doTest(Tool tool, final String id) throws Exception;
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/model/MCErrorTest.java b/tlatools/org.lamport.tlatools/test/tlc2/model/MCErrorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2f2e87bd5e7f6ceb15f4a19ec615f4c61508e080
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/model/MCErrorTest.java
@@ -0,0 +1,55 @@
+package tlc2.model;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class MCErrorTest {
+	
+	@Test
+	public void testGetErrorMessage()
+	{
+		final String expected = "this is an error message";
+		MCError actual = new MCError(expected);
+		assertEquals(expected, actual.getMessage());
+	}
+	
+	@Test
+	public void testUpdateStatesForTraceExpressions()
+	{
+		MCError error = new MCError();
+		MCState[] states = new MCState[] {
+				Utils.buildState(1, "init", "", "x = 1"),
+				Utils.buildState(2, "next", "", "x = 2"),
+				Utils.buildState(3, "next", "", "x = 3"),
+				Utils.buildState(5, "next", "", "x = 4"),
+				Utils.buildState(6, "next", "", "x = 5")
+		};
+		
+		for (MCState state : states)
+		{
+			error.addState(state);
+		}
+		
+		Map<String, String> map = new HashMap<String, String>();
+		map.put("x", "y");
+		error.updateStatesForTraceExpression(map);
+		
+		List<MCState> actualStates = error.getStates();
+		assertEquals(states.length, actualStates.size());
+		for (MCState actualState : actualStates)
+		{
+			MCVariable[] actualVariables = actualState.getVariables();
+			assertEquals(1, actualVariables.length);
+			for (MCVariable actualVariable : actualVariables)
+			{
+				assertEquals("x", actualVariable.getName());
+				assertEquals("y", actualVariable.getSingleLineDisplayName());
+			}
+		}
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/model/MCStateTest.java b/tlatools/org.lamport.tlatools/test/tlc2/model/MCStateTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..02e837489f6070fa19d185c1bb6a9fc0037e1d53
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/model/MCStateTest.java
@@ -0,0 +1,83 @@
+package tlc2.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import util.TLAConstants;
+
+public class MCStateTest {
+	
+	@Test
+	public void testParseRoundTrips()
+	{
+		testParseRoundTrip(1, "Initial predicate", "", "x = 8", "y = 7");
+		testParseRoundTrip(2, "YIncr", "line 8, col 10 to line 10, col 26 of module Bla", "x = 8", "y = 15");
+		testParseRoundTrip(1, "Initial predicate", "", "x = 1", "y = FALSE");
+		testParseRoundTrip(2, "Next", "line 7, col 9 to line 11, col 23 of module Alias", "x = 2", "y = TRUE");
+		testParseRoundTrip(3, "Next", "line 7, col 9 to line 11, col 23 of module Alias", "x = 3", "y = FALSE");
+		testParseRoundTrip(4, "Next", "line 7, col 9 to line 11, col 23 of module Alias", "x = 4", "y = TRUE");
+	}
+	
+	@Test
+	public void testSimpleRecordPrinter()
+	{
+		final MCState input = Utils.buildState(1, "Initial predicate", "", "x = 8", "y = 7");
+		final String actual = input.asSimpleRecord();
+		final String[] expectedTokens = new String[] {
+				TLAConstants.L_SQUARE_BRACKET,
+				"x", TLAConstants.RECORD_ARROW.trim(), "8",
+				TLAConstants.COMMA,
+				"y", TLAConstants.RECORD_ARROW.trim(), "7",
+				TLAConstants.R_SQUARE_BRACKET
+		};
+
+		String remaining = actual.trim();
+		for (String expectedToken : expectedTokens)
+		{
+			if (remaining.startsWith(expectedToken))
+			{
+				remaining = remaining.substring(expectedToken.length()).trim();
+			}
+			else
+			{
+				fail(String.format("Required token [%s]; received [%s]", expectedToken, remaining));
+			}
+		}
+	}
+	
+	private static void testParseRoundTrip(
+			int ordinal,
+			String name,
+			String location,
+			String ...assignments)
+	{
+		final MCState expected = Utils.buildState(ordinal, name, location, assignments);
+		final List<String> inputLines = Utils.toTlcOutputFormat(expected);
+		final String input = String.join(TLAConstants.CR, inputLines);
+		final MCState actual = MCState.parseState(input);
+		MCStateTest.compareStates(expected, actual);
+	}
+	
+	private static void compareStates(final MCState expected, final MCState actual)
+	{
+		assertEquals(expected.getName(), actual.getName());
+		// The label is not trimmed in parsing; we are maintaining backward compatibility
+		assertEquals(" " + expected.getLabel(), actual.getLabel());
+		assertEquals(expected.isStuttering(), actual.isStuttering());
+		assertEquals(expected.isBackToState(), actual.isBackToState());
+		assertEquals(expected.getStateNumber(), actual.getStateNumber());
+		
+		MCVariable[] expectedVars = expected.getVariables();
+		MCVariable[] actualVars = actual.getVariables();
+		assertEquals(expectedVars.length, actualVars.length);
+		for (int i = 0; i < expectedVars.length; i++)
+		{
+			assertEquals(expectedVars[i].getName(), actualVars[i].getName());
+			assertEquals(expectedVars[i].getValueAsString(), actualVars[i].getValueAsString());
+		}
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/model/Utils.java b/tlatools/org.lamport.tlatools/test/tlc2/model/Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e36194b40ed6739b37e54ccb5aecb657b0081d5
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/model/Utils.java
@@ -0,0 +1,54 @@
+package tlc2.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import tla2sany.st.Location;
+
+/**
+ * Some helper functions for dealing with various TLC model class types.
+ */
+public class Utils {
+
+	public static MCState buildState(
+			final int ordinal,
+			final String name,
+			final String location,
+			final String ...assignments)
+	{
+		MCVariable[] variables = new MCVariable[assignments.length];
+		for (int i = 0; i < assignments.length; i++)
+		{
+			String assignment = assignments[i];
+			String[] split = assignment.split("=");
+			variables[i] = new MCVariable(split[0].trim(), split[1].trim());
+		}
+		
+		return new MCState(
+				variables,
+				name,
+				Utils.toLabelFormat(name, location),
+				Location.parseLocation(location),
+				false,
+				false,
+				ordinal);
+	}
+	
+	public static String toLabelFormat(String name, String location)
+	{
+		String label = String.format("%s %s", name, location).trim();
+		return String.format("<%s>", label);
+	}
+	
+	public static List<String> toTlcOutputFormat(final MCState state)
+	{
+		List<String> inputLines = new ArrayList<String>();
+		inputLines.add(String.format("%d: %s", state.getStateNumber(), state.getLabel()));
+		for (MCVariable variable : state.getVariables())
+		{
+			inputLines.add(String.format("/\\ %s = %s", variable.getName(), variable.getValueAsString()));
+		}
+
+		return inputLines;
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java b/tlatools/org.lamport.tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java
index f15520973b0e22532159a057d80540e864370321..1f2fb1ddb96c6de2f82b6e0a8c5b0ae79d0a3949 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/output/SpecTraceExpressionWriterTest.java
@@ -1,5 +1,7 @@
 package tlc2.output;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -128,4 +130,31 @@ public class SpecTraceExpressionWriterTest {
 		
 		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/org.lamport.tlatools/test/tlc2/tool/AliasLivenessLassoTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasLivenessLassoTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..663f888e029edf15e72d38a203eddb730f789f9d
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasLivenessLassoTest.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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+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 AliasLivenessLassoTest extends ModelCheckerTestCase {
+
+	public AliasLivenessLassoTest() {
+		super("Alias", new String[] { "-config", "AliasLasso.cfg" }, EC.ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		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("/\\ y = FALSE\n/\\ x = 1\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 1 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 2\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 2 e2: TRUE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 3\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 3 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 4\n/\\ a = -3\n/\\ b = FALSE\n/\\ anim = \"e1: 4 e2: TRUE\"\n/\\ te = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertBackToState(1, "<Next line 14, col 9 to line 18, col 23 of module Alias>");
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasLivenessStutteringTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasLivenessStutteringTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7dd63104d839ce0989ebe54e62c6193455d0e7a
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasLivenessStutteringTest.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+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 AliasLivenessStutteringTest extends ModelCheckerTestCase {
+
+	public AliasLivenessStutteringTest() {
+		super("Alias", new String[] { "-config", "AliasStuttering.cfg" }, EC.ExitStatus.VIOLATION_LIVENESS);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "5", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		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);
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 1\n/\\ a = 0\n/\\ b = TRUE\n/\\ anim = \"e1: 1 e2: FALSE\"\n/\\ te = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+		
+		assertStuttering(2);
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetySimuTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetySimuTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e1bb0923add0bae60f8b88b684f1a81a0251d20
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetySimuTest.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+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 AliasSafetySimuTest extends ModelCheckerTestCase {
+
+	public AliasSafetySimuTest() {
+		super("Alias", new String[] { "-config", "Alias.tla", "-simulate", "num=1" }, EC.ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@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>(7);
+		// Trace prefix
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 1\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 1 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 2\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 2 e2: TRUE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 3\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 3 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 4\n/\\ a = 0\n/\\ b = TRUE\n/\\ anim = \"e1: 4 e2: TRUE\"\n/\\ te = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetyTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e67c52618445c6f31bf628821b337389b674ef5
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/AliasSafetyTest.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+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 AliasSafetyTest extends ModelCheckerTestCase {
+
+	public AliasSafetyTest() {
+		super("Alias", new String[] { "-config", "Alias.tla" }, EC.ExitStatus.VIOLATION_SAFETY);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "4", "4", "0"));
+		assertTrue(recorder.recordedWithStringValue(EC.TLC_SEARCH_DEPTH, "4"));
+
+		assertFalse(recorder.recorded(EC.GENERAL));
+
+		// Assert the error trace
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
+		final List<String> expectedTrace = new ArrayList<String>(7);
+		// Trace prefix
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 1\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 1 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 2\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 2 e2: TRUE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = FALSE\n/\\ x = 3\n/\\ a = 1\n/\\ b = FALSE\n/\\ anim = \"e1: 3 e2: FALSE\"\n/\\ te = TRUE");
+		expectedTrace.add("/\\ y = TRUE\n/\\ x = 4\n/\\ a = 0\n/\\ b = TRUE\n/\\ anim = \"e1: 4 e2: TRUE\"\n/\\ te = TRUE");
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/CommonTestCase.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/CommonTestCase.java
index ef430379929639a5718c2f5c711f07094fd935d0..3768fdd80a6875646d2bc528335a131fff363186 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/CommonTestCase.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/CommonTestCase.java
@@ -42,7 +42,6 @@ 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;
@@ -53,7 +52,7 @@ 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;
+	public static final String BASE_PATH = System.getProperty("basepath", BASE_DIR + TEST_MODEL);
 
 	protected final TestMPRecorder recorder;
 
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstDieHardTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstDieHardTest.java
index 75997ec73551443a41d04fe7e57bcf6787f936c9..b42d3cf5d984984e6dd6d325e0592b2eeee52c8e 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstDieHardTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstDieHardTest.java
@@ -51,7 +51,8 @@ public class DepthFirstDieHardTest extends ModelCheckerTestCase {
 		assertFalse(recorder.recorded(EC.GENERAL));
 		
 		// Assert the error trace
-		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1));
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT1));
+		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");
@@ -62,7 +63,7 @@ public class DepthFirstDieHardTest extends ModelCheckerTestCase {
 		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);
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		assertZeroUncovered();
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java
index c6249216383b02c3cc08eab681dd481ff9f50212..9d0f691506ee18e495b490e96e4406057f8b55be 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/DepthFirstErrorTraceTest.java
@@ -51,7 +51,8 @@ public class DepthFirstErrorTraceTest extends ModelCheckerTestCase {
 		assertFalse(recorder.recorded(EC.GENERAL));
 	
 		// Assert the error trace
-		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT1));
+		assertFalse(recorder.recorded(EC.TLC_STATE_PRINT1));
+		assertTrue(recorder.recorded(EC.TLC_STATE_PRINT2));
 		final List<String> expectedTrace = new ArrayList<String>(5);
 		expectedTrace.add("x = 0");
 		expectedTrace.add("x = 1");
@@ -61,7 +62,7 @@ public class DepthFirstErrorTraceTest extends ModelCheckerTestCase {
 		expectedTrace.add("x = 5");
 		expectedTrace.add("x = 6");
 		expectedTrace.add("x = 7");
-		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT1), expectedTrace);
+		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		assertZeroUncovered();
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/Github461Test.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/Github461Test.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e05070fb0c52ddc02cc9418d49339f7baffd695
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test/tlc2/tool/InliningTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/InliningTest.java
index e980ade6d7da40cadea6fa526ee9bcb872e2ee6b..6817718bb5a139b114f259d4745c2dc717f7ccb9 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/InliningTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/InliningTest.java
@@ -144,7 +144,7 @@ public class InliningTest extends ModelCheckerTestCase {
 				}
 			}
 			System.out.println(methodNameMatch);
-//			return false;
+			return false;
 		}
 		return true;
 	}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/MonolithSpecTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/MonolithSpecTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d645654bd80f267cfc5d661a8c8056e56f50a39
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/MonolithSpecTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class MonolithSpecTest extends ModelCheckerTestCase {
+
+	public MonolithSpecTest() {
+		super("MonolithSpec", new String[] { "-config", "MonolithSpec.tla" /* note the extension */ });
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "214", "54", "0"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/PostAssumptionTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/PostAssumptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7a1eac4b01b447d1b2981dae3f2af055a915735
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/PostAssumptionTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 org.junit.Test;
+
+import tlc2.output.EC;
+import tlc2.tool.liveness.ModelCheckerTestCase;
+
+public class PostAssumptionTest extends ModelCheckerTestCase {
+
+	public PostAssumptionTest() {
+		super("DieHardTLA", new String[] { "-config", "DieHardTLAPA" }, EC.ExitStatus.VIOLATION_ASSUMPTION);
+	}
+
+	@Test
+	public void testSpec() {
+		assertTrue(recorder.recorded(EC.TLC_FINISHED));
+		assertTrue(recorder.recordedWithStringValues(EC.TLC_STATS, "97", "16", "0"));
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomElementSimulationTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomElementSimulationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ecaf022ad5aa93506f8f85e32d87393665fcad2b
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomElementSimulationTest.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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.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 RandomElementSimulationTest extends ModelCheckerTestCase {
+
+	public RandomElementSimulationTest() {
+		super("RandomElement", new String[] { "-seed", Long.toString(8006803340504660123L), "-simulate", "num=1" },
+				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<String> expectedTrace = new ArrayList<String>(11);
+		expectedTrace.add("/\\ x = 843\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 843\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 227\n/\\ y = 2");
+		expectedTrace.add("/\\ x = 227\n/\\ y = 3");
+		expectedTrace.add("/\\ x = 502\n/\\ y = 4");
+		expectedTrace.add("/\\ x = 535\n/\\ y = 5");
+		expectedTrace.add("/\\ x = 277\n/\\ y = 6");
+		expectedTrace.add("/\\ x = 327\n/\\ y = 7");
+		expectedTrace.add("/\\ x = 729\n/\\ y = 8");
+		expectedTrace.add("/\\ x = 550\n/\\ y = 9");
+		expectedTrace.add("/\\ x = 318\n/\\ y = 10");
+		List<Object> actualTrace = recorder.getRecords(EC.TLC_STATE_PRINT2);
+		assertTraceWith(actualTrace, expectedTrace);
+
+		assertZeroUncovered();
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetATest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetATest.java
index 44e4429427dad340128b7d396f2ea5cadbe72346..ab699703e85be63a515b52758f06d75fb4161384 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetATest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetATest.java
@@ -28,6 +28,6 @@ package tlc2.tool;
 public class RandomSubsetATest extends RandomSubset {
 
 	public RandomSubsetATest() {
-		super(15041980L, 47567, 100000003);
+		super(15041980L, 1730011, 100000002);
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetBTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetBTest.java
index 05edf09ddb478a1615b94b94c538077650934e79..41f3e78a05904d017fb8a1304685122d2fed2dba 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetBTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetBTest.java
@@ -28,6 +28,6 @@ package tlc2.tool;
 public class RandomSubsetBTest extends RandomSubset {
 
 	public RandomSubsetBTest() {
-		super(918347981374L, 66058, 100000003);
+		super(918347981374L, 48268, 100000000);
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetNextTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetNextTest.java
index 81618cee0ff1b46affe9c6748f18bd8db05f2fe5..370e4aea5e56e83120d6cfd0aa558217bb9d51b3 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetNextTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/RandomSubsetNextTest.java
@@ -47,22 +47,22 @@ public class RandomSubsetNextTest extends ModelCheckerTestCase {
 	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.recordedWithStringValues(EC.TLC_STATS, "67291", "7729", "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");
+		expectedTrace.add("/\\ x = 23\n/\\ y = 0");
+		expectedTrace.add("/\\ x = 26\n/\\ y = 1");
+		expectedTrace.add("/\\ x = 18\n/\\ y = 2");
+		expectedTrace.add("/\\ x = 29\n/\\ y = 3");
+		expectedTrace.add("/\\ x = 189\n/\\ y = 4");
+		expectedTrace.add("/\\ x = 19\n/\\ y = 5");
+		expectedTrace.add("/\\ x = 92\n/\\ y = 6");
+		expectedTrace.add("/\\ x = 250\n/\\ y = 7");
+		expectedTrace.add("/\\ x = 41\n/\\ y = 8");
+		expectedTrace.add("/\\ x = 52\n/\\ y = 9");
+		expectedTrace.add("/\\ x = 78\n/\\ y = 10");
 		assertTraceWith(recorder.getRecords(EC.TLC_STATE_PRINT2), expectedTrace);
 		
 		assertZeroUncovered();
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CCoverageTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CCoverageTest.java
index b5a40b16da90067cca0c51b1666fbbb5e01d9c1a..c367f1f06b710ce80f48bf28b548ec5880ad536d 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CCoverageTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CCoverageTest.java
@@ -120,6 +120,8 @@ public class CCoverageTest extends AbstractCoverageTest {
 				"  ||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");
+				"  |||line 61, col 26 to line 61, col 29 of module C: 21\n"
+				+ "<Constraint line 42, col 1 to line 42, col 10 of module C>: 252:253\n" + 
+				"  line 42, col 15 to line 42, col 20 of module C: 253");
     }
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java
index b6926887f6f2f03166f1f29e5d83150c4bbc6ac5..9d85368bf4a5c5520d4ced00059b1878d160c98e 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/coverage/CoverageStatisticsTest.java
@@ -99,6 +99,8 @@ public class CoverageStatisticsTest extends AbstractCoverageTest {
 				"  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");
+				"  line 42, col 21 to line 42, col 34 of module CoverageStatistics: 19\n"
+				+ "<Constraint line 48, col 1 to line 48, col 10 of module CoverageStatistics>: 97:98\n" + 
+				"  line 48, col 15 to line 48, col 20 of module CoverageStatistics: 98");
     }
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
index 7249b45070590c4e1a3ee56bf347607299016422..577f7a2aa7a3c937da98708b7c04cdea26ca7a5b 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/ModelCheckerTestCase.java
@@ -45,6 +45,7 @@ import tlc2.output.MP;
 import tlc2.tool.CommonTestCase;
 import tlc2.tool.ModelChecker;
 import util.FileUtil;
+import util.FilenameToStream;
 import util.SimpleFilenameToStream;
 import util.ToolIO;
 
@@ -111,7 +112,7 @@ public abstract class ModelCheckerTestCase extends CommonTestCase {
 
 		try {
 			// TEST_MODEL is where TLC should look for user defined .tla files
-			ToolIO.setUserDir(BASE_DIR + TEST_MODEL + path);
+			ToolIO.setUserDir(BASE_PATH + path);
 			
 			MP.setRecorder(recorder);
 			
@@ -120,10 +121,10 @@ public abstract class ModelCheckerTestCase extends CommonTestCase {
 			// 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;
+			TLCGlobals.livenessThreshold = getLivenessThreshold();
 			
 			tlc = new TLC();
-			tlc.setResolver(new SimpleFilenameToStream());
+			tlc.setResolver(getResolver());
 			// * We want *no* deadlock checking to find the violation of the
 			// temporal formula
 			// * We use (unless overridden) a single worker to simplify
@@ -139,8 +140,14 @@ public abstract class ModelCheckerTestCase extends CommonTestCase {
 				args.add("-deadlock");
 			}
 			
+			if (noGenerateSpec()) {
+				args.add("-noGenerateSpecTE");
+			}
 			args.add("-fp");
 			args.add("0");
+			// Deterministic simulation (order in which actions are explored).
+			args.add("-seed");
+			args.add("1");
 			
 			if (doCoverage()) {
 				args.add("-coverage");
@@ -176,6 +183,18 @@ public abstract class ModelCheckerTestCase extends CommonTestCase {
 		}
 	}
 
+	protected double getLivenessThreshold() {
+		return Double.MAX_VALUE;
+	}
+
+	protected FilenameToStream getResolver() {
+		return new SimpleFilenameToStream();
+	}
+
+	protected boolean noGenerateSpec() {
+		return true;
+	}
+
 	protected void beforeTearDown() {
 		// No-op
 	}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2PostCondition.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2PostCondition.java
new file mode 100644
index 0000000000000000000000000000000000000000..bfe9d4dfc18466f098069fdc12d4215267e0a584
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SimulationTest2PostCondition.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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.TLC;
+import tlc2.output.EC;
+
+public class SimulationTest2PostCondition extends SuccessfulSimulationTestCase {
+
+	public SimulationTest2PostCondition() {
+		super("Test2", "/", new String[] { "-config", "Test2PostCondition.cfg", "-simulate", "-depth", "6" });
+		TLC.setTraceNum(1);
+	}
+	@Test
+	public void testSpec() {
+		super.testSpec();
+		assertTrue(recorder.recorded(EC.TLC_ASSUMPTION_FALSE));
+	}
+}
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SimulationTestAssumption.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f2729afb2ce1be36dc18a3d71f4a5e392c38c45
--- /dev/null
+++ b/tlatools/org.lamport.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/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
index 2eb3c22857017d75737719f160977454a0bfde3b..ba1f69f9f0baf3785a86d9186d014f8fb1e9b352 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/liveness/simulation/SuccessfulSimulationTestCase.java
@@ -48,6 +48,10 @@ 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
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java b/tlatools/org.lamport.tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java
index f44cd05fd2a33c1accceed3fde0ea344d61a6226..108eb231bf2528cfb80ca7a80f3d351552956184 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/tool/simulation/SimulationWorkerTest.java
@@ -7,6 +7,7 @@ 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.AtomicLong;
 import java.util.concurrent.atomic.LongAdder;
 
 import org.junit.After;
@@ -67,9 +68,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		StateVec initStates = tool.getInitStates();
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 1000, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 1000, false, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		assertFalse(res.isError());
 		worker.join();
@@ -84,9 +85,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
 		int maxTraceNum = 3;
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, maxTraceNum, false,
-				null, liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, maxTraceNum, false,
+				null, liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		
 		assertTrue(res.isError());
@@ -158,9 +159,8 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null, liveCheck);
+		worker.start(initStates);
 		
 		SimulationWorkerResult res = resultQueue.take();
 		assertTrue(res.isError());
@@ -206,9 +206,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		
 		assertTrue(res.isError());
@@ -234,9 +234,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck);
+		worker.start(initStates);
 		
 		SimulationWorkerResult res = resultQueue.take();
 		
@@ -255,9 +255,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		
 		assertTrue(res.isError());
@@ -283,9 +283,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, true, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, true, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		
 		assertTrue(res.isError());
@@ -328,9 +328,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		assertFalse(res.isError());
 		worker.join();
@@ -345,9 +345,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		StateVec initStates = tool.getInitStates();
 		ILiveCheck liveCheck =  new NoOpLiveCheck(tool, "BasicMultiTrace");
 		BlockingQueue<SimulationWorkerResult> resultQueue = new LinkedBlockingQueue<>();
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, 100, 100, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, 100, false, null,
+				liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		assertFalse(res.isError());
 		worker.join();
@@ -366,9 +366,9 @@ public class SimulationWorkerTest extends CommonTestCase {
 		// 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, initStates, resultQueue, 0, 100, traceNum, false, null,
-				liveCheck, new LongAdder(), new LongAdder());
-		worker.start();
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, 100, traceNum, false, null,
+				liveCheck);
+		worker.start(initStates);
 		
 		// Check one result.
 		SimulationWorkerResult res = resultQueue.take();
@@ -393,10 +393,10 @@ public class SimulationWorkerTest extends CommonTestCase {
 		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, initStates, resultQueue, 0, traceDepth, 100, false,
-				null, liveCheck, new LongAdder(), new LongAdder());
-		worker.start();				
+		int traceDepth = 1;
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, 100, false,
+				null, liveCheck);
+		worker.start(initStates);
 		SimulationWorkerResult res = resultQueue.take();
 		assertFalse(res.isError());
 		
@@ -416,15 +416,18 @@ public class SimulationWorkerTest extends CommonTestCase {
 		// Have the worker generate a specified number of traces of a fixed length.
 		LongAdder numOfGenStates = new LongAdder();
 		LongAdder numOfGenTraces = new LongAdder();
-		long traceDepth = 5;
+		AtomicLong m2AndMean = new AtomicLong();
+		int traceDepth = 5;
 		long traceNum = 5;
-		SimulationWorker worker = new SimulationWorker(0, tool, initStates, resultQueue, 0, traceDepth, traceNum, false,
-				null, liveCheck, numOfGenStates, numOfGenTraces);
+		SimulationWorker worker = new SimulationWorker(0, tool, resultQueue, 0, traceDepth, traceNum, false,
+				null, liveCheck, numOfGenStates, numOfGenTraces, m2AndMean);
 
-		worker.start();
+		worker.start(initStates);
 		worker.join();
 		assertFalse(worker.isAlive());
 		assertEquals(70, numOfGenStates.longValue());
 		assertEquals(5, numOfGenTraces.longValue());
+		// With traces all length 5, mean is 5 and m2 is 0, hence m2AndMean = 5
+		assertEquals(5, m2AndMean.get());
 	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/value/impl/RecordValueTest.java b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/RecordValueTest.java
index 694acb43ad73a7b3bdfd3af480d11c1de4cced96..45cdc1b959d76be727c26c3560141ab16d7e704f 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/value/impl/RecordValueTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/RecordValueTest.java
@@ -78,4 +78,28 @@ public class RecordValueTest {
 		assertTrue(deepCopy.values[0].equals(bVal));
 		assertTrue(deepCopy.values[1].equals(aVal));
 	}
+
+	@Test
+	public void testErrorMessages() {
+		final Value aVal = new StringValue("aVal");
+		final RecordValue recVal = new RecordValue(UniqueString.of("a"), aVal);
+
+		try{
+			recVal.apply(new StringValue("b"), 0);
+		} catch(util.Assert.TLCRuntimeException ex){
+			assertTrue(ex.getMessage().contains("Attempted to access nonexistent field 'b' of record\n[a |-> \"aVal\"]"));
+		}
+
+		try{
+			recVal.apply(IntValue.gen(0), 0);
+		} catch(util.Assert.TLCRuntimeException ex){
+			assertTrue(ex.getMessage().contains("Attempted to access record by a non-string argument: 0"));
+		}
+
+		try{
+			recVal.select(IntValue.gen(0));
+		} catch(util.Assert.TLCRuntimeException ex){
+			assertTrue(ex.getMessage().contains("Attempted to access record by a non-string argument: 0"));
+		}
+	}
 }
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/value/impl/SubsetValueTest.java b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/SubsetValueTest.java
index 6270858465f43a5dd32ee7ac2d4f1c5ff672e0ed..4de94db482cc638d9b421c97f522d36e437f7d52 100644
--- a/tlatools/org.lamport.tlatools/test/tlc2/value/impl/SubsetValueTest.java
+++ b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/SubsetValueTest.java
@@ -43,6 +43,7 @@ import org.junit.Test;
 
 import tlc2.util.FP64;
 import tlc2.value.RandomEnumerableValues;
+import tlc2.value.impl.Enumerable.Ordering;
 import tlc2.value.impl.SubsetValue.CoinTossingSubsetEnumerator;
 import tlc2.value.impl.SubsetValue.KElementEnumerator;
 import tlc2.value.impl.SubsetValue.SubsetEnumerator;
@@ -549,4 +550,117 @@ public class SubsetValueTest {
         
         assertEquals(normalized, unnormalized);
 	}
+
+	@Test
+	public void testRandomSubsetGeneratorK0() {
+		final Set<Value> values = new HashSet<>();
+		final SubsetValue subsetValue = new KSubsetValue(0, new IntervalValue(1, 10));
+		final ValueEnumeration elements = subsetValue.elements(Ordering.RANDOMIZED);
+		for (int i = 0; i < 100; i++) {
+			final Value nextElement = elements.nextElement();
+			values.add(nextElement);
+		}
+		assertEquals(1, values.size()); //empty set
+	}
+	
+	@Test
+	public void testRandomSubsetGeneratorKNegative() {
+		try {
+			new KSubsetValue(-1, new IntervalValue(1, 2)).elements(Ordering.RANDOMIZED);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail("Expected an IllegalArgumentException");
+	}
+	
+	@Test
+	public void testRandomSubsetGeneratorKNplus1() {
+		try {
+			new KSubsetValue(3, new IntervalValue(1, 2)).elements(Ordering.RANDOMIZED);
+		} catch (IllegalArgumentException e) {
+			return;
+		}
+		fail("Expected an IllegalArgumentException");
+	}
+	
+	@Test
+	public void testRandomSubsetGeneratorN10() {
+		final Set<Value> values = new HashSet<>();
+		final SubsetValue subsetValue = new KSubsetValue(3, new IntervalValue(1, 10));
+		final ValueEnumeration elements = subsetValue.elements(Ordering.RANDOMIZED);
+		for (int i = 0; i < 1000; i++) {
+			final Value nextElement = elements.nextElement();
+			values.add(nextElement);
+		}
+		// Eventually, the impl generates all values with high probability.
+		assertEquals(subsetValue.numberOfKElements(3), values.size());
+	}
+
+	// N100 would overflow default implementation in SubsetValue.
+	@Test
+	public void testRandomSubsetGeneratorN100() {
+		final Set<Value> values = new HashSet<>();
+		final SubsetValue subsetValue = new KSubsetValue(3, new IntervalValue(1, 100));
+		final ValueEnumeration elements = subsetValue.elements(Ordering.RANDOMIZED);
+		for (int i = 0; i < 100; i++) {
+			final Value nextElement = elements.nextElement();
+			values.add(nextElement);
+		}
+		// For large input sets, generating a small number of subsets doesn't cause
+		// significant duplicates.
+		assertEquals(100, values.size());
+	}
 }
+
+/*
+
+A spec by Jack Vanlightly as an eyeball test to empirically measures the distributions. 
+
+---- CONFIG ksubsets_ex_quant ----
+SPECIFICATION Spec
+=====
+
+------------------------------ MODULE ksubsets_ex_quant ------------------------------
+EXTENDS Naturals, Randomization, FiniteSets, TLC, FiniteSetsExt
+
+Elements == 1..500
+Limit == 1000
+
+VARIABLES counts,
+          total
+
+vars == <<counts, total >>
+
+AddSubset ==
+    /\ total < Limit 
+    \* /\ \E ss \in SUBSET Elements : 
+    \*     /\ Cardinality(ss) = 3 
+    /\ \E ss \in kSubset(3, Elements) : 
+        /\ IF ss \in DOMAIN counts
+            THEN counts' = [counts EXCEPT ![ss] = @ + 1]
+            ELSE counts' = counts @@ (ss :> 1)
+        /\ total' = total + 1
+
+PrintDist ==
+    /\ total = Limit
+    /\ total' = Limit + 1
+    /\ UNCHANGED <<counts>>
+    /\ \A ss \in DOMAIN counts : PrintT(<<total, ss, counts[ss]>>)
+    /\ PrintT(<<"RESULT", Cardinality(DOMAIN counts)>>)
+
+Init == 
+    /\ counts = [ss \in {} |-> 0]
+    /\ total = 0
+
+Next ==
+    \/ AddSubset
+    \/ PrintDist
+
+Spec == Init /\ [][Next]_vars  
+
+=============================================================================
+\* Modification History
+\* Last modified Wed Oct 28 09:08:31 PDT 2020 by markus
+\* Last modified Tue Oct 27 17:24:00 CET 2020 by jvanlightly
+\* Created Tue Oct 27 09:55:35 CET 2020 by jvanlightly
+*/
diff --git a/tlatools/org.lamport.tlatools/test/tlc2/value/impl/TupleValueTest.java b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/TupleValueTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4ab22fecf7e2aee40f02da3a251c72a7fe8a092
--- /dev/null
+++ b/tlatools/org.lamport.tlatools/test/tlc2/value/impl/TupleValueTest.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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.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;
+import tlc2.value.impl.TupleValue;
+import tlc2.value.impl.IntValue;
+import tlc2.value.impl.StringValue;
+import tlc2.value.impl.Value;
+import util.Assert;
+
+
+public class TupleValueTest {
+
+    @Test
+    public void testErrorMessages() {
+        Value elems[] = {new StringValue("A")};
+        final TupleValue tupVal = new TupleValue(elems);
+
+        try{
+            tupVal.apply(IntValue.gen(2), 0);
+        } catch(Assert.TLCRuntimeException ex){
+            assertTrue(ex.getMessage().contains("Attempted to access index 2 of tuple\n<<\"A\">>\nwhich is out of bounds"));
+        }
+
+        try{
+            tupVal.apply(new StringValue("a"), 0);
+        } catch(Assert.TLCRuntimeException ex){
+            assertTrue(ex.getMessage().contains("Attempted to access tuple at a non integral index: \"a\""));
+        }
+
+        try{
+            tupVal.select(new StringValue("a"));
+        } catch(Assert.TLCRuntimeException ex){
+            assertTrue(ex.getMessage().contains("Attempted to access tuple at a non integral index: \"a\""));
+        }
+
+        try{
+            Value args[] = {new StringValue("arg1"), new StringValue("arg2")};
+            tupVal.apply(args, 0);
+        } catch(Assert.TLCRuntimeException ex){
+            assertTrue(ex.getMessage().contains("Attempted to access tuple with 2 arguments when it expects 1."));
+        }
+    }
+}
diff --git a/tlatools/org.lamport.tlatools/test/util/TestPrintStream.java b/tlatools/org.lamport.tlatools/test/util/TestPrintStream.java
index df6400f37613cff591c72c802319082bd94e3ece..ad12a39b826eba7f167eed3e091692ab88490f98 100644
--- a/tlatools/org.lamport.tlatools/test/util/TestPrintStream.java
+++ b/tlatools/org.lamport.tlatools/test/util/TestPrintStream.java
@@ -68,4 +68,12 @@ public class TestPrintStream extends PrintStream {
 		}
 		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
diff --git a/toolbox/org.lamport.tla.toolbox.doc/.classpath b/toolbox/org.lamport.tla.toolbox.doc/.classpath
index 2d1a4302f0478d0513df5056506f5cc1a333b3fa..eca7bdba8f03f22510b7980a94dbfe10c16c0901 100644
--- a/toolbox/org.lamport.tla.toolbox.doc/.classpath
+++ b/toolbox/org.lamport.tla.toolbox.doc/.classpath
@@ -1,7 +1,7 @@
-<?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/J2SE-1.5"/>
-	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?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.8"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/toolbox/org.lamport.tla.toolbox.doc/.settings/org.eclipse.jdt.core.prefs b/toolbox/org.lamport.tla.toolbox.doc/.settings/org.eclipse.jdt.core.prefs
index dcd9a25180fcd86feea791a05c20a579e1c6f70d..295926d964165896ea8aa7c1b1b3c9d3d3caa77e 100644
--- a/toolbox/org.lamport.tla.toolbox.doc/.settings/org.eclipse.jdt.core.prefs
+++ b/toolbox/org.lamport.tla.toolbox.doc/.settings/org.eclipse.jdt.core.prefs
@@ -1,7 +1,7 @@
-#Thu Dec 04 22:50:29 CET 2008
 eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
-org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.5
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/toolbox/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF b/toolbox/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF
index b1ec28784e3d81e88b379a5913100f370f3183dd..794da370899773ce21c9bbf25ac407096121fcb5 100644
--- a/toolbox/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF
+++ b/toolbox/org.lamport.tla.toolbox.doc/META-INF/MANIFEST.MF
@@ -2,8 +2,8 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: TLA+ Toolbox Help
 Bundle-SymbolicName: org.lamport.tla.toolbox.doc; singleton:=true
-Bundle-Version: 1.7.0.qualifier
-Bundle-RequiredExecutionEnvironment: J2SE-1.4
+Bundle-Version: 1.8.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Vendor: Simon Zambrovski, Leslie Lamport
 Bundle-ActivationPolicy: lazy
 Eclipse-BundleShape: dir
diff --git a/toolbox/org.lamport.tla.toolbox.doc/pom.xml b/toolbox/org.lamport.tla.toolbox.doc/pom.xml
index dbc49b5232a9ff942d82baeecbabdb45239a135b..334c56db86fa6dc22467201eb41d7900a61d9786 100644
--- a/toolbox/org.lamport.tla.toolbox.doc/pom.xml
+++ b/toolbox/org.lamport.tla.toolbox.doc/pom.xml
@@ -11,7 +11,7 @@
   </parent>
   <groupId>tlatoolbox</groupId>
   <artifactId>org.lamport.tla.toolbox.doc</artifactId>
-  <version>1.7.0-SNAPSHOT</version>
+  <version>1.8.0-SNAPSHOT</version>
   <packaging>eclipse-plugin</packaging>
   <properties>
       <!-- Do not include non-code project in Sonar reporting. -->
diff --git a/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/eclipse/jface/dialogs/ForkedProgressMonitorDialog.java b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/eclipse/jface/dialogs/ForkedProgressMonitorDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3745378d338328a526284b42c14a2b87125d7c6
--- /dev/null
+++ b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/eclipse/jface/dialogs/ForkedProgressMonitorDialog.java
@@ -0,0 +1,642 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.dialogs;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * A modal dialog that displays progress during a long running operation.
+ * <p>
+ * This concrete dialog class can be instantiated as is, or further subclassed
+ * as required.
+ * </p>
+ * <p>
+ * Typical usage is:
+ * </p>
+ * <pre>
+ *    try {
+ *       IRunnableWithProgress op = ...;
+ *       new ProgressMonitorDialog(activeShell).run(true, true, op);
+ *    } catch (InvocationTargetException e) {
+ *       // handle exception
+ *    } catch (InterruptedException e) {
+ *       // handle cancelation
+ *    }
+ * </pre>
+ *
+ * <p>
+ * Note that the ProgressMonitorDialog is not intended to be used with multiple
+ * runnables - this dialog should be discarded after completion of one
+ * IRunnableWithProgress and a new one instantiated for use by a second or
+ * sebsequent IRunnableWithProgress to ensure proper initialization.
+ * </p>
+ * <p>
+ * Note that not forking the process will result in it running in the UI which
+ * may starve the UI. The most obvious symptom of this problem is non
+ * responsiveness of the cancel button. If you are running within the UI Thread
+ * you should do the bulk of your work in another Thread to prevent starvation.
+ * It is recommended that fork is set to true in most cases.
+ * </p>
+ */
+public class ForkedProgressMonitorDialog extends IconAndMessageDialog implements
+		IRunnableContext {
+	/**
+	 * Name to use for task when normal task name is empty string.
+	 */
+	private static String DEFAULT_TASKNAME = JFaceResources
+			.getString("ProgressMonitorDialog.message"); //$NON-NLS-1$
+
+	/**
+	 * Constants for label and monitor size
+	 */
+	private static int LABEL_DLUS = 40;
+
+	private static int BAR_DLUS = 9;
+
+	/**
+	 * The progress indicator control.
+	 */
+	protected ProgressIndicator progressIndicator;
+
+	/**
+	 * The label control for the task. Kept for backwards compatibility.
+	 */
+	protected Label taskLabel;
+
+	/**
+	 * The label control for the subtask.
+	 */
+	protected Label subTaskLabel;
+
+	/**
+	 * The Cancel button control.
+	 */
+	protected Button cancel;
+
+	/**
+	 * Indicates whether the Cancel button is to be shown.
+	 */
+	protected boolean operationCancelableState = false;
+
+	/**
+	 * Indicates whether the Cancel button is to be enabled.
+	 */
+	protected boolean enableCancelButton;
+
+	/**
+	 * The progress monitor.
+	 */
+	private ProgressMonitor progressMonitor = new ProgressMonitor();
+
+	/**
+	 * The name of the current task (used by ProgressMonitor).
+	 */
+	private String task;
+
+	/**
+	 * The nesting depth of currently running runnables.
+	 */
+	private int nestingDepth;
+
+	/**
+	 * The cursor used in the cancel button;
+	 */
+	protected Cursor arrowCursor;
+
+	/**
+	 * Flag indicating whether to open or merely create the dialog before run.
+	 */
+	private boolean openOnRun = true;
+
+	/**
+	 * Internal progress monitor implementation.
+	 */
+	private class ProgressMonitor implements IProgressMonitorWithBlocking {
+		private String fSubTask = "";//$NON-NLS-1$
+
+		private volatile boolean fIsCanceled;
+
+		/**
+		 * is the process forked
+		 */
+		protected boolean forked = false;
+
+		/**
+		 * is locked
+		 */
+		protected boolean locked = false;
+
+		@Override
+		public void beginTask(String name, int totalWork) {
+			if (progressIndicator.isDisposed()) {
+				return;
+			}
+			if (name == null) {
+				task = "";//$NON-NLS-1$
+			} else {
+				task = name;
+			}
+			String s = task;
+			if (s.length() <= 0) {
+				s = DEFAULT_TASKNAME;
+			}
+			setMessage(s, false);
+			if (!forked) {
+				update();
+			}
+			if (totalWork == UNKNOWN) {
+				progressIndicator.beginAnimatedTask();
+			} else {
+				progressIndicator.beginTask(totalWork);
+			}
+		}
+
+		@Override
+		public void done() {
+			if (!progressIndicator.isDisposed()) {
+				progressIndicator.sendRemainingWork();
+				progressIndicator.done();
+			}
+		}
+
+		@Override
+		public void setTaskName(String name) {
+			if (name == null) {
+				task = "";//$NON-NLS-1$
+			} else {
+				task = name;
+			}
+			String s = task;
+			if (s.length() <= 0) {
+				s = DEFAULT_TASKNAME;
+			}
+			setMessage(s, false);
+			if (!forked) {
+				update();
+			}
+		}
+
+		@Override
+		public boolean isCanceled() {
+			return fIsCanceled;
+		}
+
+		@Override
+		public void setCanceled(boolean b) {
+			fIsCanceled = b;
+			if (locked) {
+				clearBlocked();
+			}
+		}
+
+		@Override
+		public void subTask(String name) {
+			if (subTaskLabel.isDisposed()) {
+				return;
+			}
+			if (name == null) {
+				fSubTask = "";//$NON-NLS-1$
+			} else {
+				fSubTask = name;
+			}
+//			subTaskLabel.setText(shortenText(fSubTask, subTaskLabel));
+			subTaskLabel.setText(fSubTask);
+			if (!forked) {
+				subTaskLabel.update();
+			}
+		}
+
+		@Override
+		public void worked(int work) {
+			internalWorked(work);
+		}
+
+		@Override
+		public void internalWorked(double work) {
+			if (!progressIndicator.isDisposed()) {
+				progressIndicator.worked(work);
+			}
+		}
+
+		@Override
+		public void clearBlocked() {
+			if (getShell() == null || getShell().isDisposed())
+				return;
+			locked = false;
+			updateForClearBlocked();
+		}
+
+		@Override
+		public void setBlocked(IStatus reason) {
+			if (getShell() == null || getShell().isDisposed())
+				return;
+			locked = true;
+			updateForSetBlocked(reason);
+		}
+	}
+
+	/**
+	 * Clear blocked state from the receiver.
+	 */
+	protected void updateForClearBlocked() {
+		progressIndicator.showNormal();
+		setMessage(task, true);
+		if (imageLabel != null) {
+			imageLabel.setImage(getImage());
+		}
+	}
+
+	/**
+	 * Set blocked state from the receiver.
+	 *
+	 * @param reason
+	 *            IStatus that gives the details
+	 */
+	protected void updateForSetBlocked(IStatus reason) {
+		progressIndicator.showPaused();
+		setMessage(reason.getMessage(), true);
+		if (imageLabel != null) {
+			imageLabel.setImage(getImage());
+		}
+	}
+
+	/**
+	 * Creates a progress monitor dialog under the given shell. The dialog has a
+	 * standard title and no image. <code>open</code> is non-blocking.
+	 *
+	 * @param parent
+	 *            the parent shell, or <code>null</code> to create a top-level
+	 *            shell
+	 */
+	public ForkedProgressMonitorDialog(Shell parent) {
+		super(parent);
+		// no close button on the shell style
+		if (isResizable()) {
+			setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
+					| SWT.APPLICATION_MODAL | SWT.RESIZE | SWT.MAX);
+		} else {
+			setShellStyle(getDefaultOrientation() | SWT.BORDER | SWT.TITLE
+					| SWT.APPLICATION_MODAL);
+		}
+		setBlockOnOpen(false);
+	}
+
+	/**
+	 * Enables the cancel button (asynchronously).
+	 *
+	 * @param b
+	 *            The state to set the button to.
+	 */
+	private void asyncSetOperationCancelButtonEnabled(final boolean b) {
+		if (getShell() != null) {
+			getShell().getDisplay().asyncExec(() -> setOperationCancelButtonEnabled(b));
+		}
+	}
+
+	/**
+	 * The cancel button has been pressed.
+	 *
+	 * @since 3.0
+	 */
+	@Override
+	protected void cancelPressed() {
+		// NOTE: this was previously done from a listener installed on the
+		// cancel button. On GTK, the listener installed by
+		// Dialog.createButton is called first and this was throwing an
+		// exception because the cancel button was already disposed
+		cancel.setEnabled(false);
+		progressMonitor.setCanceled(true);
+		super.cancelPressed();
+	}
+
+	/**
+	 * The <code>ProgressMonitorDialog</code> implementation of this method
+	 * only closes the dialog if there are no currently running runnables.
+	 */
+	@Override
+	public boolean close() {
+		if (getNestingDepth() <= 0) {
+			clearCursors();
+			return super.close();
+		}
+		return false;
+	}
+
+	/**
+	 * Clear the cursors in the dialog.
+	 *
+	 * @since 3.0
+	 */
+	protected void clearCursors() {
+		if (cancel != null && !cancel.isDisposed()) {
+			cancel.setCursor(null);
+		}
+		Shell shell = getShell();
+		if (shell != null && !shell.isDisposed()) {
+			shell.setCursor(null);
+		}
+		if (arrowCursor != null) {
+			arrowCursor.dispose();
+		}
+		arrowCursor = null;
+	}
+
+	@Override
+	protected void configureShell(final Shell shell) {
+		super.configureShell(shell);
+		shell.setText(JFaceResources.getString("ProgressMonitorDialog.title")); //$NON-NLS-1$
+		shell.setCursor(shell.getDisplay().getSystemCursor(SWT.CURSOR_WAIT));
+		// Add a listener to set the message properly when the dialog becomes
+		// visible
+		shell.addListener(SWT.Show, event -> shell.getDisplay().asyncExec(() -> setMessage(message, true)));
+	}
+
+	@Override
+	protected void createButtonsForButtonBar(Composite parent) {
+		// cancel button
+		createCancelButton(parent);
+	}
+
+	/**
+	 * Creates the cancel button.
+	 *
+	 * @param parent
+	 *            the parent composite
+	 * @since 3.0
+	 */
+	protected void createCancelButton(Composite parent) {
+		cancel = createButton(parent, IDialogConstants.CANCEL_ID,
+				IDialogConstants.CANCEL_LABEL, false);
+		if (arrowCursor == null) {
+			arrowCursor = new Cursor(cancel.getDisplay(), SWT.CURSOR_ARROW);
+		}
+		cancel.setCursor(arrowCursor);
+		setOperationCancelButtonEnabled(enableCancelButton);
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		setMessage(DEFAULT_TASKNAME, false);
+		createMessageArea(parent);
+		// Only set for backwards compatibility
+		taskLabel = messageLabel;
+		// progress indicator
+		progressIndicator = new ProgressIndicator(parent);
+		GridData gd = new GridData();
+		gd.heightHint = convertVerticalDLUsToPixels(BAR_DLUS);
+		gd.horizontalAlignment = GridData.FILL;
+		gd.grabExcessHorizontalSpace = true;
+		gd.horizontalSpan = 2;
+		progressIndicator.setLayoutData(gd);
+		// label showing current task
+		subTaskLabel = new Label(parent, SWT.LEFT | SWT.WRAP);
+		gd = new GridData(GridData.FILL_HORIZONTAL);
+		gd.heightHint = convertVerticalDLUsToPixels(LABEL_DLUS);
+		gd.horizontalSpan = 2;
+		subTaskLabel.setLayoutData(gd);
+		subTaskLabel.setFont(parent.getFont());
+		return parent;
+	}
+
+	@Override
+	protected Point getInitialSize() {
+		Point calculatedSize = super.getInitialSize();
+		if (calculatedSize.x < 450) {
+			calculatedSize.x = 450;
+		}
+		return calculatedSize;
+	}
+
+	/**
+	 * Returns the progress monitor to use for operations run in this progress
+	 * dialog.
+	 *
+	 * @return the progress monitor
+	 */
+	public IProgressMonitor getProgressMonitor() {
+		return progressMonitor;
+	}
+
+	/**
+	 * This implementation of IRunnableContext#run(boolean, boolean,
+	 * IRunnableWithProgress) runs the given <code>IRunnableWithProgress</code>
+	 * using the progress monitor for this progress dialog and blocks until the
+	 * runnable has been run, regardless of the value of <code>fork</code>.
+	 * The dialog is opened before the runnable is run, and closed after it
+	 * completes. It is recommended that <code>fork</code> is set to true in
+	 * most cases. If <code>fork</code> is set to <code>false</code>, the
+	 * runnable will run in the UI thread and it is the runnable's
+	 * responsibility to call <code>Display.readAndDispatch()</code> to ensure
+	 * UI responsiveness.
+	 */
+	@Override
+	public void run(boolean fork, boolean cancelable,
+			IRunnableWithProgress runnable) throws InvocationTargetException,
+			InterruptedException {
+		setCancelable(cancelable);
+		try {
+			aboutToRun();
+			// Let the progress monitor know if they need to update in UI Thread
+			progressMonitor.forked = fork;
+			ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
+					.getDisplay());
+		} finally {
+			finishedRun();
+		}
+	}
+
+	/**
+	 * Returns whether the dialog should be opened before the operation is run.
+	 * Defaults to <code>true</code>
+	 *
+	 * @return <code>true</code> to open the dialog before run,
+	 *         <code>false</code> to only create the dialog, but not open it
+	 * @since 3.0
+	 */
+	public boolean getOpenOnRun() {
+		return openOnRun;
+	}
+
+	/**
+	 * Sets whether the dialog should be opened before the operation is run.
+	 * NOTE: Setting this to false and not forking a process may starve any
+	 * asyncExec that tries to open the dialog later.
+	 *
+	 * @param openOnRun
+	 *            <code>true</code> to open the dialog before run,
+	 *            <code>false</code> to only create the dialog, but not open
+	 *            it
+	 * @since 3.0
+	 */
+	public void setOpenOnRun(boolean openOnRun) {
+		this.openOnRun = openOnRun;
+	}
+
+	/**
+	 * Returns the nesting depth of running operations.
+	 *
+	 * @return the nesting depth of running operations
+	 * @since 3.0
+	 */
+	protected int getNestingDepth() {
+		return nestingDepth;
+	}
+
+	/**
+	 * Increments the nesting depth of running operations.
+	 *
+	 * @since 3.0
+	 */
+	protected void incrementNestingDepth() {
+		nestingDepth++;
+	}
+
+	/**
+	 * Decrements the nesting depth of running operations.
+	 *
+	 * @since 3.0
+	 *
+	 */
+	protected void decrementNestingDepth() {
+		nestingDepth--;
+	}
+
+	/**
+	 * Called just before the operation is run. Default behaviour is to open or
+	 * create the dialog, based on the setting of <code>getOpenOnRun</code>,
+	 * and increment the nesting depth.
+	 *
+	 * @since 3.0
+	 */
+	protected void aboutToRun() {
+		if (getOpenOnRun()) {
+			open();
+		} else {
+			create();
+		}
+		incrementNestingDepth();
+	}
+
+	/**
+	 * Called just after the operation is run. Default behaviour is to decrement
+	 * the nesting depth, and close the dialog.
+	 *
+	 * @since 3.0
+	 */
+	protected void finishedRun() {
+		decrementNestingDepth();
+		close();
+	}
+
+	/**
+	 * Sets whether the progress dialog is cancelable or not.
+	 *
+	 * @param cancelable
+	 *            <code>true</code> if the end user can cancel this progress
+	 *            dialog, and <code>false</code> if it cannot be canceled
+	 */
+	public void setCancelable(boolean cancelable) {
+		if (cancel == null) {
+			enableCancelButton = cancelable;
+		} else {
+			asyncSetOperationCancelButtonEnabled(cancelable);
+		}
+	}
+
+	/**
+	 * Helper to enable/disable Cancel button for this dialog.
+	 *
+	 * @param b
+	 *            <code>true</code> to enable the cancel button, and
+	 *            <code>false</code> to disable it
+	 * @since 3.0
+	 */
+	protected void setOperationCancelButtonEnabled(boolean b) {
+		operationCancelableState = b;
+		if (cancel != null && !cancel.isDisposed()) {
+			cancel.setEnabled(b);
+		}
+	}
+
+	@Override
+	protected Image getImage() {
+		return getInfoImage();
+	}
+
+	/**
+	 * Set the message in the message label.
+	 *
+	 * @param messageString
+	 *            The string for the new message.
+	 * @param force
+	 *            If force is true then always set the message text.
+	 */
+	private void setMessage(String messageString, boolean force) {
+		// must not set null text in a label
+		message = messageString == null ? "" : messageString; //$NON-NLS-1$
+		if (messageLabel == null || messageLabel.isDisposed()) {
+			return;
+		}
+		if (force || messageLabel.isVisible()) {
+			messageLabel.setToolTipText(message);
+			messageLabel.setText(shortenText(message, messageLabel));
+		}
+	}
+
+	/**
+	 * Update the message label. Required if the monitor is forked.
+	 */
+	private void update() {
+		if (messageLabel == null || messageLabel.isDisposed()) {
+			return;
+		}
+		messageLabel.update();
+	}
+
+	@Override
+	public int open() {
+		// Check to be sure it is not already done. If it is just return OK.
+		if (!getOpenOnRun()) {
+			if (getNestingDepth() == 0) {
+				return OK;
+			}
+		}
+		int result = super.open();
+		// update message label just in case beginTask() has been invoked
+		// already
+		if (task == null || task.length() == 0)
+			setMessage(DEFAULT_TASKNAME, true);
+		else
+			setMessage(task, true);
+		return result;
+	}
+}
diff --git a/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/PCalTranslator.java b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/PCalTranslator.java
index 0feca318b3392364a8317bed20625fc9bc005747..e0655248913d320c9339485c448764c731c0e789 100644
--- a/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/PCalTranslator.java
+++ b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/PCalTranslator.java
@@ -33,7 +33,6 @@ import java.util.List;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.jface.dialogs.ProgressMonitorDialog;
 import org.eclipse.jface.operation.IRunnableContext;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.text.IDocument;
@@ -50,6 +49,7 @@ import org.lamport.tla.toolbox.util.pref.IPreferenceConstants;
 import org.lamport.tla.toolbox.util.pref.PreferenceStoreHelper;
 
 import pcal.Translator;
+import pcal.ValidationCallBack;
 
 public class PCalTranslator {
 	
@@ -62,8 +62,8 @@ public class PCalTranslator {
 		// call to the PlusCal translator call is forked off into a non-UI thread.
 		// However, we use a ProgressMonitorDialog to lock the UI from further
 		// modifications.
-		final IRunnableContext context = new ProgressMonitorDialog(tlaEditor.getEditorSite().getShell());
-		context.run(true, false, new IRunnableWithProgress() {
+		final IRunnableContext context = new ValidationProgressMonitorDialog(tlaEditor.getEditorSite().getShell());
+		context.run(true, true, new IRunnableWithProgress() {
 			public void run(final IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException {
 				final IEditorInput editorInput = tlaEditor.getEditorInput();
 				final IDocument doc = tlaEditor.getDocumentProvider().getDocument(editorInput);
@@ -89,7 +89,7 @@ public class PCalTranslator {
 				// succeed, we know there are parser problems which we will use to marker the
 				// editor.
 				final Translator translator = new Translator(doc.get(), asList);
-				if (translator.translate()) {
+				if (translator.translate((ValidationCallBack) context)) {
 					// Update the mapping to/from TLA+ to PlusCal.
 					spec.setTpMapping(translator.getMapping(), file.getName(), progressMonitor);
 
diff --git a/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/ValidationProgressMonitorDialog.java b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/ValidationProgressMonitorDialog.java
new file mode 100644
index 0000000000000000000000000000000000000000..0313be1b6cd0bc6e1c38ad07c9e06c76352cadd8
--- /dev/null
+++ b/toolbox/org.lamport.tla.toolbox.editor.basic/src/org/lamport/tla/toolbox/editor/basic/pcal/ValidationProgressMonitorDialog.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * 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 org.lamport.tla.toolbox.editor.basic.pcal;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import org.eclipse.jface.dialogs.ForkedProgressMonitorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+import pcal.ValidationCallBack;
+
+public class ValidationProgressMonitorDialog extends ForkedProgressMonitorDialog implements IRunnableContext, ValidationCallBack {
+	
+	private Button continueButton;
+
+	private Button thirdButton;
+
+	public ValidationProgressMonitorDialog(final Shell parent) {
+		super(parent);
+	}
+	
+	@Override
+	protected void configureShell(final Shell shell) {
+		super.configureShell(shell);
+		// Set the title (dialog frame).
+		shell.setText("Translate PlusCal Algorithm.");
+	}
+	
+	@Override
+	protected void createButtonsForButtonBar(final Composite parent) {
+		// Create a second button with which the user can chose to continue.
+		thirdButton = createButton(parent, IDialogConstants.OK_ID, "&Overwrite Translation", true);
+		thirdButton.setEnabled(false); // disabled by default unless doIt explicitly called.
+		thirdButton.setVisible(false);
+		
+		// Create a second button with which the user can chose to continue.
+		continueButton = createButton(parent, IDialogConstants.OK_ID, "&Overwrite Translation", true);
+		continueButton.setEnabled(false); // disabled by default unless doIt explicitly called.
+		
+		// Let superclass create the cancel button to which we attach our listener.
+		super.createButtonsForButtonBar(parent);
+		cancel.setText("&Abort");
+		
+		getShell().setDefaultButton(continueButton);
+	}
+	
+	@Override
+	public boolean shouldCancel() {
+
+		final Object lock = new Object();
+
+		final Listener listener = new Listener() {
+			public void handleEvent(final Event e) {
+				// Nothing to do here except releasing the latch.
+				synchronized (lock) {
+					lock.notifyAll(); // Technically, notify would suffice.
+				};
+			}
+		};
+
+		// Set the label on the dialog and enable the cancel and
+		// continue buttons.
+		getShell().getDisplay().syncExec(() -> {
+			getProgressMonitor().setTaskName("Overwrite modified TLA+ translation?");
+			getProgressMonitor().subTask(
+					"The TLA+ translation has been manually modified since its last translation (chksum(tla) mismatch).  Click the \"Overwrite Translation\" button to replace the TLA+ translation anyway or \"Cancel\" to abort re-translation and keep the modified TLA+ translation.");
+			continueButton.setText("&Overwrite Translation");
+			continueButton.setEnabled(true);
+			continueButton.addListener(SWT.Selection, listener);
+			cancel.setText("&Abort");
+			cancel.setEnabled(true);
+			cancel.addListener(SWT.Selection, listener);
+		});
+		
+		// Block this thread until the user presses either the cancel or continue button.
+		try {
+			synchronized (lock) {
+				lock.wait();
+			};
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			return true;
+		}
+		
+		// True if the user pressed cancel button, false if continue.
+		return getProgressMonitor().isCanceled();
+	}
+	
+	@Override
+	public Generate shouldGenerate() {
+		final BlockingQueue<Generate> q = new ArrayBlockingQueue<>(1);
+		
+		// Set the label on the dialog and enable the cancel and
+		// continue buttons.
+		getShell().getDisplay().syncExec(() -> {
+			getProgressMonitor().setTaskName("Add Checksums to TLA+ translation?");
+			getProgressMonitor().subTask(
+					"The PlusCal translator can add checksums to the \\* BEGIN TRANSLATION line of this spec to warn you before model-checking a stale TLA+ translation. Do you want to add checksums, never add checksums, or decide later?");
+			continueButton.setText("&Add Checksums");
+			continueButton.setEnabled(true);
+			continueButton.addListener(SWT.Selection,(e) -> q.offer(Generate.DO_IT));
+			cancel.setText("&Later");
+			cancel.setEnabled(true);
+			cancel.addListener(SWT.Selection,(e) -> q.offer(Generate.NOT_NOW));
+			thirdButton.setText("&Never Add");
+			thirdButton.setEnabled(true);
+			thirdButton.setVisible(true);
+			thirdButton.addListener(SWT.Selection,(e) -> q.offer(Generate.IGNORE));
+		});
+		
+		try {
+			return q.take();
+		} catch (InterruptedException e) {
+			Thread.currentThread().interrupt();
+			return Generate.NOT_NOW;
+		}
+	}
+}
diff --git a/toolbox/org.lamport.tla.toolbox.feature.standalone/build.properties b/toolbox/org.lamport.tla.toolbox.feature.standalone/build.properties
index 23e22be7adeb8de382bd9768065d96522a1286d1..b2326df4fdb4ad88adfab8191bf4e72733b8ad61 100644
--- a/toolbox/org.lamport.tla.toolbox.feature.standalone/build.properties
+++ b/toolbox/org.lamport.tla.toolbox.feature.standalone/build.properties
@@ -1,5 +1,5 @@
 bin.includes = feature.xml,\
                build.properties
-root=file:../../tlatools/org.lamport.tlatools/dist/tla2tools.jar
-root.permissions.755=tla2tools.jar
+root=file:../../tlatools/org.lamport.tlatools/dist/tla2tools.jar,file:target/CommunityModules-deps.jar
+root.permissions.755=tla2tools.jar,CommunityModules-deps.jar
 
diff --git a/toolbox/org.lamport.tla.toolbox.feature.standalone/feature.xml b/toolbox/org.lamport.tla.toolbox.feature.standalone/feature.xml
index 56a805a6e13a216d168b84a9f9c3066c863fbf11..7b295fe909741b7f4256497379f9e4ca7365a502 100644
--- a/toolbox/org.lamport.tla.toolbox.feature.standalone/feature.xml
+++ b/toolbox/org.lamport.tla.toolbox.feature.standalone/feature.xml
@@ -324,7 +324,7 @@
          id="org.eclipse.jdt.annotation"
          download-size="0"
          install-size="0"
-         version="1.1.400.v20180921-1416"
+         version="1.1.500.v20200407-1355"
          unpack="false"/>
 
    <plugin
@@ -355,4 +355,11 @@
          version="0.0.0"
          unpack="false"/>
 
+   <plugin
+         id="org.eclipse.e4.ui.ide"
+         download-size="0"
+         install-size="0"
+         version="0.0.0"
+         unpack="false"/>
+
 </feature>
diff --git a/toolbox/org.lamport.tla.toolbox.feature.standalone/pom.xml b/toolbox/org.lamport.tla.toolbox.feature.standalone/pom.xml
index c968faeb5007557fcce74562f1c0f4f5bcf8222a..ba22a99a2a7ca7afffb1dd49440ae09e7f8f40a9 100644
--- a/toolbox/org.lamport.tla.toolbox.feature.standalone/pom.xml
+++ b/toolbox/org.lamport.tla.toolbox.feature.standalone/pom.xml
@@ -17,4 +17,30 @@
       <!-- Do not include non-code project in Sonar reporting. -->
       <sonar.skip>true</sonar.skip>
   </properties>
+  
+  
+	<build>
+		<plugins>
+  			<plugin>
+					<groupId>com.googlecode.maven-download-plugin</groupId>
+					<artifactId>download-maven-plugin</artifactId>
+					<version>1.4.1</version>
+				<executions>
+					<execution>
+						<phase>process-resources</phase>
+						<goals>
+								<goal>wget</goal>
+						</goals>
+						<configuration>
+								<url>https://github.com/tlaplus/CommunityModules/releases/latest/download/CommunityModules-deps.jar</url>
+								<!-- We keep do not unpack with this plugin as we need do more than just unpack, so we might as well
+										keep all of that logic in the same configuration below. -->
+								<outputDirectory>${project.build.directory}</outputDirectory>
+								<outputFileName>CommunityModules-deps.jar</outputFileName>
+							</configuration>
+						</execution>
+					</executions>
+				</plugin>
+		</plugins>
+	</build>
 </project>
diff --git a/toolbox/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureARMCloudTLCInstanceParameters.java b/toolbox/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureARMCloudTLCInstanceParameters.java
index 4a9eed46f7c1fea11a8fa49fa52b0e4831166e8c..d53edf12f6979ff6f5375c8fc142d7eb5afd15c7 100644
--- a/toolbox/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureARMCloudTLCInstanceParameters.java
+++ b/toolbox/org.lamport.tla.toolbox.jclouds/src/org/lamport/tla/toolbox/jcloud/AzureARMCloudTLCInstanceParameters.java
@@ -176,7 +176,7 @@ public class AzureARMCloudTLCInstanceParameters extends AzureCloudTLCInstancePar
 		// https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-apt?view=azure-cli-latest
 		return "echo \"deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main\" | sudo tee /etc/apt/sources.list.d/azure-cli.list"
 				+ " && "
-				+ "apt-key adv --keyserver packages.microsoft.com --recv-keys BC528686B50D79E339D3721CEB3E94ADBE1229CF";
+				+ "curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null";
 	}
 
 	/* (non-Javadoc)
diff --git a/toolbox/org.lamport.tla.toolbox.product.product/TLAToolbox.target b/toolbox/org.lamport.tla.toolbox.product.product/TLAToolbox.target
index ce56b36199c52b3a23fea81aae4d2d49bd93e371..d2b26263a3e6bdd0bb5bdd65218628c1683f2d0a 100644
--- a/toolbox/org.lamport.tla.toolbox.product.product/TLAToolbox.target
+++ b/toolbox/org.lamport.tla.toolbox.product.product/TLAToolbox.target
@@ -3,7 +3,7 @@
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.contribution.weaving.feature.group" version="0.0.0"/>
 <unit id="org.eclipse.ajdt.feature.group" version="0.0.0"/>
-<repository id="eclipse-ajdt" location="http://download.eclipse.org/tools/ajdt/48/dev/update/ajdt-e48-2.2.4.201908131520/"/>
+<repository id="eclipse-ajdt" location="http://download.eclipse.org/tools/ajdt/410/dev/update/ajdt-e410-2.2.4.202007241909"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.apache.log4j" version="0.0.0"/>
@@ -25,7 +25,7 @@
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.apache.jclouds.feature.feature.group" version="0.0.0"/>
-<repository location="http://lemmy.github.com/jclouds2p2/"/>
+<repository id="github-jclouds" location="http://lemmy.github.com/jclouds2p2/"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.help.feature.group" version="0.0.0"/>
@@ -41,19 +41,19 @@
 <unit id="org.eclipse.core.runtime.feature.feature.group" version="0.0.0"/>
 <unit id="org.eclipse.jdt.feature.group" version="0.0.0"/>
 <unit id="org.eclipse.pde.source.feature.group" version="0.0.0"/>
-<repository id="eclipse" location="http://download.eclipse.org/eclipse/updates/4.13/R-4.13-201909161045"/>
+<repository id="eclipse" location="http://download.eclipse.org/eclipse/updates/4.16/R-4.16-202006040540/"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.egit.feature.group" version="0.0.0"/>
-<repository location="http://download.eclipse.org/egit/updates/"/>
+<repository id="eclipse-egit" location="http://download.eclipse.org/egit/updates-5.5.1/"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="de.vonloesch.pdf4eclipse.feature.feature.group" version="0.0.0"/>
-<repository location="http://lemmy.github.com/Pdf4Eclipse/"/>
+<repository id="github-pdf4eclipse" location="http://lemmy.github.com/Pdf4Eclipse/"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="false" type="InstallableUnit">
 <unit id="org.lamport.openjdk.feature.feature.group" version="0.0.0"/>
-<repository location="http://lemmy.github.com/eclipse-jdk-bundles/"/>
+<repository id="github-jdk" location="http://lemmy.github.com/eclipse-jdk-bundles/"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="com.abstratt.eclipsegraphviz.feature.feature.group" version="0.0.0"/>
@@ -61,16 +61,16 @@
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.recommenders.news.rcp.feature.feature.group" version="0.0.0"/>
-<repository location="http://download.eclipse.org/releases/2018-09/201809191000"/>
+<repository id="eclipse-rcp" location="http://download.eclipse.org/releases/2018-09/201809191000"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.mylyn.commons.feature.group" version="0.0.0"/>
 <unit id="org.eclipse.mylyn.commons.notifications.feature.group" version="0.0.0"/>
-<repository location="http://download.eclipse.org/mylyn/releases/3.24"/>
+<repository id="eclipse-mylyn" location="http://download.eclipse.org/mylyn/releases/3.24"/>
 </location>
 <location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
 <unit id="org.eclipse.e4.tools.spies.feature.feature.group" version="0.0.0"/>
-<repository location="http://download.eclipse.org/e4/snapshots/org.eclipse.e4.tools/latest/"/>
+<repository id="eclipse-e4" location="http://download.eclipse.org/e4/snapshots/org.eclipse.e4.tools/latest/"/>
 </location>
 </locations>
 <targetJRE path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
diff --git a/toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist b/toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist
index afa54db33b9f4537ab2dee65bed8423c35a7e9bf..5b0a42361d4eb3b45b07a7ad0083f4ef335eb9b6 100644
--- a/toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist
+++ b/toolbox/org.lamport.tla.toolbox.product.product/entitlements.plist
@@ -12,5 +12,7 @@
     <true/>
     <key>com.apple.security.cs.disable-library-validation</key>
     <true/>
+    <key>com.apple.security.cs.debugger</key>
+    <true/>
 </dict>
 </plist>
diff --git a/toolbox/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product b/toolbox/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product
index cf8f405108f88e8997fcd4c6e6e12ddbd41050a0..92d2dd66d859630bfea5988c10c595483a76b57d 100644
--- a/toolbox/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product
+++ b/toolbox/org.lamport.tla.toolbox.product.product/org.lamport.tla.toolbox.product.product.product
@@ -1,14 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?pde version="3.5"?>
 
-<product name="TLA+ Toolbox" uid="org.lamport.tla.toolbox.product.product" id="org.lamport.tla.toolbox.product.standalone.product" application="org.lamport.tla.toolbox.application" version="1.7.0.qualifier" useFeatures="true" includeLaunchers="true">
+<product name="TLA+ Toolbox" uid="org.lamport.tla.toolbox.product.product" id="org.lamport.tla.toolbox.product.standalone.product" application="org.lamport.tla.toolbox.application" version="1.8.0.qualifier" useFeatures="true" includeLaunchers="true">
 
    <aboutInfo>
       <image path="/org.lamport.tla.toolbox.product.standalone/images/splash_small.png"/>
       <text>
          TLA+ Toolbox provides a user interface for TLA+ Tools. 
 
-This is Version 1.7.0 of 20 April 2020 and includes:
+This is Version 1.8.0 of Day Month Year and includes:
   - SANY Version 2.2 of 20 April 2020
   - TLC Version 2.15 of 20 April 2020
   - PlusCal Version 1.10 of 20 April 2020
@@ -95,11 +95,11 @@ openFile
       <plugin id="org.lamport.tla.toolbox.jclouds" autoStart="true" startLevel="4" />
       <plugin id="packet" autoStart="true" startLevel="4" />
       <plugin id="sts" autoStart="true" startLevel="4" />
-      <property name="eclipse.buildId" value="1.7.0" />
+      <property name="eclipse.buildId" value="1.8.0" />
    </configurations>
 
    <repositories>
-      <repository location="http://lamport.org/tlatoolbox/branches/1.7.0/toolboxUpdate/" enabled="true" />
+      <repository location="http://lamport.org/tlatoolbox/branches/1.8.0/toolboxUpdate/" enabled="true" />
       <repository location="http://lamport.org/tlatoolbox/ci/toolboxUpdate/" enabled="false" />
    </repositories>
 
diff --git a/toolbox/org.lamport.tla.toolbox.product.standalone/plugin.xml b/toolbox/org.lamport.tla.toolbox.product.standalone/plugin.xml
index c6efde3da48d005bcf9b9cf6187b42a363f0b632..d85fca23c68cfbe62a323c2c50e0a839e438c1b0 100644
--- a/toolbox/org.lamport.tla.toolbox.product.standalone/plugin.xml
+++ b/toolbox/org.lamport.tla.toolbox.product.standalone/plugin.xml
@@ -30,7 +30,7 @@
          </property>
          <property
                name="aboutText"
-               value="TLA+ Toolbox provides a user interface for TLA+ Tools. &#x0A;&#x0A;This is Version 1.7.0 of 20 April 2020 and includes:&#x0A;  - SANY Version 2.2 of 20 April 2020&#x0A;  - TLC Version 2.15 of 20 April 2020&#x0A;  - PlusCal Version 1.10 of 20 April 2020&#x0A;  - TLATeX Version 1.0 of 20 September 2017&#x0A;&#x0A;Don&apos;t forget to click on help.  You can learn about features that you never knew about or have forgotten.&#x0A;&#x0A;Please send us reports of problems or suggestions; see https://groups.google.com/d/forum/tlaplus .&#x0A;&#x0A;Some icons used in the Toolbox were provided by www.flaticon.com">
+               value="TLA+ Toolbox provides a user interface for TLA+ Tools. &#x0A;&#x0A;This is Version 1.8.0 of Day Month Year and includes:&#x0A;  - SANY Version 2.2 of 20 April 2020&#x0A;  - TLC Version 2.15 of 20 April 2020&#x0A;  - PlusCal Version 1.10 of 20 April 2020&#x0A;  - TLATeX Version 1.0 of 20 September 2017&#x0A;&#x0A;Don&apos;t forget to click on help.  You can learn about features that you never knew about or have forgotten.&#x0A;&#x0A;Please send us reports of problems or suggestions; see https://groups.google.com/d/forum/tlaplus .&#x0A;&#x0A;Some icons used in the Toolbox were provided by www.flaticon.com">
          </property>
          <property
                name="aboutImage"
diff --git a/toolbox/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java b/toolbox/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java
index cc83746bfb2d99a158e282ea13cee1992ed111b2..45347ff9332c9433fe759f701965161670586f37 100644
--- a/toolbox/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java
+++ b/toolbox/org.lamport.tla.toolbox.product.standalone/src/org/lamport/tla/toolbox/ui/intro/ToolboxIntroPart.java
@@ -197,7 +197,7 @@ public class ToolboxIntroPart extends IntroPart implements IIntroPart {
 
 		/* Examples */
 
-		final StyledText styledExamples = new StyledText(outerContainer, SWT.WRAP | SWT.CENTER);
+		final StyledText styledExamples = new StyledText(outerContainer, SWT.WRAP | SWT.LEFT);
 		styledExamples.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1));
 		styledExamples.setBackground(backgroundColor);
 		String exampleText = "Clicking on one of the buttons below imports an introductory example into the "
@@ -431,7 +431,7 @@ public class ToolboxIntroPart extends IntroPart implements IIntroPart {
 
 		final Label lblVersion = new Label(outerContainer, SWT.WRAP);
 		lblVersion.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1));
-		lblVersion.setText("Version 1.7.0 of 20 April 2020");
+		lblVersion.setText("Version 1.8.0 of Day Month Year");
 		lblVersion.setBackground(backgroundColor);
 	}
 
diff --git a/toolbox/org.lamport.tla.toolbox.product.uitest/Functional/TLA.Functional.PCal.Checksums.test b/toolbox/org.lamport.tla.toolbox.product.uitest/Functional/TLA.Functional.PCal.Checksums.test
index 3962a7f69b0640a7a9a5a98101c8d3555d99fed6..6bd89c5bd59fb18158a7d4ebc9e2e3179003bb9f 100644
--- a/toolbox/org.lamport.tla.toolbox.product.uitest/Functional/TLA.Functional.PCal.Checksums.test
+++ b/toolbox/org.lamport.tla.toolbox.product.uitest/Functional/TLA.Functional.PCal.Checksums.test
@@ -29,21 +29,22 @@ try -command {
 	get-menu -path $TLA-MENU-PATH-NEW-MODEL | click
 	get-window $TLA-DIALOG-TITLE-NEW-MODEL | get-button $TLA-BUTTON-OK | click
 	
+	// trigger divergence dialog and go to history
 	get-menu -path "TLC Model Checker/Run model" | click
-	with [get-window "PlusCal out of sync"] {
-		get-checkbox | click
-		get-button -index 2 | click  // don't seem to able to get by title, likely due to the ampersand in the text
+	with [get-window "PlusCal algorithm has changed but not re-translated"] {
+		get-button -index 1 | click  // don't seem to able to get by title, likely due to the ampersand in the text
 	}
 	get-view "History" | close
+	
+	// trigger divergence dialog again and abort (
+	get-menu -path "TLC Model Checker/Run model" | click
+	get-window "PlusCal algorithm has changed but not re-translated" | click  // Check abort is default
 
+	// trigger divergence dialog and abort
 	get-menu -path "TLC Model Checker/Run model" | click
-	
-	listen errorLog {
-		try {
-			get-window "PlusCal out of sync"
-			log -message "Found checksum warning dialog that shouldn't be shown." -severity error -plugin "org.lamport.tla.toolbox.product.uitests"
-		} -catch { }
-	} | assert-empty
+	with [get-window "PlusCal algorithm has changed but not re-translated"] {
+		get-button -index 0 | click  // don't seem to able to get by title, likely due to the ampersand in the text
+	}
 
 	wait -ms 1200	
 } -finally {
diff --git a/toolbox/org.lamport.tla.toolbox.product.uitest/pom.xml b/toolbox/org.lamport.tla.toolbox.product.uitest/pom.xml
index 62963a225ceebe94d8baea7d9ab3c6514514ece8..92ff2ea6432c498780f78e673eb23233db124fac 100644
--- a/toolbox/org.lamport.tla.toolbox.product.uitest/pom.xml
+++ b/toolbox/org.lamport.tla.toolbox.product.uitest/pom.xml
@@ -39,8 +39,8 @@
        http://maven.xored.com/nexus/content/repositories/ci4rcptt-releases/org/eclipse/sdk/
   -->
   <properties>
-    <rcptt-maven-version>2.4.3</rcptt-maven-version>
-    <rcptt-runner-version>2.4.3</rcptt-runner-version>
+    <rcptt-maven-version>2.5.1</rcptt-maven-version>
+    <rcptt-runner-version>2.5.1</rcptt-runner-version>
     <aut-unpack-directory>target/unpack-for-aut/</aut-unpack-directory>
 <!--
     <jdk-bundle-plugin-prefix>st.theori.openjdk.</jdk-bundle-plugin-prefix>
@@ -181,7 +181,7 @@
 
               DO NOT USE - see above comment WRT truezip
 
-            <explicit>../org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.7.0-[platform].zip</explicit>
+            <explicit>../org.lamport.tla.toolbox.product.product/target/products/TLAToolbox-1.8.0-[platform].zip</explicit>
             -->
 
             <explicit>${aut-unpack-directory}</explicit>
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tla2tex/.classpath b/toolbox/org.lamport.tla.toolbox.tool.tla2tex/.classpath
index 4f83b2397ec8255a7a35e8ab04a3f65a808ccfea..eca7bdba8f03f22510b7980a94dbfe10c16c0901 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tla2tex/.classpath
+++ b/toolbox/org.lamport.tla.toolbox.tool.tla2tex/.classpath
@@ -1,7 +1,7 @@
 <?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.8"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
 	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF b/toolbox/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF
index 06c72cde4ed018bcceeac33779cf1baf4173d415..e585ae69ab50395ac77c0f1c6d6ebcfe92a4565c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF
+++ b/toolbox/org.lamport.tla.toolbox.tool.tla2tex/META-INF/MANIFEST.MF
@@ -10,7 +10,7 @@ Require-Bundle: org.eclipse.ui,
  org.lamport.tla.toolbox.editor.basic;bundle-version="1.0.0",
  org.eclipse.ui.editors;bundle-version="3.6.0"
 Bundle-ActivationPolicy: lazy
-Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-Vendor: Daniel Ricketts
 Import-Package: com.abstratt.graphviz,
  org.eclipse.core.resources,
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ActionInformationItem.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ActionInformationItem.java
index 937578e3262a864844b82fcfa1ea7b652b26ab7c..7f3161c312762c2487e9c8b795224924a167892b 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ActionInformationItem.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/ActionInformationItem.java
@@ -53,6 +53,10 @@ public class ActionInformationItem extends CoverageInformationItem {
 		return parseInitAndNext(Relation.NEXT, outputMessage, modelName);
 	}
 
+	public static ActionInformationItem parseConstraint(String outputMessage, String modelName) {
+		return parseInitAndNext(Relation.CONSTRAINT, outputMessage, modelName);
+	}
+	
 	private static ActionInformationItem parseInitAndNext(Relation rel, String outputMessage, String modelName) {
 		final Matcher matcher = pattern.matcher(outputMessage);
 		matcher.find();
@@ -140,7 +144,7 @@ public class ActionInformationItem extends CoverageInformationItem {
 
 	@Override
     public boolean includeInCounts() {
-		if (relation == Relation.PROP && count == 0) {
+		if ((relation == Relation.PROP && count == 0) || relation == Relation.CONSTRAINT) {
 			// Count should always be zero but better be safe than sorry. The reason to
 			// exclude PROP from counts is to prevent bogus zero coverage reports for
 			// properties. Consider the spec below:
@@ -278,6 +282,19 @@ public class ActionInformationItem extends CoverageInformationItem {
 						"Action %s%s:\n- %,d state%s found with %,d distinct (%.2f%%)\n- Contributes %.2f%% to total number of distinct states across all actions\n",
 						name, definition, getCount(), getCount() == 1 ? "" : "s", getUnseen(), overhead, ratio);
 			}
+		} else if (relation == Relation.CONSTRAINT) {
+			if (getCount() == getUnseen()) {
+				return String.format(
+						"Constraint %s%s:\n- Constraint did not restrict the state-space at all and accepted all states.\n",
+						name, definition);
+			} else if (getUnseen() == 0) {
+				return String.format(
+						"Constraint %s%s:\n- Constraint too restrictive because it did not accept any states.\n", name,
+						definition);
+			} else {
+				return String.format("Constraint %s%s:\n- Accepted %s out of %s state%s.\n", name, definition,
+						getUnseen(), getCount(), getCount() == 1 ? "" : "s");
+			}
 		} else if (relation == Relation.INIT) {
 			return String.format("Action %s%s (Init):\n- %,d state%s found", name, definition, getCount(),
 					getCount() == 1 ? "" : "s");
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/CoverageInformation.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/CoverageInformation.java
index 874717789eaf1e1d0125549d1cb5db45e9286ce7..fe1260930b0a2f38f5ce13d28c191ef2f67a129c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/CoverageInformation.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/CoverageInformation.java
@@ -98,6 +98,7 @@ public class CoverageInformation {
 			return items.stream()
 					.filter(item -> ((item instanceof ActionInformationItem)
 							&& ((ActionInformationItem) item).getRelation() != Relation.PROP
+							&& ((ActionInformationItem) item).getRelation() != Relation.CONSTRAINT
 							&& (((ActionInformationItem) item).getCount() == 0)))
 					.map(item -> (ActionInformationItem) item).collect(Collectors.toList());
 		}
@@ -108,6 +109,7 @@ public class CoverageInformation {
 			return items.stream()
 					.filter(item -> ((item instanceof ActionInformationItem)
 							&& ((ActionInformationItem) item).getRelation() != Relation.PROP
+							&& ((ActionInformationItem) item).getRelation() != Relation.CONSTRAINT
 							&& (((ActionInformationItem) item).getCount() == 0)))
 					.findAny().isPresent();
 		}
@@ -117,7 +119,8 @@ public class CoverageInformation {
 		synchronized (items) {
 			return items.stream()
 					.filter(item -> ((item instanceof ActionInformationItem)
-							&& ((ActionInformationItem) item).getRelation() != Relation.PROP))
+							&& ((ActionInformationItem) item).getRelation() != Relation.PROP)
+							&& ((ActionInformationItem) item).getRelation() != Relation.CONSTRAINT)
 					.map(item -> (ActionInformationItem) item).collect(Collectors.toList());
 		}
 	}
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java
index 839437ea1a5636f39e447322a47835b419763176..829445f70656aca35932a0ce6687bf214348f290 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCModelLaunchDataProvider.java
@@ -364,6 +364,18 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener
                 break;
             case MP.NONE:
 
+                if (messageCode == EC.TLC_COVERAGE_MISMATCH) {
+					// The Toolbox's error parser expects the error trace 2217 to directly follow
+					// the 2110 error marker. However, when TLC runs with multiple workers, 2217 and
+					// 2110 can be interleaved with 2776 if one or more workers have already started
+					// evaluating the next-state relation again before they receive the violation
+					// signal. See Github issue #538 for more details and in particular
+                	// https://github.com/tlaplus/tlaplus/issues/538#issuecomment-751945328
+                	// Bugs in the CostModelCreator that cause TCL_COVERAGE_MISMATCH should be fixed
+                	// separately.
+                	break;
+                }
+            	
                 if (lastDetectedError != null)
                 {
                     // something is detected which is not an error
@@ -506,6 +518,10 @@ public class TLCModelLaunchDataProvider implements ITLCOutputListener
                     this.coverageInfo.add(ActionInformationItem.parseNext(outputMessage, getModelName()));
                     informPresenter(ITLCModelLaunchDataPresenter.COVERAGE);
                     break;
+                case EC.TLC_COVERAGE_CONSTRAINT:
+                    this.coverageInfo.add(ActionInformationItem.parseConstraint(outputMessage, getModelName()));
+                    informPresenter(ITLCModelLaunchDataPresenter.COVERAGE);
+                    break;
                 case EC.TLC_COVERAGE_VALUE_COST:
                     CoverageInformationItem item = CoverageInformationItem.parseCost(outputMessage, getModelName());
                     this.coverageInfo.add(item);
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java
index d36996433b22f3d9eababe3a404462b48316e484..3d8ff45c0e1ae6995a471cd9e14ca8fc9ce3c858 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/output/data/TLCState.java
@@ -99,7 +99,15 @@ public class TLCState implements IModuleLocatable
         {
             final TLCState state = BACK_TO_STATE(number, modelName);
             // See in MP.java case for EC.TLC_BACK_TO_STATE
-            state.setLocation(Location.parseLocation(label.substring((BACK_TO_STATE + ": ").length(), label.length()))); 
+            if (label.indexOf(BACK_TO_STATE + ": ") < 0) {
+				// LiveCheck1 of simulation mode didn't report the location of the back to
+				// state in a lasso trace.  Setting the location to nullLoc here means that
+            	// a double-click on "back to state..." in the error-trace explorer doesn't
+            	// select and reveal the action in the spec.
+				state.setLocation(Location.nullLoc);
+            } else {
+            	state.setLocation(Location.parseLocation(label.substring((BACK_TO_STATE + ": ").length(), label.length()))); 
+            }
 			return state;
         } else
         {
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/TraceExplorerComposite.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/TraceExplorerComposite.java
index 71afbaaed2548475b23772925460537afaddb971..0f72dfb909880ef2b37164c45b71353de32535c9 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/TraceExplorerComposite.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/traceexplorer/TraceExplorerComposite.java
@@ -732,6 +732,10 @@ public class TraceExplorerComposite
 			job.setUser(true);
 			job.schedule();
 			
+			if (Boolean.getBoolean(TLCErrorView.class.getName() + ".noRestore")) {
+				return;
+			}
+			
 	        tableViewer.getTable().setEnabled(false);
 	        
 	        buttonExplore.setEnabled(false);
@@ -760,6 +764,10 @@ public class TraceExplorerComposite
         buttonAdd.setEnabled(true);
         buttonEdit.setEnabled(true);
         buttonRemove.setEnabled(!((IStructuredSelection)tableViewer.getSelection()).isEmpty());
+
+        if (Boolean.getBoolean(TLCErrorView.class.getName() + ".noRestore")) {
+			return;
+		}
         
         buttonRestore.setEnabled(false);
     }
@@ -791,8 +799,12 @@ public class TraceExplorerComposite
         
         if (buttonExplore != null)
         {
-            buttonExplore.setEnabled((view.getTrace() != null) && !view.getTrace().isTraceEmpty()
-                    && (tableViewer.getCheckedElements().length> 0));
+			if (Boolean.getBoolean(TLCErrorView.class.getName() + ".noRestore")) {
+				buttonExplore.setEnabled(tableViewer.getCheckedElements().length> 0);
+			} else {
+	            buttonExplore.setEnabled((view.getTrace() != null) && !view.getTrace().isTraceEmpty()
+	                    && (tableViewer.getCheckedElements().length> 0));
+			}
         }
     }
 
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java
index 95b913b608de6f5adc72528014f253d7efa3ecde..e257503b976de4dcd72aeac4c48f24b891201152 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/ModelEditor.java
@@ -4,6 +4,11 @@ import java.io.ByteArrayInputStream;
 import java.io.Closeable;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -91,6 +96,7 @@ import org.lamport.tla.toolbox.tool.tlc.ui.view.TLCErrorView;
 import org.lamport.tla.toolbox.tool.tlc.util.ChangedSpecModulesGatheringDeltaVisitor;
 import org.lamport.tla.toolbox.tool.tlc.util.ModelHelper;
 import org.lamport.tla.toolbox.ui.handler.OpenSpecHandler;
+import org.lamport.tla.toolbox.ui.view.PDFBrowser;
 import org.lamport.tla.toolbox.ui.view.PDFBrowserEditor;
 import org.lamport.tla.toolbox.util.ResourceHelper;
 import org.lamport.tla.toolbox.util.UIHelper;
@@ -665,6 +671,30 @@ public class ModelEditor extends FormEditor {
 	}
     
 	public void addOrUpdateStateGraphEditor(final IFile stateGraphDotDump) throws CoreException {
+		// -Dorg.lamport.tla.toolbox.tool.tlc.ui.editor.ModelEditor.dotWebView=true
+		if (Boolean.getBoolean(ModelEditor.class.getName() + ".dotWebView")) {
+			try {
+				
+				final Path path = stateGraphDotDump.getLocation().toFile().toPath();
+				//TODO: This might be too large to handle.
+				final String dotString =  new String(Files.readAllBytes(path), StandardCharsets.US_ASCII);
+				final String urlEncodedDotString = URLEncoder.encode(dotString, StandardCharsets.UTF_8.toString())
+		                .replaceAll("\\+", "%20") // Something about Java and JavaScript incompatibilities...
+		                .replaceAll("\\%21", "!")
+		                .replaceAll("\\%27", "'")
+		                .replaceAll("\\%28", "(")
+		                .replaceAll("\\%29", ")")
+		                .replaceAll("\\%7E", "~");
+				//TODO: Choose engine based on size of input string (there are cheaper layouts than 'dot').
+				final String url = "https://edotor.net/?engine=dot#" + urlEncodedDotString;
+
+				final PDFBrowser browser = (PDFBrowser) UIHelper.openView(PDFBrowser.ID);
+				browser.setInput(model.getName(), url);
+				return;
+			} catch (IOException e) {
+				throw new CoreException(new Status(IStatus.ERROR, TLCActivator.PLUGIN_ID, e.getMessage(), e));
+			}
+		}
 		// For historical reasons this preference is found in the tlatex bundle. Thus,
 		// we read the value from there, but don't refer to the corresponding string
 		// constants to not introduce a plugin dependency.
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/advanced/AdvancedTLCOptionsPage.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/advanced/AdvancedTLCOptionsPage.java
index 56a490719366b9e8a94fbe06cc60043ba4c69ff2..607e0f4521404be6325dc43252c063715d60b38c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/advanced/AdvancedTLCOptionsPage.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/editor/page/advanced/AdvancedTLCOptionsPage.java
@@ -82,6 +82,8 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
     private AtomicBoolean programmaticallySettingWorkerParameters;
 
     private SourceViewer m_viewSource;
+    private SourceViewer postConditionSource;
+    private SourceViewer aliasSource;
 
     private Button m_depthFirstOptionCheckbox;
     private Button m_modelCheckModeOption;
@@ -92,6 +94,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
     private Text m_simulationDepthText;
     private Text m_simulationSeedText;
     private Text m_simulationArilText;
+    private Text m_simulationNumTracesText;
     
     // The widgets to display the checkpoint size and the delete button.
     private Button m_checkpointRecoverCheckbox;
@@ -380,6 +383,65 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
         gl.marginWidth = 2;
         modeBody.setLayout(gl);
         
+        // label post condition
+		final String postConditionHelp = "After executing the model, TLC evaluates the given constant-level, no-argument (zero-arity) operator.";
+        final Label postConditionLabel = toolkit.createLabel(modeBody, "Post Condition:");
+        postConditionLabel.setToolTipText(postConditionHelp);
+        gd = new GridData();
+        gd.verticalAlignment = SWT.BEGINNING;
+        gd.horizontalIndent = 10;
+        postConditionLabel.setLayoutData(gd);
+        // field view
+        postConditionSource = FormHelper.createFormsSourceViewer(toolkit, modeBody, SWT.V_SCROLL);
+        postConditionSource.getControl().setToolTipText(postConditionHelp);
+        // layout of the source viewer
+        gd = new GridData();
+        gd.horizontalAlignment = SWT.FILL;
+        gd.grabExcessHorizontalSpace = true;
+        gd.heightHint = 60;
+        gd.minimumWidth = 200;
+        postConditionSource.getTextWidget().setLayoutData(gd);
+        postConditionSource.getTextWidget().setData(DataBindingManager.WIDGET_HAS_ENABLED_STATE_HANDLED_ELSEWHERE, new Object());
+        
+        // label alias expression
+		final String aliasHelp = "TLC allows the cfg file to contain the statement ALIAS R\n" + 
+				"where R is the definition of a record in the spec or the model.  R's\n" + 
+				"component expressions can be formed from constant-, state-, and\n" + 
+				"action-level expressions.\n" + 
+				"When printing an error-trace, TLC evaluates the record R on every step\n" + 
+				"(pair or states s -> t) of the behavior that violated the property.\n" + 
+				"TLC prints the result r of the evaluation of R in place of s by formatting\n" +
+				"each component h_i of r as a conjunct s.t. (\"h_i\" = r[\"h_i\"]).\n" +
+				"If the evaluation of R fails for a step, TLC falls back to printing s.\n\n"
+				+ "Consider  ALIAS R  with\n" + 
+				"\n" + 
+				"   R == [x |-> x+1, sum |-> x + y', te |-> ENABLED A]\n" + 
+				"\n" + 
+				"for a state s with x = 42 and y = 24, a (successor) state t with\n" +
+			    "x = 23 and y = -42, and action A defined as x' \\in Nat /\\ y' \\in Nat\n" +
+				"will print s as\n" + 
+				"   \n" + 
+				"    /\\ x = 43\n" + 
+		        "    /\\ sum = 0\n" + 
+		        "    /\\ te = TRUE";
+        final Label aliasLabel = toolkit.createLabel(modeBody, "Alias:");
+        aliasLabel.setToolTipText(aliasHelp);
+        gd = new GridData();
+        gd.verticalAlignment = SWT.BEGINNING;
+        gd.horizontalIndent = 10;
+        aliasLabel.setLayoutData(gd);
+        // field view
+        aliasSource = FormHelper.createFormsSourceViewer(toolkit, modeBody, SWT.V_SCROLL);
+        aliasSource.getControl().setToolTipText(aliasHelp);
+        // layout of the source viewer
+        gd = new GridData();
+        gd.horizontalAlignment = SWT.FILL;
+        gd.grabExcessHorizontalSpace = true;
+        gd.heightHint = 60;
+        gd.minimumWidth = 200;
+        aliasSource.getTextWidget().setLayoutData(gd);
+        aliasSource.getTextWidget().setData(DataBindingManager.WIDGET_HAS_ENABLED_STATE_HANDLED_ELSEWHERE, new Object());
+       
         // Model checking mode
         m_modelCheckModeOption = toolkit.createButton(modeBody, "Model-checking mode", SWT.RADIO);
         gd = new GridData();
@@ -450,8 +512,25 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 			}
 		});
 
+        // label number of traces
+        final Label numTracesLabel = toolkit.createLabel(modeBody, "Maximum number of traces:");
+        numTracesLabel.setToolTipText("Leave empty to generate an unlimited number (2^64-1) of traces.");
+        gd = new GridData();
+        gd.horizontalIndent = 10;
+        numTracesLabel.setLayoutData(gd);
+        // field depth
+        m_simulationNumTracesText = toolkit.createText(modeBody, "");
+        m_simulationNumTracesText.setToolTipText("Leave empty to generate an unlimited number (2^64-1) of traces.");
+        gd = new GridData();
+        gd.minimumWidth = 100;
+        gd.horizontalAlignment = SWT.FILL;
+        gd.grabExcessHorizontalSpace = true;
+        m_simulationNumTracesText.setLayoutData(gd);
+        m_simulationNumTracesText.addFocusListener(focusListener);
+        m_simulationNumTracesText.setData(DataBindingManager.WIDGET_HAS_ENABLED_STATE_HANDLED_ELSEWHERE, new Object());
+        
         // label depth
-        final Label depthLabel = toolkit.createLabel(modeBody, "Maximum length of the trace:");
+        final Label depthLabel = toolkit.createLabel(modeBody, "Maximum length of each trace:");
         gd = new GridData();
         gd.horizontalIndent = 10;
         depthLabel.setLayoutData(gd);
@@ -801,6 +880,8 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
         
         updateEnabledStatesForAdvancedLaunchRadioSelection();
 
+        dm.bindAttribute(MODEL_PARAMETER_ALIAS, aliasSource, modePart);
+        dm.bindAttribute(MODEL_PARAMETER_POST_CONDITION, postConditionSource, modePart);
         dm.bindAttribute(MODEL_PARAMETER_VIEW, m_viewSource, modePart);
         dm.bindAttribute(LAUNCH_RECOVER, m_checkpointRecoverCheckbox, featuresPart);
         
@@ -810,9 +891,12 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 
         m_modelCheckModeOption.addSelectionListener(modePartListener);
         m_viewSource.addTextListener(modePartListener);
+        postConditionSource.addTextListener(modePartListener);
+        aliasSource.addTextListener(modePartListener);
         m_depthFirstOptionCheckbox.addSelectionListener(modePartListener);
         m_depthText.addModifyListener(modePartListener);
         m_simulationModeOption.addSelectionListener(modePartListener);
+        m_simulationNumTracesText.addModifyListener(modePartListener);
         m_simulationDepthText.addModifyListener(modePartListener);
         m_simulationSeedText.addModifyListener(modePartListener);
         m_simulationArilText.addModifyListener(modePartListener);
@@ -846,6 +930,14 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
     	// view
         final String view = model.getAttribute(LAUNCH_VIEW, EMPTY_STRING);
         m_viewSource.setDocument(new Document(view));
+        
+    	// Alias Expression
+        final String alias = model.getAttribute(LAUNCH_ALIAS, EMPTY_STRING);
+        aliasSource.setDocument(new Document(alias));
+       
+    	// Post Condition
+        final String postCondition = model.getAttribute(LAUNCH_POST_CONDITION, EMPTY_STRING);
+        postConditionSource.setDocument(new Document(postCondition));
 
         // run mode mode
         final boolean isMCMode = model.getAttribute(LAUNCH_MC_MODE, LAUNCH_MC_MODE_DEFAULT);
@@ -861,6 +953,14 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
         m_depthFirstOptionCheckbox.setSelection(isDFIDMode);
         m_depthText.setEnabled(isDFIDMode);
 
+        // simulation number of traces
+        final long simuNumTraces = model.getAttribute(LAUNCH_SIMU_NUM_TRACES, LAUNCH_SIMU_NUM_TRACES_DEFAULT);
+        if (simuNumTraces == Long.MAX_VALUE) {
+        	m_simulationNumTracesText.setText("");
+        } else {
+        	m_simulationNumTracesText.setText("" + simuNumTraces);
+        }
+        
         // simulation depth
         final int simuDepth = model.getAttribute(LAUNCH_SIMU_DEPTH, LAUNCH_SIMU_DEPTH_DEFAULT);
         m_simulationDepthText.setText("" + simuDepth);
@@ -957,6 +1057,14 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 
         // DFID depth
 		model.setAttribute(LAUNCH_DFID_DEPTH, dfidDepth);
+		// simulation number traces
+		final String simuNumTracesS = m_simulationNumTracesText.getText();
+		long simuNumTraces = Long.MAX_VALUE;
+		if (!"".equals(simuNumTracesS)) {
+			simuNumTraces = Long.parseLong(simuNumTracesS);
+		}
+		model.setAttribute(LAUNCH_SIMU_NUM_TRACES, simuNumTraces);
+		
         // simulation depth
 		model.setAttribute(LAUNCH_SIMU_DEPTH, simuDepth);
         // simulation aril
@@ -994,6 +1102,14 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
         String viewFormula = FormHelper.trimTrailingSpaces(m_viewSource.getDocument().get());
         model.setAttribute(LAUNCH_VIEW, viewFormula);
 
+        // post condition
+        String postConditionFormula = FormHelper.trimTrailingSpaces(postConditionSource.getDocument().get());
+        model.setAttribute(LAUNCH_POST_CONDITION, postConditionFormula);
+
+        // alias expression
+        String aliasFormula = FormHelper.trimTrailingSpaces(aliasSource.getDocument().get());
+        model.setAttribute(LAUNCH_ALIAS, aliasFormula);
+
 		// extra vm arguments (replace newlines which otherwise cause the
 		// process to ignore all args except the first one)
         final String vmArgs = m_extraVMArgumentsText.getText().replace("\r\n", " ").replace("\n", " ");
@@ -1103,10 +1219,30 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 			setComplete(false);
 			expandSection(SEC_TLCOPT_CHECK_MODE);
 		}
+		try {
+			// no/empty value is default.
+			if (!"".equals(m_simulationNumTracesText.getText().trim())) {
+				long simuNumTraces = Long.parseLong(m_simulationNumTracesText.getText());
+				if (simuNumTraces <= 0) {
+					modelEditor.addErrorMessage("simuNumTraces1", "Length of the number of traces must be a positive long or empty for unlimited.",
+							this.getId(), IMessageProvider.ERROR, m_simulationNumTracesText);
+					setComplete(false);
+					expandSection(SEC_TLCOPT_CHECK_MODE);
+				} else {
+					modelEditor.removeErrorMessage("simuNumTraces1", m_simulationNumTracesText);
+				}
+				modelEditor.removeErrorMessage("simuNumTraces2", m_simulationNumTracesText);
+			}
+		} catch (NumberFormatException e) {
+			modelEditor.addErrorMessage("simuNumTraces2", "Length of the number of traces must be a positive long or empty for unlimited.",
+					this.getId(), IMessageProvider.ERROR, m_simulationNumTracesText);
+			setComplete(false);
+			expandSection(SEC_TLCOPT_CHECK_MODE);
+		}
 		try {
 			int simuDepth = Integer.parseInt(m_simulationDepthText.getText());
 			if (simuDepth <= 0) {
-				modelEditor.addErrorMessage("simuDepth1", "Length of the simulation tracemust be a positive integer",
+				modelEditor.addErrorMessage("simuDepth1", "Length of the simulation trace must be a positive integer.",
 						this.getId(), IMessageProvider.ERROR, m_simulationDepthText);
 				setComplete(false);
 				expandSection(SEC_TLCOPT_CHECK_MODE);
@@ -1117,7 +1253,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 			// Call of removeErrorMessage added by LL on 21 Mar 2013
 			modelEditor.removeErrorMessage("simuDepth2", m_simulationDepthText);
 		} catch (NumberFormatException e) {
-			modelEditor.addErrorMessage("simuDepth2", "Length of the simulation trace must be a positive integer",
+			modelEditor.addErrorMessage("simuDepth2", "Length of the simulation trace must be a positive integer.",
 					this.getId(), IMessageProvider.ERROR, m_simulationDepthText);
 			setComplete(false);
 			expandSection(SEC_TLCOPT_CHECK_MODE);
@@ -1126,7 +1262,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 			try {
 				long simuAril = Long.parseLong(m_simulationArilText.getText());
 				if (simuAril <= 0) {
-					modelEditor.addErrorMessage("simuAril1", "The simulation aril must be a positive integer",
+					modelEditor.addErrorMessage("simuAril1", "The simulation aril must be a positive integer.",
 							this.getId(), IMessageProvider.ERROR, m_simulationArilText);
 					setComplete(false);
 				} else {
@@ -1136,7 +1272,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 				// Call of removeErrorMessage added by LL on 21 Mar 2013
 				modelEditor.removeErrorMessage("simuAril2", m_simulationArilText);
 			} catch (NumberFormatException e) {
-				modelEditor.addErrorMessage("simuAril2", "The simulation aril must be a positive integer", this.getId(),
+				modelEditor.addErrorMessage("simuAril2", "The simulation aril must be a positive integer.", this.getId(),
 						IMessageProvider.ERROR, m_simulationArilText);
 				setComplete(false);
 				expandSection(SEC_TLCOPT_CHECK_MODE);
@@ -1149,7 +1285,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 				modelEditor.removeErrorMessage("simuSeed1", m_simulationSeedText);
 
 			} catch (NumberFormatException e) {
-				modelEditor.addErrorMessage("simuSeed1", "The simulation aril must be a positive integer", this.getId(),
+				modelEditor.addErrorMessage("simuSeed1", "The simulation aril must be a positive integer.", this.getId(),
 						IMessageProvider.ERROR, m_simulationSeedText);
 				expandSection(SEC_TLCOPT_CHECK_MODE);
 				setComplete(false);
@@ -1185,7 +1321,36 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 			}
 		}
 
-        mm.setAutoUpdate(true);
+        // check if the post condition field contains a cfg file keyword
+		final IDocument postConditionDocument = postConditionSource.getDocument();
+		if (postConditionDocument != null) {
+			final String postConditionString = FormHelper.trimTrailingSpaces(postConditionDocument.get());
+			if (SemanticHelper.containsConfigFileKeyword(postConditionString)) {
+				modelEditor.addErrorMessage(postConditionString,
+						"The toolbox cannot handle the string " + postConditionString
+								+ " because it contains a configuration file keyword.",
+						this.getId(), IMessageProvider.ERROR,
+						UIHelper.getWidget(dm.getAttributeControl(MODEL_PARAMETER_POST_CONDITION)));
+				setComplete(false);
+			}
+		}
+
+
+        // check if the alias expression field contains a cfg file keyword
+		final IDocument aliasDocument = aliasSource.getDocument();
+		if (aliasDocument != null) {
+			final String aliasString = FormHelper.trimTrailingSpaces(aliasDocument.get());
+			if (SemanticHelper.containsConfigFileKeyword(aliasString)) {
+				modelEditor.addErrorMessage(aliasString,
+						"The toolbox cannot handle the string " + aliasString
+								+ " because it contains a configuration file keyword.",
+						this.getId(), IMessageProvider.ERROR,
+						UIHelper.getWidget(dm.getAttributeControl(MODEL_PARAMETER_ALIAS)));
+				setComplete(false);
+			}
+		}
+
+		mm.setAutoUpdate(true);
 
                 
         // fpBits
@@ -1265,6 +1430,7 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
     		m_depthText.setEnabled(m_depthFirstOptionCheckbox.getSelection());
     	}
     	
+    	m_simulationNumTracesText.setEnabled(simulationMode);
     	m_simulationDepthText.setEnabled(simulationMode);
     	m_simulationSeedText.setEnabled(simulationMode);
     	m_simulationArilText.setEnabled(simulationMode);
@@ -1317,6 +1483,8 @@ public class AdvancedTLCOptionsPage extends BasicFormPage implements Closeable {
 		dm.unbindSectionAndAttribute(LAUNCH_NUMBER_OF_WORKERS);
 		dm.unbindSectionAndAttribute(LAUNCH_RECOVER);
 		dm.unbindSectionAndAttribute(MODEL_PARAMETER_VIEW);
+		dm.unbindSectionAndAttribute(MODEL_PARAMETER_POST_CONDITION);
+		dm.unbindSectionAndAttribute(MODEL_PARAMETER_ALIAS);
 	}
     
     private String generateMemoryDisplayText () {
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/ErrorTraceTreeViewer.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/ErrorTraceTreeViewer.java
index 6ec3a1b0cab18394c504db1745a5e0507ed2d140..8fa264187609983b4180455102b841adadc45479 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/ErrorTraceTreeViewer.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/ErrorTraceTreeViewer.java
@@ -193,24 +193,27 @@ class ErrorTraceTreeViewer {
 											return;
 										}
 										
+										final MainModelPage page = (MainModelPage)modelEditor.findPage(MainModelPage.ID);
 										final String init = ((TLCState) selection).getConjunctiveDescription(false);
-										final String next;
 										if (specType == IModelConfigurationDefaults.MODEL_BEHAVIOR_TYPE_SPEC_CLOSED) {
-											next = m.getAttribute(
-													IModelConfigurationConstants.MODEL_BEHAVIOR_CLOSED_SPECIFICATION,
-													"");
+											page.setInitNextBehavior(init, "");
+											modelEditor.setActivePage(MainModelPage.ID);
+											
+											Display.getDefault().asyncExec(() -> {
+												MessageDialog.openInformation(null, "Model Reconfigured",
+														"The model has been set up to begin checking from the selected "
+																+ "state; please enter the next-state relation (usually Next), review the model parameters and run the checker.");
+											});
 										} else {
-											next = null;
+											page.setInitNextBehavior(init, null);
+											modelEditor.setActivePage(MainModelPage.ID);
+											
+											Display.getDefault().asyncExec(() -> {
+												MessageDialog.openInformation(null, "Model Reconfigured",
+														"The model has been set up to begin checking from the selected "
+															+ "state; please review the model parameters and run the checker.");
+											});										
 										}
-										final MainModelPage page = (MainModelPage)modelEditor.findPage(MainModelPage.ID);
-										page.setInitNextBehavior(init, next);
-										modelEditor.setActivePage(MainModelPage.ID);
-										
-										Display.getDefault().asyncExec(() -> {
-											MessageDialog.openInformation(null, "Model Reconfigured",
-													"The model has been set up to begin checking from the selected "
-														+ "state; please review the model parameters and run the checker.");
-										});										
 
 		// We previously handled the config alteration and model check launch behind the
 		//	scenes but have since stopped doing that. I'm leaving a portion of this here,
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java
index a90270fc1637b97df92b6a14af7b2d534615a5d5..df51f0b87be901c88b3e15a4b14f289d62751376 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc.ui/src/org/lamport/tla/toolbox/tool/tlc/ui/view/TLCErrorView.java
@@ -111,11 +111,11 @@ public class TLCErrorView extends ViewPart
 
     private static final String NO_VALUE_VIEWER_TEXT
 			= "\u2022 Select a line in Error Trace to show its value here.\n"
-				+ "\u2022 Double-click on a line to go to corresponding action in spec \u2014 "
+				+ "\u2022 Double+Click on a line to go to corresponding action in spec \u2014 "
 				+ "or while holding down " + (Platform.getOS().equals(Platform.OS_MACOSX) ? "\u2318" : "CTRL")
 				+ " to go to the original PlusCal code, if present.\n"
 				+ "\u2022 Click on a variable while holding down ALT to hide the variable from view.\n"
-				+ "\u2022 Right-click on a location row for a context menu.";
+				+ "\u2022 Right+Click on a location row for a context menu.";
 
     /**
      * This is the pattern of an error message resulting from evaluating the constant
@@ -147,7 +147,6 @@ public class TLCErrorView extends ViewPart
     private SourceViewer errorViewer;
     private ErrorTraceTreeViewer errorTraceTreeViewer;
     private RecordToSourceCoupler stackTraceActionListener;
-    private SyncStackTraversal syncStackTraversalAction;
     private Button valueReflectsFiltering;
     private SourceViewer valueViewer;
     private ModelEditor modelEditor;
@@ -155,7 +154,6 @@ public class TLCErrorView extends ViewPart
     
     private TLCError unfilteredInput;
     private final HashMap<TLCState, Integer> filteredStateIndexMap;
-    private FilterErrorTrace filterErrorTraceAction;
     private Set<TLCVariable> currentErrorTraceFilterSet;
     
     @SuppressWarnings("unused")  // held onto for a nicer object graph
@@ -248,12 +246,23 @@ public class TLCErrorView extends ViewPart
              * seconds, so it is important to not reset the trace if it is not necessary.
              */
             final TLCError oldTrace = errorTraceTreeViewer.getCurrentTrace();
-            final boolean isNewTrace = (trace != null) && (oldTrace != null) && !(trace == oldTrace);
-            // update the trace information
-            if (isNewTrace)
-            {
-                this.setTraceInput(trace, true);
-            }
+            if (Boolean.getBoolean(TLCErrorView.class.getName() + ".noRestore")) {
+    			if (oldTrace.hasTrace() && !trace.hasTrace()) {
+    				// The trace evaluation failed. Don't replace the old trace but instead disable
+    				// the error trace.
+    				final Tree tree = errorTraceTreeViewer.getTreeViewer().getTree();
+    				tree.setBackground(tree.getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
+    			} else if (trace != oldTrace) {
+    				this.setTraceInput(trace, true);
+    			}
+    		} else {
+                final boolean isNewTrace = (trace != null) && (oldTrace != null) && !(trace == oldTrace);
+                // update the trace information
+                if (isNewTrace)
+                {
+                    this.setTraceInput(trace, true);
+                }
+    		}
             if (model.isSnapshot()) {
             	final String date = sdf.format(model.getSnapshotTimeStamp());
             	this.form.setText(model.getSnapshotFor().getName() + " (" + date + ")");
@@ -460,10 +469,13 @@ public class TLCErrorView extends ViewPart
 				RecordToSourceCoupler.FocusRetentionPolicy.ARROW_KEY_TRAVERSAL);
 		tree.addMouseListener(stackTraceActionListener);
 		tree.addKeyListener(stackTraceActionListener);
-		tree.addDisposeListener((event) -> {
-			final IDialogSettings ids = Activator.getDefault().getDialogSettings();
-			ids.put(SYNCED_TRAVERSAL_KEY, syncStackTraversalAction.isChecked());
-        });
+        
+        // Make it possible to expand and collapse the error trace with the push of a button.
+		final ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
+		final ToolBar toolbar = toolBarManager.createControl(errorTraceSection);
+		toolBarManager.add(new ExportErrorTrace2Clipboard(display));
+		
+		final FilterErrorTrace filterErrorTraceAction = new FilterErrorTrace();
 		tree.addMouseListener(new MouseAdapter() {
         	@Override
         	public void mouseUp(final MouseEvent event) {
@@ -488,10 +500,8 @@ public class TLCErrorView extends ViewPart
         		}
         	}
         });
-        
-        // Make it possible to expand and collapse the error trace with the push of a button.
-		final ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
-		final ToolBar toolbar = toolBarManager.createControl(errorTraceSection);
+		toolBarManager.add(filterErrorTraceAction);
+		
 		final ShiftClickAction action = new ShiftClickAction(
 				"Toggle between expand and collapse all (Shift+Click to restore the default two-level expansion)",
 				TLCUIActivator.getImageDescriptor("icons/elcl16/toggle_expand_state.png"), display) {
@@ -513,12 +523,14 @@ public class TLCErrorView extends ViewPart
 				}
 			}
 		};
-		toolBarManager.add(new ExportErrorTrace2Clipboard(display));
-		filterErrorTraceAction = new FilterErrorTrace();
-		toolBarManager.add(filterErrorTraceAction);
 		toolBarManager.add(action);
-		syncStackTraversalAction = new SyncStackTraversal();
+		
+		final SyncStackTraversal syncStackTraversalAction = new SyncStackTraversal();
 		toolBarManager.add(syncStackTraversalAction);
+		tree.addDisposeListener((event) -> {
+			final IDialogSettings ids = Activator.getDefault().getDialogSettings();
+			ids.put(SYNCED_TRAVERSAL_KEY, syncStackTraversalAction.isChecked());
+        });
 		toolBarManager.update(true);
 		errorTraceSection.setTextClient(toolbar);
 
@@ -1012,7 +1024,7 @@ public class TLCErrorView extends ViewPart
 	private class FilterErrorTrace extends Action {
 		private static final String DEFAULT_TOOL_TIP_TEXT
 					= "Click to select variables and expressions to omit from the trace display; "
-									+ "ALT-click on an individual item below to omit it immediately.";
+									+ "ALT+Click on an individual item below to omit it immediately.";
 		private static final String SELECTED_TOOL_TIP_TEXT = "Click to display all variables and expressions.";
 		
 		FilterErrorTrace() {
@@ -1125,7 +1137,7 @@ public class TLCErrorView extends ViewPart
 	private class ExportErrorTrace2Clipboard extends ShiftClickAction implements ISelectionChangedListener {
 		private static final String DEFAULT_TOOL_TIP_TEXT
 			= "Click to export error-trace to clipboard as\nsequence of records. "
-					+ "Shift-click to \nomit the action's position ("
+					+ "Shift+Click to \nomit the action's position ("
 					+ TLAConstants.TraceExplore.POSITION + "), \nname, and location.";
 		
 		
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF b/toolbox/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF
index 98604215258d2a84308ea2dbc6fba45d322211d4..99a03f27aa9e1edb39e1f8aa9a2bb9d000160144 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/META-INF/MANIFEST.MF
@@ -16,7 +16,7 @@ Require-Bundle: org.eclipse.ui,
  org.eclipse.jface.text;bundle-version="3.5.0",
  org.lamport.tla.toolbox.product.standalone,
  org.apache.commons.io;bundle-version="2.0.1"
-Bundle-RequiredExecutionEnvironment: JavaSE-1.7
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Bundle-ActivationPolicy: lazy
 Export-Package: org.lamport.tla.toolbox.tool.tlc,
  org.lamport.tla.toolbox.tool.tlc.job,
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/plugin.xml b/toolbox/org.lamport.tla.toolbox.tool.tlc/plugin.xml
index baeb3f54acafcb15284ec5901edfb385a373acb8..d2f77116ed9684c3fa0c1b827420b518fdb9c53c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/plugin.xml
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/plugin.xml
@@ -172,6 +172,13 @@
                type="org.lamport.tla.toolbox.tool.tlc.model.Model">
          </adapter>
       </factory>
+      <factory
+            adaptableType="org.lamport.tla.toolbox.tool.tlc.model.Model"
+            class="org.lamport.tla.toolbox.tool.tlc.model.TLCModelFactory">
+         <adapter
+               type="org.eclipse.core.resources.IResource">
+         </adapter>
+      </factory>
    </extension>
    <extension
          point="org.eclipse.core.runtime.adapters">
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java
index 2895e958978c11847717cc4ca07354d8b2c32771..a00778c7dc0695f82366a90ace83a6dde78facff 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/job/TLCJob.java
@@ -146,6 +146,12 @@ public abstract class TLCJob extends AbstractJob implements IModelConfigurationC
             {
                 arguments.add("-simulate");
 
+                final String simuNumTraces = config.getAttribute(IModelConfigurationConstants.LAUNCH_SIMU_NUM_TRACES,
+                        Long.toString(IModelConfigurationDefaults.LAUNCH_SIMU_NUM_TRACES_DEFAULT));
+                if (!"".equals(simuNumTraces) && Long.valueOf(simuNumTraces) != Long.MAX_VALUE) {
+                	arguments.add(String.format("num=%s", Long.valueOf(simuNumTraces)));
+                }
+
                 // look for advanced simulation parameters
                 int traceDepth = config.getAttribute(IModelConfigurationConstants.LAUNCH_SIMU_DEPTH,
                         IModelConfigurationDefaults.LAUNCH_SIMU_DEPTH_DEFAULT);
@@ -248,6 +254,10 @@ public abstract class TLCJob extends AbstractJob implements IModelConfigurationC
         // debugging only
         //arguments.put("-debug", null); 
         
+		// Keep TLC from generating a trace spec. For historic reasons, the Toolbox uses
+		// its own mechanism to generate a trace spec.
+        arguments.add("-noGenerateSpecTE");
+        
         // run in tool mode
         arguments.add("-tool");
         
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java
index 098f4bbf018f9308f941212b04aa54c0bf15b64d..6fed348c4dadc53d433887c4656292d7f7bb4c47 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationConstants.java
@@ -69,6 +69,14 @@ public interface IConfigurationConstants
      * the VIEW to map from variables to values
      */
     public static final String LAUNCH_VIEW = "view";
+    /**
+     * the ALIAS to map from variables to values
+     */
+    public static final String LAUNCH_ALIAS = "alias";
+    /**
+     * the POSTCONDITION to map from variables to values
+     */
+    public static final String LAUNCH_POST_CONDITION = "postCondition";
     /**
      * MC depth first 
      */
@@ -77,6 +85,10 @@ public interface IConfigurationConstants
      * the depth of DFID run 
      */
     public static final String LAUNCH_DFID_DEPTH = "dfidDepth";
+    /**
+     * the number of traces to generate by simulation.
+     */
+    public static final String LAUNCH_SIMU_NUM_TRACES = "simuNumTraces";
     /**
      * the depth of simulation run 
      */
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java
index ed0d470495a7f44842fb64caae61a2069590867e..ac0d6c21c2227c86a1ffc689fe335ea3da34c829 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IConfigurationDefaults.java
@@ -83,6 +83,10 @@ public interface IConfigurationDefaults
      * Default depth for DFID MC is 100 
      */
     public static final int LAUNCH_DFID_DEPTH_DEFAULT = 100;
+    /** 
+     * Default depth for Simulation is 0 
+     */
+    public static final long LAUNCH_SIMU_NUM_TRACES_DEFAULT = Long.MAX_VALUE;
     /** 
      * Default depth for Simulation is 100 
      */
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IModelConfigurationConstants.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IModelConfigurationConstants.java
index 18e1588ca0fb7f4fd2149df9d0fb6dda137381d1..7021df38113d897ce6c8d13bac899bb0f3f8b1e3 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IModelConfigurationConstants.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/IModelConfigurationConstants.java
@@ -83,6 +83,14 @@ public interface IModelConfigurationConstants extends IConfigurationConstants {
      * view
      */
     public static final String MODEL_PARAMETER_VIEW = "modelParameterView";
+    /**
+     * post condition
+     */
+    public static final String MODEL_PARAMETER_POST_CONDITION = "modelParameterPostCondition";
+    /**
+     * alias
+     */
+    public static final String MODEL_PARAMETER_ALIAS  = "modelParameterAlias";
     /**
      * constant expression to be evaluated
      */
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java
index aebc2f6ddb47e28e2faeb9910cfb96cfb9ca8a52..b8ccc3d2a3829511a4d3164e8c35160d42623b0c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TLCModelLaunchDelegate.java
@@ -14,7 +14,6 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -22,7 +21,6 @@ import java.util.Random;
 import java.util.Set;
 import java.util.Vector;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import org.eclipse.core.internal.resources.ResourceException;
@@ -54,12 +52,10 @@ import org.eclipse.debug.core.Launch;
 import org.eclipse.debug.core.model.IProcess;
 import org.eclipse.debug.core.model.IStreamsProxy;
 import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
-import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
 import org.eclipse.jface.text.FindReplaceDocumentAdapter;
 import org.eclipse.jface.text.IDocument;
-import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
@@ -88,6 +84,7 @@ import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceReference;
 
 import pcal.Validator;
+import pcal.Validator.ValidationResult;
 import tla2sany.semantic.ModuleNode;
 import tla2sany.semantic.OpDeclNode;
 import tlc2.TLCGlobals;
@@ -129,20 +126,6 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate
      * Only generate the models but do not run TLC
      */
     public static final String MODE_GENERATE = "generate";
-    
-    private static final AtomicBoolean PERFORM_VALIDATION_BEFORE_LAUNCH = new AtomicBoolean(true);
-    
-    private static final Integer DIVERGENCE_CONTINUE_LAUNCH = Integer.valueOf(1);
-    private static final Integer DIVERGENCE_SHOW_HISTORY = Integer.valueOf(2);
-    private static final LinkedHashMap<String, Integer> DIVERGENCE_DIALOG_BUTTONS;
-    
-    static {
-    	DIVERGENCE_DIALOG_BUTTONS = new LinkedHashMap<>();
-    	DIVERGENCE_DIALOG_BUTTONS.put("&Abort Launch", Integer.valueOf(0));
-    	DIVERGENCE_DIALOG_BUTTONS.put("Continue &Launch", DIVERGENCE_CONTINUE_LAUNCH);
-    	DIVERGENCE_DIALOG_BUTTONS.put("Abort Launch && Show &History", DIVERGENCE_SHOW_HISTORY); // "&&" because "&" is mnemonic.
-    }
-
 
     // Mutex rule for the following jobs to run after each other
     protected MutexRule mutexRule = new MutexRule();
@@ -183,16 +166,16 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate
             return false;
         }
 
+		final AtomicBoolean success = new AtomicBoolean(true);
         final int specType = config.getAttribute(MODEL_BEHAVIOR_SPEC_TYPE, MODEL_BEHAVIOR_TYPE_DEFAULT);
         if ((specType != MODEL_BEHAVIOR_TYPE_NO_SPEC)
-        		&& PERFORM_VALIDATION_BEFORE_LAUNCH.get()
         		&& mode.equals(MODE_MODELCHECK)) {
             final Model model = config.getAdapter(Model.class);
             final IFile rootModule = model.getSpec().toSpec().getRootFile();
-            final Validator.ValidationResult result;
+            final Set<Validator.ValidationResult> results;
 
             try (final InputStream is = rootModule.getContents()) {
-            	result = Validator.validate(is);
+				results = Validator.validate(model.getSpec().toSpec().getRootModule().getRootParseUnit(), is);
             } catch (final IOException e) {
             	monitor.done();
             	
@@ -200,90 +183,110 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate
             }
             
     		final Display d = PlatformUI.getWorkbench().getDisplay();
-    		final AtomicInteger returnCode = new AtomicInteger(-1);
-            switch (result) {
-            	case NO_PLUSCAL_EXISTS:
-            	case NO_DIVERGENCE:
-            		break;
-            	case ERROR_ENCOUNTERED:
-            		d.syncExec(() -> {
-            			final MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(
-    							d.getActiveShell(), "Error encountered",
-    							"Something went wrong attempting to detect divergence between PlusCal and its translation, continue anyway?",
-    							"Do not bug me about PlusCal verification during the rest of my Toolbox session.", false,
-    							null, null);
-    					
-    					if (dialog.getToggleState()) {
-    						PERFORM_VALIDATION_BEFORE_LAUNCH.set(false);
-    					}
-    					
-    					returnCode.set(dialog.getReturnCode());
-            		});
-					
-					if (returnCode.get() == IDialogConstants.NO_ID) {
-						return false;
-					}
-        			break;
-            	case NO_TRANSLATION_EXISTS:
-            		d.syncExec(() -> {
-            			final MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(
-    							d.getActiveShell(), "Translation missing",
-    							"Your spec appears to contain PlusCal but no TLA+ translation, are you sure you want to continue?",
-    							"Do not bug me about PlusCal verification during the rest of my Toolbox session.", false,
-    							null, null);
-    					
-    					if (dialog.getToggleState()) {
-    						PERFORM_VALIDATION_BEFORE_LAUNCH.set(false);
-    					}
-    					
-    					returnCode.set(dialog.getReturnCode());
-            		});
-					
-					if (returnCode.get() == IDialogConstants.NO_ID) {
-						return false;
+    		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. */
+        		d.syncExec(() -> {
+					final MessageDialog md = new MessageDialog(d.getActiveShell(),
+							"The PlusCal algorithm and its translation have been modified after the last translation.",
+							null,
+							String.format(
+									"The PlusCal algorithm and its translation in module %s have been modified since the last translation (chksums mismatch).\n"
+									+ "\nTo permanently disable this warning, change the conjuncts on the \\* BEGIN TRANSLATION line to:\n"
+									+ "\n\t\tchksum(pcal) \\in STRING /\\ chksum(tla) \\in STRING\n"
+									+ "\nWould you like to abort model-checking?",
+									model.getSpec().getRootModuleName()),
+							MessageDialog.WARNING, new String[] { "&Abort Model-Checking", "Continue &Model-Checking",
+									"Abort && Show Spec &History" },
+							0);
+					final int open = md.open();
+        			if (open != 1) {
+        				success.set(false);
+        				if (open == 2) {
+    						final Module m = new Module(model.getSpec().getRootFile());
+    						ShowHistoryHandler.openHistoryForModule(m);
+        				}
+        			}
+        		});
+    		} 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. */
+				/*
+				 * Raising a transient, modal dialog doesn't result in a good UX. Instead, it
+				 * would be best to annotate the model editor with a warning. Unfortunately,
+				 * this is not possible from this non-UI bundle and it's overkill to introduce a
+				 * new listener. Thus, we will raise just another non-modal dialog that informs
+				 * the user how to silence this warning. 
+				 */
+				d.syncExec(() -> {
+					final MessageDialog md = new MessageDialog(d.getActiveShell(),
+							"The TLA+ translation has been modified after the last translation.", null,
+							String.format(
+									"The TLA+ translation in module %s has changed since its last translation (chksum(tla) mismatch).\n"
+									+ "\nTo permanently disable this warning, change the second conjunct on the \\* BEGIN TRANSLATION line to:\n"
+									+ "\n\t\tchksum(tla) \\in STRING\n"
+									+ "\nWould you like to abort model-checking?",
+									model.getSpec().getRootModuleName()),
+							MessageDialog.INFORMATION, new String[] { "&Abort Model-Checking",
+									"Continue &Model-Checking", "Abort && Show Spec &History" },
+							1);
+					final int open = md.open();
+					if (open != 1) {
+						success.set(false);
+						if (open == 2) {
+							final Module m = new Module(model.getSpec().getRootFile());
+							ShowHistoryHandler.openHistoryForModule(m);
+						}
 					}
-        			break;
-            	case NO_CHECKSUMS_EXIST:
-            		d.syncExec(() -> {
-            			final MessageDialogWithToggle dialog = MessageDialogWithToggle.openInformation(
-    							d.getActiveShell(), "A note about your spec",
-    							"Your spec contains PlusCal and a TLA+ translation - but they have not been verified to "
-    									+ "be in sync; consider re-translating the PlusCal algorithm.",
-    							"Do not bug me about PlusCal verification during the rest of my Toolbox session.", false,
-    							null, null);
-    					
-    					if (dialog.getToggleState()) {
-    						PERFORM_VALIDATION_BEFORE_LAUNCH.set(false);
-    					}
-            		});
-					
-            		break;
-            	case DIVERGENCE_EXISTS:
-            		d.syncExec(() -> {
-            			final MessageDialogWithToggle dialog = MessageDialogWithToggle.open(MessageDialogWithToggle.QUESTION,
-    							d.getActiveShell(), "PlusCal out of sync",
-    							"The PlusCal and TLA+ translation in your spec appear to be out of sync - would you like to"
-    									+ " stop the launch?",
-    							"Do not bug me about PlusCal verification during the rest of my Toolbox session.", false,
-    							null, null, SWT.NONE, DIVERGENCE_DIALOG_BUTTONS);
-    					
-    					if (dialog.getToggleState()) {
-    						PERFORM_VALIDATION_BEFORE_LAUNCH.set(false);
-    					}
-    					
-    					returnCode.set(dialog.getReturnCode());
-            		});
-					
-					if (returnCode.get() != DIVERGENCE_CONTINUE_LAUNCH.intValue()) {
-						if (returnCode.get() == DIVERGENCE_SHOW_HISTORY.intValue()) {
+				});
+    		} 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. */
+				d.syncExec(() -> {
+					final MessageDialog md = new MessageDialog(d.getActiveShell(),
+							"PlusCal algorithm has changed but not re-translated", null,
+							String.format(
+									"The PlusCal algorithm in module %s has changed since its last translation (chksum(pcal) mismatch).\n"
+									+ "\nTo permanently disable this warning, change the first conjunct on the \\* BEGIN TRANSLATION line to:\n"
+									+ "\n\t\tchksum(pcal) \\in STRING\n"
+									+ "\nWould you like to abort model-checking?",
+									model.getSpec().getRootModuleName()),
+							MessageDialog.WARNING, new String[] { "&Abort Model-Checking", "Continue &Model-Checking",
+									"Abort && Show Spec &History" },
+							0);
+					final int open = md.open();
+					if (open != 1) {
+						success.set(false);
+						if (open == 2) {
 							final Module m = new Module(model.getSpec().getRootFile());
 							ShowHistoryHandler.openHistoryForModule(m);
 						}
-						
-						return false;
 					}
-            		break;
-            }
+				});
+    		} else if (results.contains(ValidationResult.ERROR_ENCOUNTERED)) {
+        		d.syncExec(() -> {
+        			MessageDialogWithToggle.openWarning(
+							d.getActiveShell(), "Error encountered",
+							"Something went wrong attempting to detect divergence between PlusCal and its translation, continue anyway?");
+					
+    				success.set(false);
+        		});
+    		}
         }
         
 		try {
@@ -293,7 +296,7 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate
 			monitor.done();
 		}
 
-        return true;
+        return success.get();
     }
 
     /**
@@ -586,6 +589,12 @@ public class TLCModelLaunchDelegate extends LaunchConfigurationDelegate
 
                 // view
                 writer.addView(config.getAttribute(LAUNCH_VIEW, TLAConstants.EMPTY_STRING), MODEL_PARAMETER_VIEW);
+                
+				writer.addPostCondition(config.getAttribute(LAUNCH_POST_CONDITION, TLAConstants.EMPTY_STRING),
+						MODEL_PARAMETER_POST_CONDITION);
+				
+				writer.addAlias(config.getAttribute(LAUNCH_ALIAS, TLAConstants.EMPTY_STRING),
+						MODEL_PARAMETER_ALIAS);
             }
 
             // calculator expression
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java
index 76bf6b1b7cd26cceb507e97b852c8e84ea35718e..8547a40b64f8477176d51698e06244e97bcd4247 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/launch/TraceExplorerDelegate.java
@@ -70,6 +70,7 @@ import org.lamport.tla.toolbox.util.ResourceHelper;
 import org.lamport.tla.toolbox.util.TLAMarkerInformationHolder;
 import org.lamport.tla.toolbox.util.UIHelper;
 
+import tla2sany.modanalyzer.SpecObj;
 import tla2sany.semantic.OpDefNode;
 import tlc2.model.Assignment;
 import tlc2.model.Formula;
@@ -438,7 +439,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa
 		writer.addPrimer(ModelHelper.TE_MODEL_NAME, ResourceHelper.getModuleName(model.getSpec().getRootFilename()),
 				model.getTraceExplorerExtends());
 
-        writeModelInfo(config, writer);
+        writeModelInfo(model.getSpec().getValidRootModule(), config, writer);
         
         writer.addTraceFunction(trace);
 
@@ -466,7 +467,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa
         // add the initial state predicate and next state action without
         // the trace exploration expressions in order to determine if they parse
         // initNext[0] is the identifier for init, initNext[1] is the identifier for next
-        final String[] initNextActionConstraint = writer.addInitNext(trace, null);
+        final String[] initNextActionConstraint = writer.addInitNext(trace);
 		if (initNextActionConstraint != null) {
 			initId = initNextActionConstraint[0];
 			nextId = initNextActionConstraint[1];
@@ -646,33 +647,17 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa
 				model.getTraceExplorerExtends());
 
         // write constants, model values, new definitions, definition overrides
-        writeModelInfo(configuration, writer);
+        writeModelInfo(model.getSpec().getValidRootModule(), configuration, writer);
         
         writer.addTraceFunction(trace);
 
         // variables declarations for trace explorer expressions
-        writer.addVariablesAndDefinitions(traceExpressionData, TLAConstants.TraceExplore.TRACE_EXPLORE_EXPRESSIONS, false);
+        writer.addVariablesAndDefinitions(traceExpressionData, TLAConstants.TraceExplore.TRACE_EXPLORE_EXPRESSIONS, true);
 
         // add init and next
         writer.addInitNext(trace, traceExpressionData, initId, nextId, actionConstraintId);
 
-        MCState finalState = trace.get(trace.size() - 1);
-        boolean isBackToState = finalState.isBackToState();
-        boolean isStuttering = finalState.isStuttering();
-
-        // add temporal property or invariant depending on type of trace
-        // read the method comments to see the form of the invariant or property
-        if (isStuttering)
-        {
-            writer.addStutteringProperty(trace.get(trace.size() - 2));
-        } else if (isBackToState)
-        {
-            writer.addBackToStateProperty(trace.get(trace.size() - 2), trace.get(finalState.getStateNumber() - 1));
-        } else
-        {
-            // checking deadlock eliminates the need for the following
-            // writer.addInvariantForTraceExplorer(finalState);
-        }
+        writer.addProperties(trace);
 
         writer.writeFiles(tlaFile, cfgFile, monitor);
 
@@ -729,7 +714,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa
      * @param writer
      * @throws CoreException
      */
-    private void writeModelInfo(final ILaunchConfiguration config, final AbstractSpecWriter writer) throws CoreException
+    private void writeModelInfo(final SpecObj specObj, final ILaunchConfiguration config, final AbstractSpecWriter writer) throws CoreException
     {
         // constants list
     	final List<Assignment> constants = ModelHelper.deserializeAssignmentList(config.getAttribute(MODEL_PARAMETER_CONSTANTS,
@@ -751,7 +736,7 @@ public class TraceExplorerDelegate extends TLCModelLaunchDelegate implements ILa
         List<Assignment> overrides = ModelHelper.deserializeAssignmentList(config.getAttribute(MODEL_PARAMETER_DEFINITIONS,
                 new ArrayList<String>()));
         writer.addFormulaList(SpecWriterUtilities.createOverridesContent(overrides, TLAConstants.Schemes.DEFOV_SCHEME,
-        		ToolboxHandle.getCurrentSpec().getValidRootModule()), TLAConstants.KeyWords.CONSTANT,
+        		specObj), TLAConstants.KeyWords.CONSTANT,
         		MODEL_PARAMETER_DEFINITIONS);
     }
 }
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java
index 8278d3ef3c058c16d3e720288fd88c780a87dfcb..644a0921fc01a8aa7abb8a770d45c7e11b20405c 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/Model.java
@@ -1279,6 +1279,13 @@ public class Model implements IModelConfigurationConstants, IAdaptable {
 		return this.launchConfig.hasAttribute(key);
 	}
 	
+	public long getAttribute(String key, long defaultValue) throws CoreException {
+		// TODO Replace this generic lookup method with real getters for the
+		// various keys. E.g. see getEvalExpression/unsavedSetEvalExpression
+		final String l = this.launchConfig.getAttribute(key, Long.toString(defaultValue));
+		return Long.valueOf(l);
+	}
+	
 	public int getAttribute(String key, int defaultValue) throws CoreException {
 		// TODO Replace this generic lookup method with real getters for the
 		// various keys. E.g. see getEvalExpression/unsavedSetEvalExpression
@@ -1313,6 +1320,10 @@ public class Model implements IModelConfigurationConstants, IAdaptable {
 		}
 	}
 
+	public void setAttribute(String key, long value) {
+		setAttribute(key, Long.toString(value));
+	}
+
 	public void setAttribute(String key, String value) {
 		// TODO Replace this generic lookup method with real getters for the
 		// various keys. E.g. see getEvalExpression/unsavedSetEvalExpression
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCModelFactory.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCModelFactory.java
index d877b865357dc84347e45ebcf4ec2ba058bc8616..31e8b3071d68eee1b6c5e39ad49691cbf704134f 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCModelFactory.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/model/TLCModelFactory.java
@@ -30,6 +30,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.Assert;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdapterFactory;
@@ -54,6 +55,16 @@ public class TLCModelFactory implements IAdapterFactory, ILaunchConfigurationLis
 	 */
 	@SuppressWarnings("unchecked")
 	public <T> T getAdapter(Object adaptableObject, Class<T> adapterType) {
+		if (IResource.class.equals(adapterType) && adaptableObject instanceof Model) {
+			// Convert a Model instance to an IResource (to connect SpecExplorer with ShowInSystemExplorerHandler).
+			final Model model = (Model) adaptableObject;
+			if (model.getFolder().exists()) {
+				// Before a model has been checked or validated, the model folder doesn't exist. If it exists,
+				// a user is likely more interested to open the folder.
+				return (T) model.getFolder();
+			}
+			return (T) model.getFile();
+		}
 		if (!Model.class.equals(adapterType)) {
 			return null;
 		}
@@ -77,7 +88,7 @@ public class TLCModelFactory implements IAdapterFactory, ILaunchConfigurationLis
 	 * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
 	 */
 	public Class<?>[] getAdapterList() {
-		return new Class[] {ILaunchConfiguration.class};
+		return new Class[] {ILaunchConfiguration.class, IResource.class};
 	}
 
 	/* (non-Javadoc)
diff --git a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/util/ModelHelper.java b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/util/ModelHelper.java
index 62c8ff038cbe4d88769562f4a84ee051e5149550..2b1d282a93f8c2b6a91c9c2a39cf604c624ba3ed 100644
--- a/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/util/ModelHelper.java
+++ b/toolbox/org.lamport.tla.toolbox.tool.tlc/src/org/lamport/tla/toolbox/tool/tlc/util/ModelHelper.java
@@ -801,6 +801,12 @@ public class ModelHelper implements IModelConfigurationConstants, IModelConfigur
         } else if (attributeName.equals(MODEL_PARAMETER_VIEW))
         {
             return "View";
+        } else if (attributeName.equals(MODEL_PARAMETER_POST_CONDITION))
+        {
+            return "Post Condition";
+        } else if (attributeName.equals(MODEL_PARAMETER_ALIAS))
+        {
+            return "Alias Expression";
         } else if (attributeName.equals(Model.MODEL_EXPRESSION_EVAL))
         {
             return "Expression";
diff --git a/toolbox/org.lamport.tla.toolbox/plugin.xml b/toolbox/org.lamport.tla.toolbox/plugin.xml
index a27a9698a4c96fc8182075fdcbf7e58b1ac76a96..70f1aba1375343ffd54bf8dca8bfed0f34a97539 100644
--- a/toolbox/org.lamport.tla.toolbox/plugin.xml
+++ b/toolbox/org.lamport.tla.toolbox/plugin.xml
@@ -371,6 +371,10 @@
             id="org.lamport.tla.toolbox.history"
             name="Module History">
       </command>
+      <command
+            id="org.lamport.tla.toolbox.systemExplorer"
+            name="System Explorer">
+      </command>
    </extension>
 <!-- 
  Key bindings
@@ -790,6 +794,11 @@
                label="Show in History"
                style="push">
          </command>
+         <command
+               commandId="org.lamport.tla.toolbox.systemExplorer"
+               label="Open in File Manager"
+               style="push">
+         </command>
       </menuContribution>
 
       <menuContribution
@@ -1065,6 +1074,29 @@
                </and>
          </enabledWhen>
       </handler>
+      <handler
+            class="org.eclipse.ui.internal.ide.handlers.ShowInSystemExplorerHandler"
+            commandId="org.lamport.tla.toolbox.systemExplorer">
+         <enabledWhen>
+               <and>
+                  <count
+                        value="1">
+                  </count>
+                  <with
+                        variable="selection">
+                     <iterate
+                           ifEmpty="false"
+                           operator="or">
+                        <not>
+                        <instanceof
+                              value="org.lamport.tla.toolbox.ui.provider.IGroup">
+                        </instanceof>
+                        </not>
+                     </iterate>
+                  </with>
+               </and>
+         </enabledWhen>
+      </handler>
    </extension>
    <extension
          point="org.eclipse.core.expressions.propertyTesters">
@@ -1192,6 +1224,16 @@
          <adapter
                type="org.eclipse.ui.model.IWorkbenchAdapter">
          </adapter>
+         <adapter
+               type="org.eclipse.core.resources.IResource">
+         </adapter>
+      </factory>
+      <factory
+            adaptableType="org.lamport.tla.toolbox.spec.Module"
+            class="org.lamport.tla.toolbox.util.AdapterFactory">
+         <adapter
+               type="org.eclipse.core.resources.IResource">
+         </adapter>
       </factory>
    </extension>
 <!-- ================================================================================= -->
diff --git a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java
index 125f202e343fffe2faa68985c17815c5a133cff6..6f664abc7048ede28561e012f6323662a61e49e2 100644
--- a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java
+++ b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/spec/parser/ModuleParserLauncher.java
@@ -156,7 +156,7 @@ public class ModuleParserLauncher
             SANY.frontEndInitialize(moduleSpec, outputStr);
             // should cancel?
             checkCancel(monitor);
-            SANY.frontEndParse(moduleSpec, outputStr);
+            SANY.frontEndParse(moduleSpec, outputStr, false);
             // should cancel?
             checkCancel(monitor);
             SANY.frontEndSemanticAnalysis(moduleSpec, outputStr, true);
diff --git a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/AdapterFactory.java b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/AdapterFactory.java
index c099c30fb35c9266b41c7079efc326de02b8eb23..a054b510d7cd419bf70b17220e70b19576a93e3a 100644
--- a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/AdapterFactory.java
+++ b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/AdapterFactory.java
@@ -5,6 +5,7 @@ import java.util.Vector;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.IAdapterFactory;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
@@ -24,18 +25,17 @@ import tla2sany.st.Location;
 /**
  * A toolkit with adapter methods
  * @author Simon Zambrovski
- * @version $Id$
  */
 public class AdapterFactory implements IAdapterFactory
 {
     // list of supported targets
-    private static final Class[] CLASSES = new Class[] { IWorkbenchAdapter.class };
+    private static final Class<?>[] CLASSES = new Class[] { IWorkbenchAdapter.class, IResource.class };
 
     /*
      * (non-Javadoc)
      * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()
      */
-    public Class[] getAdapterList()
+    public Class<?>[] getAdapterList()
     {
         return CLASSES;
     }
@@ -44,15 +44,22 @@ public class AdapterFactory implements IAdapterFactory
      * (non-Javadoc)
      * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)
      */
-    public Object getAdapter(Object adaptableObject, Class adapterType)
+    @SuppressWarnings("unchecked")
+	public <T> T getAdapter(Object adaptableObject, Class<T> adapterType)
     {
         if (adapterType == IWorkbenchAdapter.class)
         {
             if (adaptableObject instanceof Spec)
             {
-                return new SpecWorkbenchAdapter();
+                return (T) new SpecWorkbenchAdapter();
             }
         }
+		if (IResource.class.equals(adapterType) && adaptableObject instanceof Module) {
+			return (T) ((Module) adaptableObject).getResource();
+		}
+		if (IResource.class.equals(adapterType) && adaptableObject instanceof Spec) {
+			return (T) ((Spec) adaptableObject).getRootFile();
+		}
         return null;
     }
 
diff --git a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/RCPNameToFileIStream.java b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/RCPNameToFileIStream.java
index 7e28d9ee4011f23c97704169c920a9a037cd9ae0..ba241075001cb4db79c5c7b5f7206b9f0d71ebc4 100644
--- a/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/RCPNameToFileIStream.java
+++ b/toolbox/org.lamport.tla.toolbox/src/org/lamport/tla/toolbox/util/RCPNameToFileIStream.java
@@ -175,7 +175,7 @@ public class RCPNameToFileIStream implements FilenameToStream
 	private File getFromArchive(String prefix, String name) {
 		final File outputFile = new TLAFile(TMPDIR + File.separator + name, true, this);
 		outputFile.deleteOnExit(); // Written to TMPDIR which is likely deleted regularly anyway.
-		try (FileSystem fileSystem = FileSystems.newFileSystem(new File(prefix).toPath(), null)) {
+		try (FileSystem fileSystem = FileSystems.newFileSystem(new File(prefix).toPath(), (ClassLoader) null)) {
 	        Path fileToExtract = fileSystem.getPath(name);
 	        Files.copy(fileToExtract, outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
 	        return outputFile;