diff --git a/.classpath b/.classpath
index e8e4f613fd49674618a3d0ac5d279e377d9a3b1c..9383d15144712e0f35ce5aeb646ac490db8a38d7 100644
--- a/.classpath
+++ b/.classpath
@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="prism/src"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
-		<attributes>
-			<attribute name="module" value="true"/>
-		</attributes>
-	</classpathentry>
+	<classpathentry kind="src" path="prism/unit-tests"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
 	<classpathentry kind="lib" path="prism/lib/epsgraphics.jar"/>
 	<classpathentry kind="lib" path="prism/lib/jcommon.jar"/>
 	<classpathentry kind="lib" path="prism/lib/jfreechart.jar"/>
diff --git a/.github/workflows/make-tests.yml b/.github/workflows/make-tests.yml
index 8aa9308441f8d392f477c2ab3b54e7af5896b864..22eda7f22540613c0d60c75db740ac164fc27bb1 100644
--- a/.github/workflows/make-tests.yml
+++ b/.github/workflows/make-tests.yml
@@ -25,4 +25,5 @@ jobs:
       working-directory: ./prism
       run: |
         make
+        make unittests
         make tests
diff --git a/prism-tests/bugfixes/export-precision/rare.nm b/prism-tests/bugfixes/export-precision/rare.nm
new file mode 100644
index 0000000000000000000000000000000000000000..407d01938f80e2d5210553581d1926b9698def9b
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm
@@ -0,0 +1,18 @@
+mdp
+
+const double p = 1e-13;
+
+module rare
+  rare : bool init false;
+
+  [tick] !rare -> p:   (rare' = true)
+                + 1-p: true;
+  [tick] rare  -> true;
+endmodule
+
+rewards
+  rare:  1-p;
+  !rare: p;
+//  [tick] rare:  1-p;
+//  [tick] !rare: p;
+endrewards
\ No newline at end of file
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.args b/prism-tests/bugfixes/export-precision/rare.nm.args
new file mode 100644
index 0000000000000000000000000000000000000000..92752a42c5855889fea19e5c320a50af07d4ffe2
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.args
@@ -0,0 +1,4 @@
+-explicit
+-mtbdd
+-sparse
+-hybrid
\ No newline at end of file
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.importexport.auto b/prism-tests/bugfixes/export-precision/rare.nm.importexport.auto
new file mode 100644
index 0000000000000000000000000000000000000000..c04e7fb5a3f690207c074c2a9e88f5d93726d14e
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.importexport.auto
@@ -0,0 +1,7 @@
+# Reduce precision for some tests due to minor differences between the engines
+
+-exportmodelprecision 16 -exportmodel rare.nm.tra
+-exportmodelprecision 16 -exportmodel rare.nm.srew
+
+-exportmodelprecision 16 -mdp -importmodel rare.nm.sta,tra,srew -exportmodel rare.nm.tra
+-exportmodelprecision 16 -mdp -importmodel rare.nm.sta,tra,srew -exportmodel rare.nm.srew
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.props b/prism-tests/bugfixes/export-precision/rare.nm.props
new file mode 100644
index 0000000000000000000000000000000000000000..e4fc9a1dc6be644beb8c114a971a726ef3c138e4
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.props
@@ -0,0 +1,6 @@
+// RESULT (p=1e-13): 1.0
+Pmax=? [F rare];
+
+// RESULT (p=1e-13): true
+0.99 <= Rmin=? [F rare];
+
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.props.args b/prism-tests/bugfixes/export-precision/rare.nm.props.args
new file mode 100644
index 0000000000000000000000000000000000000000..44e8123df489d4e418e54e8b02e07b0bc9656825
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.props.args
@@ -0,0 +1 @@
+-gs
\ No newline at end of file
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.srew b/prism-tests/bugfixes/export-precision/rare.nm.srew
new file mode 100644
index 0000000000000000000000000000000000000000..59d3f4f13a2511ec12746949a97c727e115dc89c
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.srew
@@ -0,0 +1,3 @@
+2 2
+0 1e-13
+1 0.9999999999999
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.sta b/prism-tests/bugfixes/export-precision/rare.nm.sta
new file mode 100644
index 0000000000000000000000000000000000000000..55dbb9c7e1331f61ca4474fa7bce7e4e1ae6a649
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.sta
@@ -0,0 +1,3 @@
+(rare)
+0:(false)
+1:(true)
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.tra b/prism-tests/bugfixes/export-precision/rare.nm.tra
new file mode 100644
index 0000000000000000000000000000000000000000..ab817053b4c60cf3e7ae9d0279575afe2feea8dd
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.tra
@@ -0,0 +1,4 @@
+2 2 3
+0 0 0 0.9999999999999 tick
+0 0 1 1e-13 tick
+1 0 1 1 tick
diff --git a/prism-tests/bugfixes/export-precision/rare.nm.trew b/prism-tests/bugfixes/export-precision/rare.nm.trew
new file mode 100644
index 0000000000000000000000000000000000000000..7c0d8e29f825b8c712d0d36c533c1c814214c20a
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.nm.trew
@@ -0,0 +1,3 @@
+2 2
+0 0 1e-13
+1 1 0.9999999999999
diff --git a/prism-tests/bugfixes/export-precision/rare.pm b/prism-tests/bugfixes/export-precision/rare.pm
new file mode 100644
index 0000000000000000000000000000000000000000..99a73e0c592fa043d545590fa4095777e854af7c
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm
@@ -0,0 +1,18 @@
+dtmc
+
+const double p = 1e-13;
+
+module rare
+  rare : bool init false;
+
+  [tick] !rare -> p:   (rare' = true)
+                + 1-p: true;
+  [tick] rare  -> true;
+endmodule
+
+rewards
+  rare:  1-p;
+  !rare: p;
+//  [tick] rare:  1-p;
+//  [tick] !rare: p;
+endrewards
\ No newline at end of file
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.args b/prism-tests/bugfixes/export-precision/rare.pm.args
new file mode 100644
index 0000000000000000000000000000000000000000..92752a42c5855889fea19e5c320a50af07d4ffe2
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.args
@@ -0,0 +1,4 @@
+-explicit
+-mtbdd
+-sparse
+-hybrid
\ No newline at end of file
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.importexport.auto b/prism-tests/bugfixes/export-precision/rare.pm.importexport.auto
new file mode 100644
index 0000000000000000000000000000000000000000..dd1c2e1ad5b2e751ddc80e91f44b7718ea48d306
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.importexport.auto
@@ -0,0 +1,7 @@
+# Reduce precision for some tests due to minor differences between the engines
+
+-exportmodelprecision 16 -exportmodel rare.pm.tra
+-exportmodelprecision 16 -exportmodel rare.pm.srew
+
+-exportmodelprecision 16 -dtmc -importmodel rare.pm.sta,tra,srew -exportmodel rare.pm.tra
+-exportmodelprecision 16 -dtmc -importmodel rare.pm.sta,tra,srew -exportmodel rare.pm.srew
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.props b/prism-tests/bugfixes/export-precision/rare.pm.props
new file mode 100644
index 0000000000000000000000000000000000000000..76fc9a3fc4597d74904cccfc7bf4691888f28289
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.props
@@ -0,0 +1,6 @@
+// RESULT (p=1e-13): 1.0
+P=? [F rare];
+
+// RESULT (p=1e-13): true
+R>=0.99 [F rare];
+
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.srew b/prism-tests/bugfixes/export-precision/rare.pm.srew
new file mode 100644
index 0000000000000000000000000000000000000000..59d3f4f13a2511ec12746949a97c727e115dc89c
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.srew
@@ -0,0 +1,3 @@
+2 2
+0 1e-13
+1 0.9999999999999
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.sta b/prism-tests/bugfixes/export-precision/rare.pm.sta
new file mode 100644
index 0000000000000000000000000000000000000000..55dbb9c7e1331f61ca4474fa7bce7e4e1ae6a649
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.sta
@@ -0,0 +1,3 @@
+(rare)
+0:(false)
+1:(true)
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.tra b/prism-tests/bugfixes/export-precision/rare.pm.tra
new file mode 100644
index 0000000000000000000000000000000000000000..5244c7f2f63c14d011c3a90cd1148baabbcf0a54
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.tra
@@ -0,0 +1,4 @@
+2 3
+0 0 0.9999999999999
+0 1 1e-13
+1 1 1
diff --git a/prism-tests/bugfixes/export-precision/rare.pm.trew b/prism-tests/bugfixes/export-precision/rare.pm.trew
new file mode 100644
index 0000000000000000000000000000000000000000..7c0d8e29f825b8c712d0d36c533c1c814214c20a
--- /dev/null
+++ b/prism-tests/bugfixes/export-precision/rare.pm.trew
@@ -0,0 +1,3 @@
+2 2
+0 0 1e-13
+1 1 0.9999999999999
diff --git a/prism-tests/functionality/export/ctmc/cluster.sm.exportmodel.auto b/prism-tests/functionality/export/ctmc/cluster.sm.exportmodel.auto
index 8d6faaee8e6809b7538750e6b767f695e9e98e1a..a70c568d2615015025925b96fc0a428b4f8c6cd2 100644
--- a/prism-tests/functionality/export/ctmc/cluster.sm.exportmodel.auto
+++ b/prism-tests/functionality/export/ctmc/cluster.sm.exportmodel.auto
@@ -1,18 +1,20 @@
+# Reduce precision for some tests due to minor differences between the engines
+
 # Export all model info in different formats
 
--exportmodel cluster.sm.all
--exportmodel cluster.sm.mrmc.all:mrmc
--exportmodel cluster.sm.matlab.all:matlab
--exportmodel cluster.sm.rows.all:rows
+-exportmodelprecision 16 -exportmodel cluster.sm.all
+-exportmodelprecision 16 -exportmodel cluster.sm.mrmc.all:mrmc
+-exportmodelprecision 16 -exportmodel cluster.sm.matlab.all:matlab
+-exportmodelprecision 16 -exportmodel cluster.sm.rows.all:rows
 
 # Export model info separately (for a few formats)
 
--exportmodel cluster.sm.tra
+-exportmodelprecision 16 -exportmodel cluster.sm.tra
 -exportmodel cluster.sm.lab
 -exportmodel cluster.sm.sta
 -exportmodel cluster.sm.srew
 -exportmodel cluster.sm.trew
--exportmodel cluster.sm.mrmc.tra:mrmc
+-exportmodelprecision 16 -exportmodel cluster.sm.mrmc.tra:mrmc
 -exportmodel cluster.sm.mrmc.lab:mrmc
 -exportmodel cluster.sm.mrmc.sta:mrmc
 -exportmodel cluster.sm.mrmc.srew:mrmc
diff --git a/prism-tests/functionality/export/dot/cluster.sm.dot.auto b/prism-tests/functionality/export/dot/cluster.sm.dot.auto
index 163c4ce97f5c00e93fa894d7c902ea4f7b18e864..fe5db71578bab5161f8c477d0be0e985bd874192 100644
--- a/prism-tests/functionality/export/dot/cluster.sm.dot.auto
+++ b/prism-tests/functionality/export/dot/cluster.sm.dot.auto
@@ -1,2 +1,4 @@
--exporttransdot cluster.sm.dot
--exporttransdotstates cluster.sm.sta.dot
+# Reduce precision for some tests due to minor differences between the engines
+
+-exportmodelprecision 16 -exporttransdot cluster.sm.dot
+-exportmodelprecision 16 -exporttransdotstates cluster.sm.sta.dot
diff --git a/prism-tests/functionality/export/dot/robot.prism.dot.auto b/prism-tests/functionality/export/dot/robot.prism.dot.auto
index 910b64faba1d332e1c9473f77c3eea524691b9ec..58c6e6f4b73596dcc52e663398c277a7dcefaa42 100644
--- a/prism-tests/functionality/export/dot/robot.prism.dot.auto
+++ b/prism-tests/functionality/export/dot/robot.prism.dot.auto
@@ -1,2 +1,4 @@
--exporttransdot robot.prism.dot
--exporttransdotstates robot.prism.sta.dot
+# Reduce precision for some tests due to minor differences between the engines
+
+-exportmodelprecision 16 -exporttransdot robot.prism.dot
+-exportmodelprecision 16 -exporttransdotstates robot.prism.sta.dot
diff --git a/prism-tests/functionality/export/mdp/robot.prism.exportmodel.auto b/prism-tests/functionality/export/mdp/robot.prism.exportmodel.auto
index c8bc43acdb694162825f638c5306783185634787..65ce80074bd2993fe4d769185b1032e864ac0301 100644
--- a/prism-tests/functionality/export/mdp/robot.prism.exportmodel.auto
+++ b/prism-tests/functionality/export/mdp/robot.prism.exportmodel.auto
@@ -1,21 +1,23 @@
+# Reduce precision for some tests due to minor differences between the engines
+
 # Export all model info in different formats
 
--exportmodel robot.prism.all
--exportmodel robot.prism.mrmc.all:mrmc
--exportmodel robot.prism.matlab.all:matlab
--exportmodel robot.prism.rows.all:rows
+-exportmodelprecision 16 -exportmodel robot.prism.all
+-exportmodelprecision 16 -exportmodel robot.prism.mrmc.all:mrmc
+-exportmodelprecision 16 -exportmodel robot.prism.matlab.all:matlab
+-exportmodelprecision 16 -exportmodel robot.prism.rows.all:rows
 
 # Export model info separately (for a few formats)
 
--exportmodel robot.prism.tra
+-exportmodelprecision 16 -exportmodel robot.prism.tra
 -exportmodel robot.prism.lab
 -exportmodel robot.prism.sta
--exportmodel robot.prism.srew
+-exportmodelprecision 16 -exportmodel robot.prism.srew
 -exportmodel robot.prism.trew
 -exportmodel robot.prism.mrmc.tra:mrmc
 -exportmodel robot.prism.mrmc.lab:mrmc
 -exportmodel robot.prism.mrmc.sta:mrmc
--exportmodel robot.prism.mrmc.srew:mrmc
+-exportmodelprecision 16 -exportmodel robot.prism.mrmc.srew:mrmc
 -exportmodel robot.prism.mrmc.trew:mrmc
 
 # Export model info separately (for a few formats) -explicit
@@ -23,7 +25,7 @@
 # -exportmodel robot.prism.tra -ex # order different
 -exportmodel robot.prism.sta -ex
 -exportmodel robot.prism.lab -ex
--exportmodel robot.prism.srew -ex
+-exportmodelprecision 16 -exportmodel robot.prism.srew -ex
 #-exportmodel robot.prism.trew -ex
 #-exportmodel robot.prism.mrmc.tra:mrmc -ex
 #-exportmodel robot.prism.mrmc.sta:mrmc -ex
diff --git a/prism-tests/functionality/import/badge.pepa.importexport.auto b/prism-tests/functionality/import/badge.pepa.importexport.auto
index cbd661d0809cf84c54b15e14a44091a81aabfc12..27594350fd81006de3b621465a94a3fd3646de7f 100644
--- a/prism-tests/functionality/import/badge.pepa.importexport.auto
+++ b/prism-tests/functionality/import/badge.pepa.importexport.auto
@@ -1 +1,3 @@
--importpepa badge.pepa -exportmodel badge.pepa.sta,tra,lab
+# Reduce precision for some tests due to minor differences between the engines
+
+-importpepa badge.pepa -exportmodelprecision 16 -exportmodel badge.pepa.sta,tra,lab
diff --git a/prism-tests/functionality/import/robot.prism.importexport.auto b/prism-tests/functionality/import/robot.prism.importexport.auto
index 622f5609d71835584286b91f106d6cdfa74682fc..2520cbf00a0643df05f72409de5204e6dde7b705 100644
--- a/prism-tests/functionality/import/robot.prism.importexport.auto
+++ b/prism-tests/functionality/import/robot.prism.importexport.auto
@@ -1,3 +1,5 @@
--mdp -importmodel robot.all -exportmodel robot.sta,tra,lab,srew
+# Reduce precision for some tests due to minor differences between the engines
+
+-mdp -importmodel robot.all -exportmodelprecision 16 -exportmodel robot.sta,tra,lab,srew
 -mdp -importmodel robot.all -exportmodel robot.sta,tra,lab,srew -ex
 -importmodel robot.all -exportmodel robot.sta,tra,lab,srew -ex
diff --git a/prism/.classpath b/prism/.classpath
index 7b56dae84013b22578d85638eae5b40052928e5f..f81095ef0758573bbe251fa57d8569d54a13dc13 100644
--- a/prism/.classpath
+++ b/prism/.classpath
@@ -1,7 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="unit-tests"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
 	<classpathentry kind="lib" path="lib/epsgraphics.jar"/>
 	<classpathentry kind="lib" path="lib/jcommon.jar"/>
 	<classpathentry kind="lib" path="lib/jfreechart.jar" sourcepath="/jfreechart/source"/>
diff --git a/prism/Makefile b/prism/Makefile
index b6b05dc739a99d6ac6a74943e478829a1681a50d..b179d1a404fe64c0c1b6d548bcdb26c2b82c287a 100644
--- a/prism/Makefile
+++ b/prism/Makefile
@@ -20,6 +20,7 @@ export PRISM_LIB_DIR     = lib
 export PRISM_INCLUDE_DIR = include
 export PRISM_IMAGES_DIR  = images
 export PRISM_DTDS_DIR    = dtds
+export PRISM_TESTS_DIR   = unit-tests
 
 # Location of CUDD (used to be variable; now mainly fixed with the git repo layout)
 
@@ -202,6 +203,7 @@ else
 endif
 LD = $(CXX)
 JAVAC = javac
+JAVA = java
 
 export CC CXX LD JAVAC JAVACC
 
@@ -451,6 +453,14 @@ make_dirs:
 		(dos2unix $(PRISM_INCLUDE_DIR)/jni/*.h) \
 	fi;
 
+# Compile unit tests
+make_tests:
+	@echo Making $(PRISM_TESTS_DIR) ...; \
+	(cd $(PRISM_TESTS_DIR) && \
+	$(MAKE) \
+	CLASSPATHSEP="$(CLASSPATHSEP)") \
+	|| exit 1;
+
 # Copy/modify the launch scripts and put in the bin directory
 bin_scripts:
 	@for target in $(BIN_TARGETS); do \
@@ -542,6 +552,11 @@ count_loc:
 # Testing #
 ###########
 
+# Run all unit tests
+unittests: make_tests
+	# Provide Regex to match our test classes. If none is given, only certain test classes are excluded by default.
+	$(JAVA) -jar lib/junit-platform-console-standalone.jar -cp classes --include-classname '^(Test.*|.+[.$$]Test.*|.+Tests?[.$$].+|.*Tests?)$$' -scan-classpath
+
 # Run a single test case from the test suite (useful quick check that the build was ok)
 test:
 	bin/prism ../prism-tests/functionality/verify/dtmcs/dtmc_pctl.pm ../prism-tests/functionality/verify/dtmcs/dtmc_pctl.pm.props -prop 2 -test
@@ -562,7 +577,7 @@ testyices:
 # Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
 tests: testslocal
 	@if [ -d ../prism-tests ]; then \
-	  cd ../prism-tests && "$(PWD)"/etc/scripts/prism-auto -t -m . -p "$(PWD)"/bin/prism --nailgun --ngprism "$(PWD)"/bin/ngprism $(TESTS_ARGS); \
+	  etc/scripts/prism-auto -t -m ../prism-tests -p bin/prism --nailgun --ngprism bin/ngprism $(TESTS_ARGS); \
 	else \
 	  echo "Skipping tests"; \
 	fi
@@ -570,13 +585,13 @@ tests: testslocal
 # Just display the command to run the test suite on this version of PRISM
 # Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
 testsecho:
-	@echo etc/scripts/prism-auto -t -m --nailgun --ngprism bin/ngprism ../prism-tests -p bin/prism $(TESTS_ARGS)
+	@echo etc/scripts/prism-auto -t -m ../prism-tests -p bin/prism --nailgun --ngprism bin/ngprism $(TESTS_ARGS)
 
 # Run local tests (in ./tests)
 # Optionally, extra arguments for prism-auto are picked up via variable TESTS_ARGS
 testslocal:
 	@if [ -d tests ]; then \
-	  cd tests && "$(PWD)"/etc/scripts/prism-auto -t -m . -p "$(PWD)"/bin/prism --nailgun --ngprism "$(PWD)"/bin/ngprism $(TESTS_ARGS); \
+	  etc/scripts/prism-auto -t -m tests -p bin/prism --nailgun --ngprism bin/ngprism $(TESTS_ARGS); \
 	else \
 	  echo "Skipping local tests"; \
 	fi
@@ -587,10 +602,9 @@ testslocal:
 # - We run with --test-all, as failures for some engines should not abort the tests
 # - We run with a timeout of 1 minute, as some engines take a long time for some properties
 testsfull:
-	cd ../prism-tests && \
-	"$(PWD)"/etc/scripts/prism-auto -t -m . \
-	--skip-export-runs --skip-duplicate-runs --test-all -a all-engines.args --timeout 1m \
-	-p "$(PWD)"/bin/prism --nailgun $(TESTS_ARGS);
+	etc/scripts/prism-auto -t -m ../prism-tests \
+	--skip-export-runs --skip-duplicate-runs --test-all -a ../prism-tests/all-engines.args --timeout 1m \
+	-p bin/prism --nailgun $(TESTS_ARGS);
 
 ##########################
 # Building distributions #
@@ -696,7 +710,7 @@ clean: checks
 celan: clean
 
 # Clean PRISM + CUDD and external libs
-clean_all: checks clean_cudd clean_ppl clean_ext clean
+clean_all: checks clean_cudd clean_ppl clean_ext clean clean_tests
 
 clean_cudd:
 	@(cd $(CUDD_DIR) && $(MAKE) distclean)
@@ -713,6 +727,9 @@ clean_ext:
 	  || exit 1; \
 	done )
 
+clean_tests:
+	@(cd $(PRISM_TESTS_DIR) && $(MAKE) clean)
+
 # Remove just the prism.jar binary
 clean_binary:
 	@echo "Removing JAR file ($(PRISM_LIB_DIR)/prism.jar)..."
diff --git a/prism/etc/scripts/prism-auto b/prism/etc/scripts/prism-auto
index 9726ed762392fce113ae7ed61e10540190f87b3e..bc7f80b9f6aec72fea81ce89340023e9d9fe2fbb 100755
--- a/prism/etc/scripts/prism-auto
+++ b/prism/etc/scripts/prism-auto
@@ -366,7 +366,7 @@ def prependToFile(prefix, option):
 
 def expandFilenames(args, dir=""):
     def isImportExportArg(arg):
-        return (arg.startswith("-export") or arg.startswith("-import"))
+        return (arg.startswith("-export") or arg.startswith("-import")) and not arg=="-exportmodelprecision"
     if args:
         return [args[0]] + [expandName(dir, args[i+1]) if isImportExportArg(args[i]) else args[i+1] for i in range(len(args)-1)]
     else:
@@ -376,7 +376,7 @@ def expandFilenames(args, dir=""):
 
 def renameExports(prefix, args):
     def isExportArg(arg):
-        return arg.startswith("-export")
+        return arg.startswith("-export") and not arg=="-exportmodelprecision"
     if args:
         return [args[0]] + [prependToFile(prefix, args[i+1]) if isExportArg(args[i]) else args[i+1] for i in range(len(args)-1)]
     else:
@@ -396,7 +396,7 @@ def hasExportSwitches(args):
 # and that multiple reward structures will result in filenames extended with a number
 
 def getExpectedOutFilesFromArgs(args):
-    options = [args[i+1] for i in range(len(args)-1) if args[i].startswith("-export")]
+    options = [args[i+1] for i in range(len(args)-1) if args[i].startswith("-export") and not args[i]=="-exportmodelprecision"]
     # Sometimes there are options appended, after a ":" - remove these
     files = map(lambda option: option.split(':')[0], options)
 
@@ -623,9 +623,9 @@ def runPrism(args, bmArgs, dir=""):
             os.makedirs(logDir)
         logFile = os.path.join(logDir, createLogFileName(args, dir))
         #f = open(logFile, 'w')
-        prismArgs = prismArgs + ['-mainlog', logFile]
-        exitCode = execute(prismArgs)
-        #exitCode = subprocess.Popen(prismArgs, cwd=dir, stdout=f).wait()
+        prismArgsExec = prismArgs + ['-mainlog', logFile]
+        exitCode = execute(prismArgsExec)
+        #exitCode = subprocess.Popen(prismArgsExec, cwd=dir, stdout=f).wait()
     elif options.test or options.ddWarnings:
         # create logfile
         if isWindows():
@@ -641,8 +641,8 @@ def runPrism(args, bmArgs, dir=""):
             f.close()
         # we want cleanup when we are done, as this is a real temporary file
         cleanupLogFile = True
-        prismArgs = prismArgs + ['-mainlog', logFile]
-        exitCode = execute(prismArgs)
+        prismArgsExec = prismArgs + ['-mainlog', logFile]
+        exitCode = execute(prismArgsExec)
     else:
         exitCode = execute(prismArgs)
     # Extract DD reference count warnings
@@ -681,6 +681,10 @@ def runPrism(args, bmArgs, dir=""):
                     printTestResult(line)
             print("To see log file, run:")
             print("edit " + logFile)
+            prismArgsPrint = prismArgs.copy()
+            prismArgsPrint[0] = options.prismExec
+            print("To re-run failing test:")
+            print(' '.join(prismArgsPrint))
         else:
             # in verbose test mode, print extra info and increment failure counter
             printColoured('FAILURE', "\n[Exit code: " + str(exitCode) + "]")
diff --git a/prism/include/PrismNativeGlob.h b/prism/include/PrismNativeGlob.h
index af85536b48b6a672e117d2ae8f9022807acf856b..2c79635f95891ed5f8c9d1c0ddcf7a65038df3fb 100644
--- a/prism/include/PrismNativeGlob.h
+++ b/prism/include/PrismNativeGlob.h
@@ -115,6 +115,7 @@ EXPORT extern double lin_eq_method_param;
 EXPORT extern int term_crit;
 EXPORT extern double term_crit_param;
 EXPORT extern int max_iters;
+EXPORT extern int export_model_precision;
 // use "compact modified" sparse matrix storage?
 EXPORT extern bool compact;
 // sparse bits info
diff --git a/prism/include/jni/prism_PrismNative.h b/prism/include/jni/prism_PrismNative.h
index b8e7f14f090c11be4c240a57a3e79f548c730348..33be832a47cd981697de1676f2083bf3f55214b6 100644
--- a/prism/include/jni/prism_PrismNative.h
+++ b/prism/include/jni/prism_PrismNative.h
@@ -71,6 +71,14 @@ JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetTermCritParam
 JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetMaxIters
   (JNIEnv *, jclass, jint);
 
+/*
+ * Class:     prism_PrismNative
+ * Method:    PN_SetExportModelPrecision
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetExportModelPrecision
+  (JNIEnv *, jclass, jint);
+
 /*
  * Class:     prism_PrismNative
  * Method:    PN_SetSBMaxMem
diff --git a/prism/lib/README.md b/prism/lib/README.md
index c5e5ce04de1453ebea5b481f9b2e0ef67a3e2262..6ca08b985d5aa74ca616e9dc5a96c3e8c6128d5f 100644
--- a/prism/lib/README.md
+++ b/prism/lib/README.md
@@ -8,6 +8,7 @@ To simplify maintenance of scripts and config files, we mostly omit version numb
 * jfreechart.jar - JFreeChart, version 1.0.13
 * jhoafparser.jar - jhoafparser, version 1.1.1
 * nailgun-server.jar - Nailgun, version 0.9.2-SNAPSHOT
+* junit-platform-console-standalone.jar - JUnit 5 executable & dependencies, version 1.7.2
 
 See here for more details and links:
 
diff --git a/prism/lib/junit-platform-console-standalone.jar b/prism/lib/junit-platform-console-standalone.jar
new file mode 100644
index 0000000000000000000000000000000000000000..f37056501f4ff5c4cab960b9bf06bddad14b4afa
Binary files /dev/null and b/prism/lib/junit-platform-console-standalone.jar differ
diff --git a/prism/src/common/iterable/Range.java b/prism/src/common/iterable/Range.java
index bfa1b1475d89bd653da2f8352c3ce30537b3146d..55f5bcfe0849fe289f88237f0449d00e883cf4ac 100644
--- a/prism/src/common/iterable/Range.java
+++ b/prism/src/common/iterable/Range.java
@@ -250,7 +250,7 @@ public class Range implements FunctionalPrimitiveIterable.OfInt
 	/**
 	 * A abstract base class for an Iterator from {@code first} (inclusive) and {@code last} (exclusive) with a custom step width.
 	 */
-	protected abstract class RangeIterator implements FunctionalPrimitiveIterator.OfInt
+	public abstract class RangeIterator implements FunctionalPrimitiveIterator.OfInt
 	{
 		protected int next;
 
diff --git a/prism/src/explicit/DTMC.java b/prism/src/explicit/DTMC.java
index 64de5c3453eda34c98ebcee35aa8a92ff03e057b..3688e1db1bb96a0376876eb864deb4c0847f3d47 100644
--- a/prism/src/explicit/DTMC.java
+++ b/prism/src/explicit/DTMC.java
@@ -53,7 +53,7 @@ public interface DTMC extends Model
 	}
 
 	@Override
-	default void exportToPrismExplicitTra(PrismLog out)
+	default void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Output transitions to .tra file
 		int numStates = getNumStates();
@@ -69,7 +69,7 @@ public interface DTMC extends Model
 			// Print out (sorted) transitions
 			for (Map.Entry<Integer, Pair<Double, Object>> e : sorted.entrySet()) {
 				// Note use of PrismUtils.formatDouble to match PRISM-exported files
-				out.print(i + " " + e.getKey() + " " + PrismUtils.formatDouble(e.getValue().first));
+				out.print(i + " " + e.getKey() + " " + PrismUtils.formatDouble(precision, e.getValue().first));
 				Object action = e.getValue().second; 
 				if (action != null && !"".equals(action)) {
 					out.print(" " + action);
@@ -81,7 +81,7 @@ public interface DTMC extends Model
 	}
 
 	@Override
-	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		// Iterate through outgoing transitions for this state
 		Iterator<Map.Entry<Integer, Double>> iter = getTransitionsIterator(i);
@@ -91,7 +91,7 @@ public interface DTMC extends Model
 			out.print(i + " -> " + e.getKey());
 			// Annotate this arrow with the probability 
 			explicit.graphviz.Decoration d = new explicit.graphviz.Decoration();
-			d.setLabel(e.getValue().toString());
+			d.setLabel(PrismUtils.formatDouble(precision, e.getValue()));
 			// Apply any other decorators requested
 			if (decorators != null) {
 				for (Decorator decorator : decorators) {
@@ -104,7 +104,7 @@ public interface DTMC extends Model
 	}
 
 	@Override
-	default void exportToPrismLanguage(final String filename) throws PrismException
+	default void exportToPrismLanguage(final String filename, int precision) throws PrismException
 	{
 		try (FileWriter out = new FileWriter(filename)) {
 			out.write(getModelType().keyword() + "\n");
@@ -125,7 +125,7 @@ public interface DTMC extends Model
 					else
 						out.write("+");
 					// Note use of PrismUtils.formatDouble to match PRISM-exported files
-					out.write(PrismUtils.formatDouble(transition.getValue()) + ":(x'=" + transition.getKey() + ")");
+					out.write(PrismUtils.formatDouble(precision, transition.getValue()) + ":(x'=" + transition.getKey() + ")");
 				}
 				out.write(";\n");
 				sorted.clear();
diff --git a/prism/src/explicit/FastAdaptiveUniformisationModelChecker.java b/prism/src/explicit/FastAdaptiveUniformisationModelChecker.java
index 3549fa429f0115b388e4664dfaf61fe673c7ff8c..425cf12c1eded70a3bc0d8c2e2f1a12693a31721 100644
--- a/prism/src/explicit/FastAdaptiveUniformisationModelChecker.java
+++ b/prism/src/explicit/FastAdaptiveUniformisationModelChecker.java
@@ -215,7 +215,7 @@ public class FastAdaptiveUniformisationModelChecker extends PrismComponent
 		mainLog.println("\nTotal probability lost is : " + fau.getTotalDiscreteLoss());
 		mainLog.println("Maximal number of states stored during analysis : " + fau.getMaxNumStates());
 
-		return new Result(new Double(fau.getValue()));
+		return new Result(Double.valueOf(fau.getValue()));
 	}
 
 	/**
@@ -247,6 +247,6 @@ public class FastAdaptiveUniformisationModelChecker extends PrismComponent
 		fau.computeTransientProbsAdaptive(time);
 		mainLog.println("\nTotal probability lost is : " + fau.getTotalDiscreteLoss());
 		mainLog.println("Maximal number of states stored during analysis : " + fau.getMaxNumStates());
-		return new Result(new Double(fau.getValue()));
+		return new Result(Double.valueOf(fau.getValue()));
 	}
 }
diff --git a/prism/src/explicit/IterationMethod.java b/prism/src/explicit/IterationMethod.java
index 9c7d611549bc9841b1fb921ae982d6b56eb98a9c..6113562e220eaf4080dd0671beb0f96ae05691df 100644
--- a/prism/src/explicit/IterationMethod.java
+++ b/prism/src/explicit/IterationMethod.java
@@ -47,6 +47,8 @@ import prism.PrismUtils;
  */
 public abstract class IterationMethod {
 
+	public static final int LOGGING_PRECISION = 12;
+
 	/**
 	 * Interface for an object that provides the basic steps for a value iteration.
 	 */
@@ -624,14 +626,14 @@ public abstract class IterationMethod {
 				if (done) {
 					maxError = PrismUtils.measureSupNormInterval(below.getSolnVector(), above.getSolnVector(), absolute);
 					mc.getLog().println("Max " + (!absolute ? "relative ": "") +
-							"diff between upper and lower bound on convergence: " + PrismUtils.formatDouble(maxError));
+							"diff between upper and lower bound on convergence: " + PrismUtils.formatDouble(LOGGING_PRECISION, maxError));
 					done = true;
 				}
 
 				if (!done && updatesTimer.triggered()) {
 					double diff = PrismUtils.measureSupNormInterval(below.getSolnVector(), above.getSolnVector(), absolute);
 					mc.getLog().print("Iteration " + iters + ": ");
-					mc.getLog().print("max " + (absolute ? "" : "relative ") + "diff=" + PrismUtils.formatDouble(diff));
+					mc.getLog().print("max " + (absolute ? "" : "relative ") + "diff=" + PrismUtils.formatDouble(LOGGING_PRECISION, diff));
 					mc.getLog().println(", " + PrismUtils.formatDouble2dp(updatesTimer.elapsedMillisTotal() / 1000.0) + " sec so far");
 				}
 			}
@@ -766,7 +768,7 @@ public abstract class IterationMethod {
 						if (!doneSCC && updatesTimer.triggered()) {
 							double diff = PrismUtils.measureSupNormInterval(below.getSolnVector(), above.getSolnVector(), absolute, statesForSCC.iterator());
 							mc.getLog().print("Iteration " + iters + ": ");
-							mc.getLog().print("max " + (absolute ? "" : "relative ") + "diff (for iteration " + itersInSCC + " in current SCC " + (finishedNonSingletonSCCs+1) + " of " + numNonSingletonSCCs + ") = " + PrismUtils.formatDouble(diff));
+							mc.getLog().print("max " + (absolute ? "" : "relative ") + "diff (for iteration " + itersInSCC + " in current SCC " + (finishedNonSingletonSCCs+1) + " of " + numNonSingletonSCCs + ") = " + PrismUtils.formatDouble(LOGGING_PRECISION, diff));
 							mc.getLog().println(", " + PrismUtils.formatDouble2dp(updatesTimer.elapsedMillisTotal() / 1000.0) + " sec so far");
 						}
 					}
@@ -790,7 +792,7 @@ public abstract class IterationMethod {
 			if (done) {
 				maxError = PrismUtils.measureSupNormInterval(below.getSolnVector(), above.getSolnVector(), absolute);
 				mc.getLog().println("Max " + (absolute ? "" : "relative ") +
-						"diff between upper and lower bound on convergence: " + PrismUtils.formatDouble(maxError));
+						"diff between upper and lower bound on convergence: " + PrismUtils.formatDouble(LOGGING_PRECISION, maxError));
 				done = true;
 			}
 
diff --git a/prism/src/explicit/LTS.java b/prism/src/explicit/LTS.java
index 055011400f83c1493ad1243b779c5ccd9db11882..a855c247e7b676d37d4cd6419e0693158399a494 100644
--- a/prism/src/explicit/LTS.java
+++ b/prism/src/explicit/LTS.java
@@ -47,7 +47,7 @@ public interface LTS extends NondetModel
 	}
 
 	@Override
-	default void exportToPrismExplicitTra(PrismLog out)
+	default void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Output transitions to .tra file
 		int numStates = getNumStates();
@@ -63,7 +63,7 @@ public interface LTS extends NondetModel
 	}
 
 	@Override
-	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		// Iterate through outgoing transitions (i.e. choices) for this state
 		int numChoices = getNumChoices(i);
@@ -86,7 +86,7 @@ public interface LTS extends NondetModel
 	}
 
 	@Override
-	default void exportToPrismLanguage(String filename) throws PrismException
+	default void exportToPrismLanguage(String filename, int precision) throws PrismException
 	{
 		throw new UnsupportedOperationException();
 	}
@@ -112,7 +112,7 @@ public interface LTS extends NondetModel
 	// Accessors (for NondetModel) - default implementations
 	
 	@Override
-	default void exportToDotFileWithStrat(PrismLog out, BitSet mark, int[] strat)
+	default void exportToDotFileWithStrat(PrismLog out, BitSet mark, int[] strat, int precision)
 	{
 		throw new UnsupportedOperationException();
 	}
diff --git a/prism/src/explicit/MDP.java b/prism/src/explicit/MDP.java
index facc73a58fb55be78273c2278916d11fd794dd01..60e0519d70fe2c92be30975d779def5538fd61ed 100644
--- a/prism/src/explicit/MDP.java
+++ b/prism/src/explicit/MDP.java
@@ -66,7 +66,7 @@ public interface MDP extends MDPGeneric<Double>
 	}
 
 	@Override
-	default void exportToPrismExplicitTra(PrismLog out)
+	default void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Output transitions to .tra file
 		int numStates = getNumStates();
@@ -84,7 +84,7 @@ public interface MDP extends MDPGeneric<Double>
 				// Print out (sorted) transitions
 				for (Map.Entry<Integer, Double> e : sorted.entrySet()) {
 					// Note use of PrismUtils.formatDouble to match PRISM-exported files
-					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(e.getValue()));
+					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(precision, e.getValue()));
 					Object action = getAction(i, j);
 					out.print(action == null ? "\n" : (" " + action + "\n"));
 				}
@@ -94,7 +94,7 @@ public interface MDP extends MDPGeneric<Double>
 	}
 
 	@Override
-	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		// Iterate through outgoing choices for this state
 		int numChoices = getNumChoices(i);
@@ -125,7 +125,7 @@ public interface MDP extends MDPGeneric<Double>
 				out.print(nij + " -> " + e.getKey() + " ");
 				// Annotate this arrow with the probability 
 				d = new explicit.graphviz.Decoration();
-				d.setLabel(e.getValue().toString());
+				d.setLabel(PrismUtils.formatDouble(precision, e.getValue()));
 				// Apply any other decorators requested
 				if (decorators != null) {
 					for (Decorator decorator : decorators) {
@@ -139,7 +139,7 @@ public interface MDP extends MDPGeneric<Double>
 	}
 
 	@Override
-	default void exportToPrismLanguage(final String filename) throws PrismException
+	default void exportToPrismLanguage(final String filename, int precision) throws PrismException
 	{
 		try (FileWriter out = new FileWriter(filename)) {
 			// Output transitions to PRISM language file
@@ -165,7 +165,7 @@ public interface MDP extends MDPGeneric<Double>
 						else
 							out.write("+");
 						// Note use of PrismUtils.formatDouble to match PRISM-exported files
-						out.write(PrismUtils.formatDouble(e.getValue()) + ":(x'=" + e.getKey() + ")");
+						out.write(PrismUtils.formatDouble(precision, e.getValue()) + ":(x'=" + e.getKey() + ")");
 					}
 					out.write(";\n");
 					sorted.clear();
@@ -210,7 +210,7 @@ public interface MDP extends MDPGeneric<Double>
 	}
 	
 	@Override
-	default void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[])
+	default void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[], int precision)
 	{
 		int numStates = getNumStates();
 		out.print("digraph " + getModelType() + " {\nnode [shape=box];\n");
@@ -230,7 +230,7 @@ public interface MDP extends MDPGeneric<Double>
 				Iterator<Map.Entry<Integer, Double>> iter = getTransitionsIterator(i, j);
 				while (iter.hasNext()) {
 					Map.Entry<Integer, Double> e = iter.next();
-					out.print(nij + " -> " + e.getKey() + " [ label=\"" + e.getValue() + "\"" + style + " ];\n");
+					out.print(nij + " -> " + e.getKey() + " [ label=\"" + PrismUtils.formatDouble(precision, e.getValue()) + "\"" + style + " ];\n");
 				}
 			}
 		}
diff --git a/prism/src/explicit/MDPModelChecker.java b/prism/src/explicit/MDPModelChecker.java
index 90611458a055e1c6e84bb98b8af9ecd31d5cdb50..3faeb548829d81fea52df5e057d4f18f886f1419 100644
--- a/prism/src/explicit/MDPModelChecker.java
+++ b/prism/src/explicit/MDPModelChecker.java
@@ -484,7 +484,8 @@ public class MDPModelChecker extends ProbModelChecker
 			}
 			// Export
 			PrismLog out = new PrismFileLog(exportAdvFilename);
-			new DTMCFromMDPMemorylessAdversary(mdp, strat).exportToPrismExplicitTra(out);
+			int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
+			new DTMCFromMDPMemorylessAdversary(mdp, strat).exportToPrismExplicitTra(out, precision);
 			out.close();
 		}
 
@@ -2230,7 +2231,8 @@ public class MDPModelChecker extends ProbModelChecker
 			}
 			// Export
 			PrismLog out = new PrismFileLog(exportAdvFilename);
-			new DTMCFromMDPMemorylessAdversary(mdp, strat).exportToPrismExplicitTra(out);
+			int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
+			new DTMCFromMDPMemorylessAdversary(mdp, strat).exportToPrismExplicitTra(out, precision);
 			out.close();
 		}
 
diff --git a/prism/src/explicit/Model.java b/prism/src/explicit/Model.java
index 944b6adff7f7aa48c867c4ac17592486e5104914..68648c97c7a3c291e2c7c49d598622b81376e7a1 100644
--- a/prism/src/explicit/Model.java
+++ b/prism/src/explicit/Model.java
@@ -44,10 +44,9 @@ import explicit.graphviz.Decorator;
 import parser.State;
 import parser.Values;
 import parser.VarList;
-import prism.ModelType;
-import prism.PrismException;
-import prism.PrismFileLog;
-import prism.PrismLog;
+import prism.*;
+
+import static prism.PrismSettings.DEFAULT_EXPORT_MODEL_PRECISION;
 
 /**
  * Interface for (abstract) classes that provide (read-only) access to an explicit-state model.
@@ -321,24 +320,42 @@ public interface Model {
 	public void checkForDeadlocks(BitSet except) throws PrismException;
 
 	// Export methods (explicit files)
-	
+
 	/**
 	 * Export to explicit format readable by PRISM (i.e. a .tra file, etc.).
 	 */
 	default void exportToPrismExplicit(String baseFilename) throws PrismException
+	{
+		exportToPrismExplicit(baseFilename, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to explicit format readable by PRISM (i.e. a .tra file, etc.).
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToPrismExplicit(String baseFilename, int precision) throws PrismException
 	{
 		// Default implementation - just output .tra file
 		// (some models might override this)
-		exportToPrismExplicitTra(baseFilename + ".tra");
+		exportToPrismExplicitTra(baseFilename + ".tra", precision);
 	}
 
 	/**
 	 * Export transition matrix to explicit format readable by PRISM (i.e. a .tra file).
 	 */
 	default void exportToPrismExplicitTra(String filename) throws PrismException
+	{
+		exportToPrismExplicitTra(filename, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export transition matrix to explicit format readable by PRISM (i.e. a .tra file).
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToPrismExplicitTra(String filename, int precision) throws PrismException
 	{
 		try (PrismFileLog log = PrismFileLog.create(filename)) {
-			exportToPrismExplicitTra(log);
+			exportToPrismExplicitTra(log, precision);
 		}
 	}
 
@@ -347,24 +364,53 @@ public interface Model {
 	 */
 	default void exportToPrismExplicitTra(File file) throws PrismException
 	{
-		exportToPrismExplicitTra(file.getPath());
+		exportToPrismExplicitTra(file, DEFAULT_EXPORT_MODEL_PRECISION);
 	}
-	
+
 	/**
 	 * Export transition matrix to explicit format readable by PRISM (i.e. a .tra file).
+	 * @param precision number of significant digits >= 1
 	 */
-	public void exportToPrismExplicitTra(PrismLog log);
+	default void exportToPrismExplicitTra(File file, int precision) throws PrismException
+	{
+		exportToPrismExplicitTra(file.getPath(), precision);
+	}
+
+
+	/**
+	 * Export transition matrix to explicit format readable by PRISM (i.e. a .tra file).
+	 */
+	default void exportToPrismExplicitTra(PrismLog log)
+	{
+		exportToPrismExplicitTra(log, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export transition matrix to explicit format readable by PRISM (i.e. a .tra file).
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportToPrismExplicitTra(PrismLog log, int precision);
 	
 	// Export methods (dot files)
-	
+
 	/**
 	 * Export to a dot file.
 	 * @param filename Name of file to export to
 	 */
 	default void exportToDotFile(String filename) throws PrismException
+	{
+		exportToDotFile(filename, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file.
+	 * @param filename Name of file to export to
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(String filename, int precision) throws PrismException
 	{
 		try (PrismFileLog log = PrismFileLog.create(filename)) {
-			exportToDotFile(log);
+			exportToDotFile(log, precision);
 		}
 	}
 
@@ -374,9 +420,20 @@ public interface Model {
 	 * @param mark States to highlight (ignored if null)
 	 */
 	default void exportToDotFile(String filename, BitSet mark) throws PrismException
+	{
+		exportToDotFile(filename, mark, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, highlighting states in 'mark'.
+	 * @param filename Name of file to export to
+	 * @param mark States to highlight (ignored if null)
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(String filename, BitSet mark, int precision) throws PrismException
 	{
 		try (PrismFileLog log = PrismFileLog.create(filename)) {
-			exportToDotFile(log, mark);
+			exportToDotFile(log, mark, precision);
 		}
 	}
 
@@ -385,9 +442,19 @@ public interface Model {
 	 * @param filename Name of the file to export to
 	 */
 	default void exportToDotFile(String filename, Iterable<explicit.graphviz.Decorator> decorators) throws PrismException
+	{
+		exportToDotFile(filename, decorators, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, decorating states and transitions with the provided decorators
+	 * @param filename Name of the file to export to
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(String filename, Iterable<explicit.graphviz.Decorator> decorators, int precision) throws PrismException
 	{
 		try (PrismFileLog log = PrismFileLog.create(filename)) {
-			exportToDotFile(log, decorators);
+			exportToDotFile(log, decorators, precision);
 		}
 	}
 
@@ -397,7 +464,17 @@ public interface Model {
 	 */
 	default void exportToDotFile(PrismLog out)
 	{
-		exportToDotFile(out, (Iterable<explicit.graphviz.Decorator>)null);
+		exportToDotFile(out, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file.
+	 * @param out PrismLog to export to
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(PrismLog out, int precision)
+	{
+		exportToDotFile(out, (Iterable<explicit.graphviz.Decorator>)null, precision);
 	}
 
 	/**
@@ -406,11 +483,22 @@ public interface Model {
 	 * @param mark States to highlight (ignored if null)
 	 */
 	default void exportToDotFile(PrismLog out, BitSet mark)
+	{
+		exportToDotFile(out, mark, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, highlighting states in 'mark'.
+	 * @param out PrismLog to export to
+	 * @param mark States to highlight (ignored if null)
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(PrismLog out, BitSet mark, int precision)
 	{
 		if (mark == null) {
-			exportToDotFile(out);
+			exportToDotFile(out, precision);
 		}
-		exportToDotFile(out, Collections.singleton(new explicit.graphviz.MarkStateSetDecorator(mark)));
+		exportToDotFile(out, Collections.singleton(new explicit.graphviz.MarkStateSetDecorator(mark)), precision);
 	}
 
 	/**
@@ -420,6 +508,18 @@ public interface Model {
 	 * @param showStates Show state info on nodes?
 	 */
 	default void exportToDotFile(PrismLog out, BitSet mark, boolean showStates)
+	{
+		exportToDotFile(out, mark, showStates, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, highlighting states in 'mark'.
+	 * @param out PrismLog to export to
+	 * @param mark States to highlight (ignored if null)
+	 * @param showStates Show state info on nodes?
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(PrismLog out, BitSet mark, boolean showStates, int precision)
 	{
 		ArrayList<explicit.graphviz.Decorator> decorators = new ArrayList<explicit.graphviz.Decorator>();
 		if (showStates) {
@@ -432,7 +532,7 @@ public interface Model {
 		if (mark != null) {
 			decorators.add(new explicit.graphviz.MarkStateSetDecorator(mark));
 		}
-		exportToDotFile(out, decorators);
+		exportToDotFile(out, decorators, precision);
 	}
 
 	/**
@@ -440,6 +540,16 @@ public interface Model {
 	 * @param out PrismLog to export to
 	 */
 	default void exportToDotFile(PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	{
+		exportToDotFile(out, decorators, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, decorating states and transitions with the provided decorators
+	 * @param out PrismLog to export to
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportToDotFile(PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		explicit.graphviz.Decoration defaults = new explicit.graphviz.Decoration();
 		defaults.attributes().put("shape", "box");
@@ -462,7 +572,7 @@ public interface Model {
 			out.println(i + " " + decoration + ";");
 
 			// Transitions for state i
-			exportTransitionsToDotFile(i, out, decorators);
+			exportTransitionsToDotFile(i, out, decorators, precision);
 		}	
 		
 		// Footer
@@ -481,6 +591,23 @@ public interface Model {
 	 * @param decorators the decorators (may be {@code null})
 	 */
 	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	{
+		exportTransitionsToDotFile(i, out, decorators, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export the transitions from state {@code i} in Dot format to {@code out},
+	 * decorating using the given decorators.
+	 * <br>
+	 * The default implementation throws an UnsupportedOperationException,
+	 * so this method should be overloaded.
+	 *
+	 * @param i State index
+	 * @param out PrismLog for output
+	 * @param decorators the decorators (may be {@code null})
+	 * @param precision number of significant digits >= 1
+	 */
+	default void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		throw new UnsupportedOperationException();
 	}
@@ -488,7 +615,16 @@ public interface Model {
 	/**
 	 * Export to a equivalent PRISM language model description.
 	 */
-	public void exportToPrismLanguage(String filename) throws PrismException;
+	default void exportToPrismLanguage(String filename) throws PrismException
+	{
+		exportToPrismLanguage(filename, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a equivalent PRISM language model description.
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportToPrismLanguage(String filename, int precision) throws PrismException;
 	
 	/**
 	 * Export states list.
diff --git a/prism/src/explicit/NondetModel.java b/prism/src/explicit/NondetModel.java
index 26f75125e89c3668b1dbd94135fcbffdbb6f8b43..806ed21fdb52c359353524a33552091f805130f1 100644
--- a/prism/src/explicit/NondetModel.java
+++ b/prism/src/explicit/NondetModel.java
@@ -36,6 +36,8 @@ import java.util.function.IntPredicate;
 import prism.PrismLog;
 import strat.MDStrategy;
 
+import static prism.PrismSettings.DEFAULT_EXPORT_MODEL_PRECISION;
+
 /**
  * Interface for (abstract) classes that provide (read-only) access to an explicit-state model with nondeterminism.
  */
@@ -309,5 +311,14 @@ public interface NondetModel extends Model
 	/**
 	 * Export to a dot file, highlighting states in 'mark' and choices for a (memoryless) strategy.
 	 */
-	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[]);
+	default void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[])
+	{
+		exportToDotFileWithStrat(out, mark, strat, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export to a dot file, highlighting states in 'mark' and choices for a (memoryless) strategy.
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[], int precision);
 }
\ No newline at end of file
diff --git a/prism/src/explicit/POMDP.java b/prism/src/explicit/POMDP.java
index e1abe11c572d6be03393411b769a51d28492038a..f5b286889d4d268843d899a65d99ed24e3ff1dc1 100644
--- a/prism/src/explicit/POMDP.java
+++ b/prism/src/explicit/POMDP.java
@@ -56,7 +56,7 @@ public interface POMDP extends MDP, PartiallyObservableModel
 	}
 
 	@Override
-	default void exportToPrismExplicitTra(PrismLog out)
+	default void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Output transitions to .tra file
 		int numStates = getNumStates();
@@ -74,7 +74,7 @@ public interface POMDP extends MDP, PartiallyObservableModel
 				// Print out (sorted) transitions
 				for (Map.Entry<Integer, Double> e : sorted.entrySet()) {
 					// Note use of PrismUtils.formatDouble to match PRISM-exported files
-					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(e.getValue()) + " " + getObservation(e.getKey()));
+					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(precision, e.getValue()) + " " + getObservation(e.getKey()));
 					Object action = getAction(i, j);
 					out.print(action == null ? "\n" : (" " + action + "\n"));
 				}
diff --git a/prism/src/explicit/POMDPModelChecker.java b/prism/src/explicit/POMDPModelChecker.java
index 0ac042a70c3fca1a93ae3a4afbe8bf0bce68f5a2..6b68c63b4df2598b9926e6d03e0e3f6bd3d5a61a 100644
--- a/prism/src/explicit/POMDPModelChecker.java
+++ b/prism/src/explicit/POMDPModelChecker.java
@@ -48,6 +48,7 @@ import prism.Pair;
 import prism.PrismComponent;
 import prism.PrismException;
 import prism.PrismNotSupportedException;
+import prism.PrismSettings;
 import prism.PrismUtils;
 
 /**
@@ -246,6 +247,7 @@ public class POMDPModelChecker extends ProbModelChecker
 		// Export strategy if requested
 		// NB: proper storage of strategy for genStrat not yet supported,
 		// so just treat it as if -exportadv had been used, with default file (adv.tra)
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		if (genStrat || exportAdv) {
 			// Export in Dot format if filename extension is .dot
 			if (exportAdvFilename.endsWith(".dot")) {
@@ -257,11 +259,11 @@ public class POMDPModelChecker extends ProbModelChecker
 						d.labelAddBelow(psm.beliefs.get(state).toString(pomdp));
 						return d;
 					}
-				}));
+				}), precision);
 			}
 			// Otherwise use .tra format
 			else {
-				mdp.exportToPrismExplicitTra(exportAdvFilename);
+				mdp.exportToPrismExplicitTra(exportAdvFilename, precision);
 			}
 		}
 		// Create MDP model checker (disable strat generation - if enabled, we want the POMDP one) 
@@ -452,6 +454,7 @@ public class POMDPModelChecker extends ProbModelChecker
 		// Export strategy if requested
 		// NB: proper storage of strategy for genStrat not yet supported,
 		// so just treat it as if -exportadv had been used, with default file (adv.tra)
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		if (genStrat || exportAdv) {
 			// Export in Dot format if filename extension is .dot
 			if (exportAdvFilename.endsWith(".dot")) {
@@ -463,11 +466,11 @@ public class POMDPModelChecker extends ProbModelChecker
 						d.labelAddBelow(psm.beliefs.get(state).toString(pomdp));
 						return d;
 					}
-				}));
+				}), precision);
 			}
 			// Otherwise use .tra format
 			else {
-				mdp.exportToPrismExplicitTra(exportAdvFilename);
+				mdp.exportToPrismExplicitTra(exportAdvFilename, precision);
 			}
 		}
 
@@ -558,7 +561,7 @@ public class POMDPModelChecker extends ProbModelChecker
 		// Find observations for which not all states are in the set
 		// and remove them from the observation set to be returned
 		int numStates = pomdp.getNumStates();
-		for (int o = setObs.nextSetBit(0); o >= 0; o = set.nextSetBit(o + 1)) {
+		for (int o = setObs.nextSetBit(0); o >= 0; o = setObs.nextSetBit(o + 1)) {
 			for (int s = 0; s < numStates; s++) {
 				if (pomdp.getObservation(s) == o && !set.get(s)) {
 					setObs.set(o, false);
diff --git a/prism/src/explicit/ProbModelChecker.java b/prism/src/explicit/ProbModelChecker.java
index 73015974fa76e19b27f89bf56b96654c391d5c84..aa70838b619446130afeed0ddb7daadfbd4241ab 100644
--- a/prism/src/explicit/ProbModelChecker.java
+++ b/prism/src/explicit/ProbModelChecker.java
@@ -70,6 +70,8 @@ import prism.PrismNotSupportedException;
 import prism.PrismSettings;
 import prism.PrismUtils;
 
+import static prism.PrismSettings.DEFAULT_EXPORT_MODEL_PRECISION;
+
 /**
  * Super class for explicit-state probabilistic model checkers.
  */
@@ -1707,7 +1709,8 @@ public class ProbModelChecker extends NonProbModelChecker
 			return StateValues.create(TypeDouble.getInstance(), s -> model.isInitialState(s) ? pInit : 0.0, model);
 		}
 	}
-	
+
+
 	/**
 	 * Export (non-zero) state rewards for one reward structure of a model.
 	 * @param model The model
@@ -1716,6 +1719,19 @@ public class ProbModelChecker extends NonProbModelChecker
 	 * @param out Where to export
 	 */
 	public void exportStateRewardsToFile(Model model, int r, int exportType, PrismLog out) throws PrismException
+	{
+		exportStateRewardsToFile(model, r, exportType, out, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export (non-zero) state rewards for one reward structure of a model.
+	 * @param model The model
+	 * @param r Index of reward structure to export (0-indexed)
+	 * @param exportType The format in which to export
+	 * @param out Where to export
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportStateRewardsToFile(Model model, int r, int exportType, PrismLog out, int precision) throws PrismException
 	{
 		int numStates = model.getNumStates();
 		int nonZeroRews = 0;
@@ -1739,7 +1755,7 @@ public class ProbModelChecker extends NonProbModelChecker
 			for (int s = 0; s < numStates; s++) {
 				double d = mcRewards.getStateReward(s);
 				if (d != 0) {
-					out.println(s + " " + PrismUtils.formatDouble(d));
+					out.println(s + " " + PrismUtils.formatDouble(precision, d));
 				}
 			}
 			break;
@@ -1756,7 +1772,7 @@ public class ProbModelChecker extends NonProbModelChecker
 			for (int s = 0; s < numStates; s++) {
 				double d = mdpRewards.getStateReward(s);
 				if (d != 0) {
-					out.println(s + " " + PrismUtils.formatDouble(d));
+					out.println(s + " " + PrismUtils.formatDouble(precision, d));
 				}
 			}
 			break;
diff --git a/prism/src/explicit/STPG.java b/prism/src/explicit/STPG.java
index 1e034ac6097c24894fe9ff5a92658d535e66fa20..9ba0203c40cda840526ade93befcb88ec603f63b 100644
--- a/prism/src/explicit/STPG.java
+++ b/prism/src/explicit/STPG.java
@@ -68,7 +68,7 @@ public interface STPG extends NondetModel
 	}
 
 	@Override
-	default void exportToPrismExplicitTra(PrismLog out)
+	default void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Output transitions to .tra file
 		// Just use MDP format for now; no specific format for games 
@@ -87,7 +87,7 @@ public interface STPG extends NondetModel
 				// Print out (sorted) transitions
 				for (Map.Entry<Integer, Double> e : sorted.entrySet()) {
 					// Note use of PrismUtils.formatDouble to match PRISM-exported files
-					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(e.getValue()));
+					out.print(i + " " + j + " " + e.getKey() + " " + PrismUtils.formatDouble(precision, e.getValue()));
 					Object action = getAction(i, j);
 					out.print(action == null ? "\n" : (" " + action + "\n"));
 				}
@@ -97,7 +97,7 @@ public interface STPG extends NondetModel
 	}
 
 	@Override
-	default void exportToPrismLanguage(final String filename) throws PrismException
+	default void exportToPrismLanguage(final String filename, int precision) throws PrismException
 	{
 		throw new UnsupportedOperationException();
 	}
diff --git a/prism/src/explicit/STPGAbstrSimple.java b/prism/src/explicit/STPGAbstrSimple.java
index fd0dbee564f3b0a62b329152d55c660c8dd29ddf..9994a24ed74888e410f1171d78092b64004219ed 100644
--- a/prism/src/explicit/STPGAbstrSimple.java
+++ b/prism/src/explicit/STPGAbstrSimple.java
@@ -364,7 +364,7 @@ public class STPGAbstrSimple extends ModelExplicit implements STPG, NondetModelS
 	}
 
 	@Override
-	public void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	public void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		// Custom dot format for game abstractions
 		// We ignore decorators for the moment
@@ -381,14 +381,14 @@ public class STPGAbstrSimple extends ModelExplicit implements STPG, NondetModelS
 				out.print(nij + " -> " + nijk + " [ arrowhead=none,label=\"" + k + "\" ];\n");
 				out.print(nijk + " [ shape=point,label=\"\" ];\n");
 				for (Map.Entry<Integer, Double> e : distr) {
-					out.print(nijk + " -> " + e.getKey() + " [ label=\"" + e.getValue() + "\" ];\n");
+					out.print(nijk + " -> " + e.getKey() + " [ label=\"" + PrismUtils.formatDouble(precision, e.getValue()) + "\" ];\n");
 				}
 			}
 		}
 	}
 
 	@Override
-	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[])
+	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[], int precision)
 	{
 		throw new RuntimeException("Not yet supported");
 	}
diff --git a/prism/src/explicit/STPGExplicit.java b/prism/src/explicit/STPGExplicit.java
index 8012bcd511a2d0ca7284f86ca38e783970a49bd4..e327a38e5f779bf98d6f9ae304bb17c912abb947 100644
--- a/prism/src/explicit/STPGExplicit.java
+++ b/prism/src/explicit/STPGExplicit.java
@@ -161,17 +161,17 @@ public class STPGExplicit extends MDPSimple implements STPG
 	}
 
 	@Override
-	public void exportToPrismExplicitTra(PrismLog out)
+	public void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		// Resolve conflict: STPG interface does not (currently) extend MDP  
-		STPG.super.exportToPrismExplicitTra(out);
+		STPG.super.exportToPrismExplicitTra(out, precision);
 	}
 
 	@Override
-	public void exportToPrismLanguage(final String filename) throws PrismException
+	public void exportToPrismLanguage(final String filename, int precision) throws PrismException
 	{
 		// Resolve conflict: STPG interface does not (currently) extend MDP  
-		STPG.super.exportToPrismLanguage(filename);
+		STPG.super.exportToPrismLanguage(filename, precision);
 	}
 
 	@Override
diff --git a/prism/src/explicit/StateModelChecker.java b/prism/src/explicit/StateModelChecker.java
index 74bcdb04f202b8059e5fd9ff92bbd2aa45695584..83050c9a2a2d4faf0e7c45ab9441d48f0b24a364 100644
--- a/prism/src/explicit/StateModelChecker.java
+++ b/prism/src/explicit/StateModelChecker.java
@@ -1677,7 +1677,8 @@ public class StateModelChecker extends PrismComponent
 	{
 		if (getExportProductTrans()) {
 			mainLog.println("\nExporting product transition matrix to file \"" + getExportProductTransFilename() + "\"...");
-			product.getProductModel().exportToPrismExplicitTra(getExportProductTransFilename());
+			int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
+			product.getProductModel().exportToPrismExplicitTra(getExportProductTransFilename(), precision);
 		}
 		if (getExportProductStates()) {
 			mainLog.println("\nExporting product state space to file \"" + getExportProductStatesFilename() + "\"...");
diff --git a/prism/src/explicit/SubNondetModel.java b/prism/src/explicit/SubNondetModel.java
index 5753ff1840209a9aa01aff18436732ada661617e..20a767f30946054a8402a868244d7de7df0892a9 100644
--- a/prism/src/explicit/SubNondetModel.java
+++ b/prism/src/explicit/SubNondetModel.java
@@ -226,19 +226,19 @@ public class SubNondetModel implements NondetModel
 	}
 
 	@Override
-	public void exportToPrismExplicitTra(PrismLog log)
+	public void exportToPrismExplicitTra(PrismLog log, int precision)
 	{
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[])
+	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int strat[], int precision)
 	{
 		throw new UnsupportedOperationException();
 	}
 	
 	@Override
-	public void exportToPrismLanguage(String filename) throws PrismException
+	public void exportToPrismLanguage(String filename, int precision) throws PrismException
 	{
 		throw new UnsupportedOperationException();
 	}
diff --git a/prism/src/jdd/JDDVars.java b/prism/src/jdd/JDDVars.java
index bf040fcf9139da45dee72583f91154f4a1566927..36b961dab86e1d33dd681f849e99653711a0cfd4 100644
--- a/prism/src/jdd/JDDVars.java
+++ b/prism/src/jdd/JDDVars.java
@@ -397,7 +397,7 @@ public class JDDVars implements Iterable<JDDNode>
 			@Override
 			public int compare(JDDNode a, JDDNode b)
 			{
-				return new Integer(a.getIndex()).compareTo(b.getIndex());
+				return Integer.valueOf(a.getIndex()).compareTo(b.getIndex());
 			}
 		});
 	}
diff --git a/prism/src/jltl2ba/Buchi.java b/prism/src/jltl2ba/Buchi.java
index 15fca490c943e3eb504bd676b76a2ca3a688992e..0e872394374159ff72db1a9fa7bea00c5fa91e7a 100644
--- a/prism/src/jltl2ba/Buchi.java
+++ b/prism/src/jltl2ba/Buchi.java
@@ -731,7 +731,7 @@ public class Buchi {
 		for (s = bstates.prv; s != bstates; s = s.prv) {
 			stateindex = nba.nba_i_newState();
 			// System.out.println("Seen ltl2ba state " + s.id + ", mapped to " + stateindex);
-			map.put(new LTL2BAState(s.id, s._final), new Integer(stateindex));
+			map.put(new LTL2BAState(s.id, s._final), Integer.valueOf(stateindex));
 			
 			if (s.id == -1)
 				nba.nba_i_setStartState(stateindex);
@@ -756,13 +756,13 @@ public class Buchi {
 				BTrans t1;
 				APMonom transMonom = new APMonom();
 				transMonom.setFromPosNeg(t.pos, t.neg);
-				// System.out.println("Seen ltl2ba transition " + s.id + " -|" + transMonom.toString() + "|-> " + t.to.id + ", mapped to " + map.get(new Integer(s.id)) + " -> " + map.get(new Integer(t.to.id)));
+				// System.out.println("Seen ltl2ba transition " + s.id + " -|" + transMonom.toString() + "|-> " + t.to.id + ", mapped to " + map.get(Integer.valueOf(s.id)) + " -> " + map.get(Integer.valueOf(t.to.id)));
 				nba.nba_i_addEdge(map.get(new LTL2BAState(s.id, s._final)), transMonom, map.get(new LTL2BAState(t.to.id, t.to._final)));
 				for (t1 = t; t1.nxt != s.trans;) {
 					if (t1.nxt.to.id == t.to.id && t1.nxt.to._final == t.to._final) {
 						transMonom = new APMonom();
 						transMonom.setFromPosNeg(t1.nxt.pos, t1.nxt.neg);
-						// System.out.println("Seen ltl2ba transition " + s.id + " -|" + transMonom.toString() + "|-> " + t.to.id + ", mapped to " + map.get(new Integer(s.id)) + " -> " + map.get(new Integer(t.to.id)));
+						// System.out.println("Seen ltl2ba transition " + s.id + " -|" + transMonom.toString() + "|-> " + t.to.id + ", mapped to " + map.get(Integer.valueOf(s.id)) + " -> " + map.get(Integer.valueOf(t.to.id)));
 						nba.nba_i_addEdge(map.get(new LTL2BAState(s.id, s._final)), transMonom, map.get(new LTL2BAState(t.to.id, t.to._final)));
 						t1.nxt = t1.nxt.nxt;
 					} 
diff --git a/prism/src/jltl2ba/MyBitSet.java b/prism/src/jltl2ba/MyBitSet.java
index 91551bb37b39d5a0a47aae288aa4f61c41658687..8801d6f3c7eca27c10f33a1efda4d42619ec5440 100644
--- a/prism/src/jltl2ba/MyBitSet.java
+++ b/prism/src/jltl2ba/MyBitSet.java
@@ -64,7 +64,7 @@ public class MyBitSet extends BitSet implements Comparable<BitSet>, Iterable<Int
 		Vector<Integer> tmp = new Vector<Integer>();
 		for (int i = 0; i < this.size(); i++)
 			if (this.get(i))
-				tmp.add(new Integer(i));
+				tmp.add(Integer.valueOf(i));
 		return tmp;
 	}
 
@@ -137,7 +137,7 @@ public class MyBitSet extends BitSet implements Comparable<BitSet>, Iterable<Int
 		}
 		
 		public Integer next() {
-			Integer rv = new Integer(index);
+			Integer rv = Integer.valueOf(index);
 			index = _bitset.nextSetBit(index + 1);
 			return rv;
 		}
diff --git a/prism/src/jltl2dstar/DA.java b/prism/src/jltl2dstar/DA.java
index 7d7a1aa1ef7c22d166a74d43df89f99fff27f22d..6cef2349dbdbdd7a196c0594c14af0c481c4e169 100644
--- a/prism/src/jltl2dstar/DA.java
+++ b/prism/src/jltl2dstar/DA.java
@@ -219,7 +219,7 @@ public class DA {
 			Vector<Integer> mapping = new Vector<Integer>(_index.size());
 			for (i = 0, j = 0; i < _index.size(); i++) {
 				if (_index.get(i) != null) {
-					mapping.set(i, new Integer(j));
+					mapping.set(i, Integer.valueOf(j));
 					if (j != i) {
 						_index.set(j, _index.get(i));
 						_index.set(i, null);
diff --git a/prism/src/jltl2dstar/DRAOptimizations.java b/prism/src/jltl2dstar/DRAOptimizations.java
index 4c26ba116b7b0f01039bf21ea5257a21d5774c23..c35bc214d7cf718bbda4238a7a2e1be1c9f5b8bf 100644
--- a/prism/src/jltl2dstar/DRAOptimizations.java
+++ b/prism/src/jltl2dstar/DRAOptimizations.java
@@ -142,8 +142,8 @@ public class DRAOptimizations {
 		public void setColor(int state, int color) {
 			assert(color < _nr_of_colors);
 
-			_coloring.set(state, new Integer(color));
-			_color2state.set(color, new Integer(state));
+			_coloring.set(state, Integer.valueOf(color));
+			_color2state.set(color, Integer.valueOf(state));
 
 			if (_detailed) {
 				_color2states.get(color).set(state);
@@ -326,7 +326,7 @@ public class DRAOptimizations {
 		states.setSize(dra.size());
 
 		for (int i = 0; i < dra.size(); i++) {
-			states.set(i, new Integer(i));
+			states.set(i, Integer.valueOf(i));
 		}
 
 		AcceptanceSignatureContainer accsig_container = new AcceptanceSignatureContainer(dra);
diff --git a/prism/src/jltl2dstar/RabinAcceptance.java b/prism/src/jltl2dstar/RabinAcceptance.java
index 2e326480e9c528cf0e399aa337257c6a8732ca98..42af650a4731e9528a76f9248620c45731c588c9 100644
--- a/prism/src/jltl2dstar/RabinAcceptance.java
+++ b/prism/src/jltl2dstar/RabinAcceptance.java
@@ -364,7 +364,7 @@ public class RabinAcceptance implements Iterable<Integer> {
 		
 		public Integer next() throws NoSuchElementException {
 			if (hasNext()) {
-				Integer rv = new Integer(index);
+				Integer rv = Integer.valueOf(index);
 				increment();
 				return rv;
 			}
diff --git a/prism/src/jltl2dstar/SCCs.java b/prism/src/jltl2dstar/SCCs.java
index c6346d500d8a0c01169687afca0053a345519ba8..c9f7756f702d8d6af0c575f34c271472cb0f511f 100644
--- a/prism/src/jltl2dstar/SCCs.java
+++ b/prism/src/jltl2dstar/SCCs.java
@@ -131,7 +131,7 @@ public class SCCs {
 		if (_state_to_scc.size() <= state) {
 			_state_to_scc.setSize(state + 1);
 		}
-		_state_to_scc.set(state, new Integer(scc));
+		_state_to_scc.set(state, Integer.valueOf(scc));
 	}
 
 	/** Set flag that the graph was discovered to be disjoint */
diff --git a/prism/src/mtbdd/PM_ExportMatrix.cc b/prism/src/mtbdd/PM_ExportMatrix.cc
index a39bc7858cd418eebed018652987056120d749b6..36bc58b1ffd7240de6852df7c9f0286bdf742cc6 100644
--- a/prism/src/mtbdd/PM_ExportMatrix.cc
+++ b/prism/src/mtbdd/PM_ExportMatrix.cc
@@ -103,9 +103,9 @@ static void export_rec(DdNode *dd, DdNode **rvars, DdNode **cvars, int num_vars,
 	// base case - non zero terminal
 	if (level == num_vars) {
 		switch (export_type) {
-		case EXPORT_PLAIN: export_string("%" PRId64 " %" PRId64 " %.12g\n", r, c, Cudd_V(dd)); break;
-		case EXPORT_MATLAB: export_string("%s(%" PRId64 ",%" PRId64 ")=%.12g;\n", export_name, r+1, c+1, Cudd_V(dd)); break;
-		case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("%" PRId64 " -> %" PRId64 " [ label=\"%.12g\" ];\n", r, c, Cudd_V(dd)); break;
+		case EXPORT_PLAIN: export_string("%" PRId64 " %" PRId64 " %.*g\n", r, c, export_model_precision, Cudd_V(dd)); break;
+		case EXPORT_MATLAB: export_string("%s(%" PRId64 ",%" PRId64 ")=%.*g;\n", export_name, r+1, c+1, export_model_precision, Cudd_V(dd)); break;
+		case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("%" PRId64 " -> %" PRId64 " [ label=\"%.*g\" ];\n", r, c, export_model_precision, Cudd_V(dd)); break;
 		}
 		return;
 	}
diff --git a/prism/src/mtbdd/PM_ExportVector.cc b/prism/src/mtbdd/PM_ExportVector.cc
index 3b1ed80058fd1690089735b5e1caade765d64684..a0110fc213711cdd1170be15712bb0e6be118c09 100644
--- a/prism/src/mtbdd/PM_ExportVector.cc
+++ b/prism/src/mtbdd/PM_ExportVector.cc
@@ -93,9 +93,9 @@ static void export_rec(DdNode *dd, DdNode **vars, int num_vars, int level, ODDNo
 	// base case - non zero terminal
 	if (level == num_vars) {
 		switch (export_type) {
-		case EXPORT_PLAIN: export_string("%" PRId64 " %.12g\n", index, Cudd_V(dd)); break;
-		case EXPORT_MATLAB: export_string("%s(%" PRId64 ")=%.12g;\n", export_name, index+1, Cudd_V(dd)); break;
-		case EXPORT_MRMC: export_string("%" PRId64 " %.12g\n", index+1, Cudd_V(dd)); break;
+		case EXPORT_PLAIN: export_string("%" PRId64 " %.*g\n", index, export_model_precision, Cudd_V(dd)); break;
+		case EXPORT_MATLAB: export_string("%s(%" PRId64 ")=%.*g;\n", export_name, index+1, export_model_precision, Cudd_V(dd)); break;
+		case EXPORT_MRMC: export_string("%" PRId64 " %.*g\n", index+1, export_model_precision, Cudd_V(dd)); break;
 		}
 		return;
 	}
diff --git a/prism/src/mtbdd/PrismMTBDD.java b/prism/src/mtbdd/PrismMTBDD.java
index f287aaf6054c2fcec96a0965569409e3dc848272..d22a4af7ee013d5dba9dd13ef83e482ef3c444ed 100644
--- a/prism/src/mtbdd/PrismMTBDD.java
+++ b/prism/src/mtbdd/PrismMTBDD.java
@@ -590,8 +590,9 @@ public class PrismMTBDD
 
 	// export vector
 	private static native int PM_ExportVector(long vector, String name, long vars, int nv, long odd, int exportType, String filename);
-	public static void ExportVector(JDDNode vector, String name, JDDVars vars, ODDNode odd, int exportType, String filename) throws FileNotFoundException
+	public static void ExportVector(JDDNode vector, String name, JDDVars vars, ODDNode odd, int exportType, String filename, int precision) throws FileNotFoundException
 	{
+		PrismNative.setExportModelPrecision(precision);
 		int res = PM_ExportVector(vector.ptr(), name, vars.array(), vars.n(), odd.ptr(), exportType, filename);
 		if (res == -1) {
 			throw new FileNotFoundException();
@@ -600,8 +601,9 @@ public class PrismMTBDD
 	
 	// export matrix
 	private static native int PM_ExportMatrix(long matrix, String name, long rv, int nrv, long cv, int ncv, long odd, int exportType, String filename);
-	public static void ExportMatrix(JDDNode matrix, String name, JDDVars rows, JDDVars cols, ODDNode odd, int exportType, String filename) throws FileNotFoundException
+	public static void ExportMatrix(JDDNode matrix, String name, JDDVars rows, JDDVars cols, ODDNode odd, int exportType, String filename, int precision) throws FileNotFoundException
 	{
+		PrismNative.setExportModelPrecision(precision);
 		int res = PM_ExportMatrix(matrix.ptr(), name, rows.array(), rows.n(), cols.array(), cols.n(), odd.ptr(), exportType, filename);
 		if (res == -1) {
 			throw new FileNotFoundException();
diff --git a/prism/src/param/BigRational.java b/prism/src/param/BigRational.java
index e04189207c243078d6b0f333e2aa06d8e6e99dbc..18bbb3c4f07582a01af5015eb0e515dd4b0065f0 100644
--- a/prism/src/param/BigRational.java
+++ b/prism/src/param/BigRational.java
@@ -132,21 +132,29 @@ public final class BigRational extends Number implements Comparable<BigRational>
 			}
 		}
 		if (cancel) {
-			if (num.equals(BigInteger.ZERO)) {
-				if (!den.equals(BigInteger.ZERO)) {
-					// not NaN (= 0/0), so this is a real zero:
-					// normalise by setting denominator to 1
-					num = BigInteger.ZERO;
-					den = BigInteger.ONE;
-				}
-			} else {
-				BigInteger gcd = num.gcd(den);
-				num = num.divide(gcd);
-				den = den.divide(gcd);
-				if (den.signum() == -1) {
-					num = num.negate();
-					den = den.negate();
-				}
+			canceled(num, den);
+		} else {
+			this.num = num;
+			this.den = den;
+		}
+	}
+
+	protected void canceled(BigInteger num, BigInteger den)
+	{
+		if (num.equals(BigInteger.ZERO)) {
+			if (!den.equals(BigInteger.ZERO)) {
+				// not NaN (= 0/0), so this is a real zero:
+				// normalise by setting denominator to 1
+				num = BigInteger.ZERO;
+				den = BigInteger.ONE;
+			}
+		} else {
+			BigInteger gcd = num.gcd(den);
+			num = num.divide(gcd);
+			den = den.divide(gcd);
+			if (den.signum() == -1) {
+				num = num.negate();
+				den = den.negate();
 			}
 		}
 		this.num = num;
@@ -175,6 +183,52 @@ public final class BigRational extends Number implements Comparable<BigRational>
 		this(num, 1);
 	}
 
+	/**
+	 * Creates a new BigRational by converting {@code value} to a fraction.
+	 * The algorithm uses iterated multiplication with 2 to determine the exponent of the argument.
+	 * 
+	 * @param num value of new rational as an integer value
+	 */
+	public  BigRational(double value)
+	{
+		if (java.lang.Double.isNaN(value)) {
+			this.num = BigInteger.ZERO;
+			this.den = BigInteger.ZERO;
+		}
+		if (value == java.lang.Double.POSITIVE_INFINITY) {
+			this.num = BigInteger.ONE;
+			this.den = BigInteger.ZERO;
+		}
+		if (value == java.lang.Double.NEGATIVE_INFINITY) {
+			this.num = BigInteger.ONE.negate();
+			this.den = BigInteger.ZERO;
+		}
+		// Test whether value must be an integer
+		if (value <= -0x1.0P52 || value >= 0x1.0P52) {
+			// Determine smallest exponent such that value = long_value * 2^exp
+			int exp = 0;
+			// Terminate as soon as value is a long
+			while ((long) value != value) {
+				value /= 2;
+				exp += 1;
+			}
+			// No need to cancel as denumerator is one
+			this.num = BigInteger.valueOf((long) value).shiftLeft(exp);
+			this.den = BigInteger.ONE;
+		} else {
+			// Determine smallest exponent such that value = long_value / 2^exp
+			int exp = 0;
+			// Terminate as soon as value is an integer
+			while ((long) value != value) {
+				value *= 2;
+				exp += 1;
+			}
+			// No need to cancel as exp is the smallest exponent
+			this.num = BigInteger.valueOf((long) value);
+			this.den = BigInteger.ONE.shiftLeft(exp);
+		}
+	}
+
 	/**
 	 * Creates a new BigRational from string {@code string}.
 	 * Formats supported are num / den where num and den are integers,
@@ -275,9 +329,7 @@ public final class BigRational extends Number implements Comparable<BigRational>
 			boolean v = (Boolean)value;
 			return v ? BigRational.ONE : BigRational.ZERO;
 		} else if (value instanceof Double) {
-			// TODO: ? might be imprecise, perhaps there
-			// is a way to get the full precision?
-			return new BigRational(((Double)value).toString());
+			return new BigRational((Double)value);
 		} else if (value instanceof String) {
 			return new BigRational((String)value);
 		}
@@ -307,20 +359,6 @@ public final class BigRational extends Number implements Comparable<BigRational>
 		return new BigRational(this.num, this.den, true);
 	}
 
-	/**
-	 * Creates a new BigRational with value {@code num} / {@code den}.
-	 * Makes sure that {@code num} and {@code den} are coprime.
-	 * To be used  
-	 * 
-	 * @param num numerator of new BigRational
-	 * @param den denominator of new BigRational
-	 * @return BigRational with value {@code num} / {@code den}
-	 */
-	private static BigRational cancel(BigInteger num, BigInteger den)
-	{
-		return new BigRational(num, den);
-	}
-
 	// operations
 
 	public BigRational add(BigRational other, boolean cancel)
diff --git a/prism/src/param/ParamModel.java b/prism/src/param/ParamModel.java
index 131e32ffb740c17e167e01ac65a5cb31a5cf6bf2..ca278715e534597853cf533f645bd0108aa6f4ce 100644
--- a/prism/src/param/ParamModel.java
+++ b/prism/src/param/ParamModel.java
@@ -260,7 +260,7 @@ public final class ParamModel extends ModelExplicit implements MDPGeneric<Functi
 	}
 
 	@Override
-	public void exportToPrismExplicitTra(PrismLog out)
+	public void exportToPrismExplicitTra(PrismLog out, int precision)
 	{
 		int i, j, numChoices;
 		Object action;
@@ -303,7 +303,7 @@ public final class ParamModel extends ModelExplicit implements MDPGeneric<Functi
 	}
 
 	@Override
-	public void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators)
+	public void exportTransitionsToDotFile(int i, PrismLog out, Iterable<explicit.graphviz.Decorator> decorators, int precision)
 	{
 		int numChoices = getNumChoices(i);
 		for (int j = 0; j < numChoices; j++) {
@@ -366,13 +366,13 @@ public final class ParamModel extends ModelExplicit implements MDPGeneric<Functi
 	}
 
 	@Override
-	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int[] strat)
+	public void exportToDotFileWithStrat(PrismLog out, BitSet mark, int[] strat, int precision)
 	{
 		throw new UnsupportedOperationException();
 	}
 
 	@Override
-	public void exportToPrismLanguage(String filename) throws PrismException
+	public void exportToPrismLanguage(String filename, int precision) throws PrismException
 	{
 		throw new UnsupportedOperationException();
 	}
diff --git a/prism/src/param/ParamModelChecker.java b/prism/src/param/ParamModelChecker.java
index fdb18e6b450473ddee6bdc43cdb56e0b25cfd52c..e4fff3226afd67da2e75974fb6773ba74aa96ca8 100644
--- a/prism/src/param/ParamModelChecker.java
+++ b/prism/src/param/ParamModelChecker.java
@@ -736,7 +736,7 @@ final public class ParamModelChecker extends PrismComponent
 			// Compute count
 			int count = vals.countOverBitSet(bsFilter);
 			// Store as object/vector
-			resObj = new Integer(count);
+			resObj = Integer.valueOf(count);
 			resVals = new RegionValues(expr.getType(), resObj, model); 
 			// Create explanation of result and print some details to log
 			resultExpl = filterTrue ? "Count of satisfying states" : "Count of satisfying states also in filter";
@@ -796,7 +796,7 @@ final public class ParamModelChecker extends PrismComponent
 				// Check "for all" over filter
 				b = vals.forallOverBitSet(bsFilter);
 				// Store as object/vector
-				resObj = new Boolean(b);
+				resObj = Boolean.valueOf(b);
 				resVals = new RegionValues(expr.getType(), resObj, model); 
 				// Create explanation of result and print some details to log
 				resultExpl = "Property " + (b ? "" : "not ") + "satisfied in ";
@@ -825,7 +825,7 @@ final public class ParamModelChecker extends PrismComponent
 			// Check "there exists" over filter
 			b = vals.existsOverBitSet(bsFilter);
 			// Store as object/vector
-			resObj = new Boolean(b);
+			resObj = Boolean.valueOf(b);
 			resVals = new RegionValues(expr.getType(), resObj, model); 
 			// Create explanation of result and print some details to log
 			resultExpl = "Property satisfied in ";
diff --git a/prism/src/param/StateBoolean.java b/prism/src/param/StateBoolean.java
index 743165ebc3864e42ddf0af2e8c67a558e160cefb..f8038e3ff80e9e6bfb774d7ccc9d395b76edad0d 100644
--- a/prism/src/param/StateBoolean.java
+++ b/prism/src/param/StateBoolean.java
@@ -38,7 +38,7 @@ final class StateBoolean extends StateValue {
 
 	public StateBoolean()
 	{
-		value = new Boolean(false);
+		value = Boolean.valueOf(false);
 	}
 	
 	public StateBoolean(boolean value)
diff --git a/prism/src/parser/ParseException.java b/prism/src/parser/ParseException.java
index 0f7f16284fcd75f294179d0c020b6badb3502275..839bcc293990cd0f81f353097b8beba052e6b0ef 100644
--- a/prism/src/parser/ParseException.java
+++ b/prism/src/parser/ParseException.java
@@ -1,5 +1,5 @@
-/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 6.0 */
-/* JavaCCOptions:KEEP_LINE_COL=null */
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 7.0 */
+/* JavaCCOptions:KEEP_LINE_COLUMN=true */
 package parser;
 
 /**
@@ -20,6 +20,11 @@ public class ParseException extends Exception {
    */
   private static final long serialVersionUID = 1L;
 
+  /**
+   * The end of line string for this machine.
+   */
+  protected static String EOL = System.getProperty("line.separator", "\n");
+
   /**
    * This constructor is used by the method "generateParseException"
    * in the generated parser.  Calling this constructor generates
@@ -60,7 +65,7 @@ public class ParseException extends Exception {
   /**
    * This is the last token that has been consumed successfully.  If
    * this object has been created due to a parse error, the token
-   * followng this token will (therefore) be the first error token.
+   * following this token will (therefore) be the first error token.
    */
   public Token currentToken;
 
@@ -88,8 +93,8 @@ public class ParseException extends Exception {
   private static String initialise(Token currentToken,
                            int[][] expectedTokenSequences,
                            String[] tokenImage) {
-    String eol = System.getProperty("line.separator", "\n");
-    StringBuffer expected = new StringBuffer();
+
+    StringBuilder expected = new StringBuilder();
     int maxSize = 0;
     for (int i = 0; i < expectedTokenSequences.length; i++) {
       if (maxSize < expectedTokenSequences[i].length) {
@@ -101,7 +106,7 @@ public class ParseException extends Exception {
       if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
         expected.append("...");
       }
-      expected.append(eol).append("    ");
+      expected.append(EOL).append("    ");
     }
     String retval = "Encountered \"";
     Token tok = currentToken.next;
@@ -117,21 +122,26 @@ public class ParseException extends Exception {
       retval += " \"";
       tok = tok.next;
     }
-    retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
-    retval += "." + eol;
-    if (expectedTokenSequences.length == 1) {
-      retval += "Was expecting:" + eol + "    ";
+    if (currentToken.next != null) {
+      retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+    }
+    retval += "." + EOL;
+    
+    
+    if (expectedTokenSequences.length == 0) {
+        // Nothing to add here
     } else {
-      retval += "Was expecting one of:" + eol + "    ";
+	    if (expectedTokenSequences.length == 1) {
+	      retval += "Was expecting:" + EOL + "    ";
+	    } else {
+	      retval += "Was expecting one of:" + EOL + "    ";
+	    }
+	    retval += expected.toString();
     }
-    retval += expected.toString();
+    
     return retval;
   }
 
-  /**
-   * The end of line string for this machine.
-   */
-  protected String eol = System.getProperty("line.separator", "\n");
 
   /**
    * Used to convert raw characters to their escaped version
@@ -139,13 +149,11 @@ public class ParseException extends Exception {
    * string literal.
    */
   static String add_escapes(String str) {
-      StringBuffer retval = new StringBuffer();
+      StringBuilder retval = new StringBuilder();
       char ch;
       for (int i = 0; i < str.length(); i++) {
         switch (str.charAt(i))
         {
-           case 0 :
-              continue;
            case '\b':
               retval.append("\\b");
               continue;
@@ -184,4 +192,4 @@ public class ParseException extends Exception {
    }
 
 }
-/* JavaCC - OriginalChecksum=83512206aea6c09dd76f640dc4cc775b (do not edit this line) */
+/* JavaCC - OriginalChecksum=8dbcceed9d408427f9932dcf43e59aaa (do not edit this line) */
diff --git a/prism/src/parser/PrismParser.java b/prism/src/parser/PrismParser.java
index 2e6a6425af58a5fd0bd3a407582f92ba8dddadb3..87fc20387b982906e4b3716736f7d70395ffaf01 100644
--- a/prism/src/parser/PrismParser.java
+++ b/prism/src/parser/PrismParser.java
@@ -27,7 +27,7 @@ public class PrismParser implements PrismParserConstants {
         static {
                 keywordList.clear();
                 for (int i = PrismParserConstants.COMMENT+1; i < PrismParserConstants.NOT; i++) {
-                        keywordList.add(PrismParserConstants.tokenImage[i].replaceAll("\u005c"", ""));
+                        keywordList.add(PrismParserConstants.tokenImage[i].replaceAll("\"", ""));
                 }
         }
 
@@ -54,26 +54,26 @@ public class PrismParser implements PrismParserConstants {
                         p = new PrismParser();
                         str = (args.length > 1) ? new FileInputStream(args[1]) : System.in;
                         src = (args.length > 1) ? "file "+args[1] : "stdin";
-                        System.out.println("Reading from "+src+"...\u005cn");
+                        System.out.println("Reading from "+src+"...\n");
 
                         if (args[0].equals("-modulesfile") || args[0].equals("-mf")) {
                                 ModulesFile mf = p.parseModulesFile(str);
-                                System.out.print("Modules file:\u005cn=============\u005cn\u005cn" + mf);
-                                System.out.print("\u005cnTree:\u005cn=====\u005cn" + mf.toTreeString());
+                                System.out.print("Modules file:\n=============\n\n" + mf);
+                                System.out.print("\nTree:\n=====\n" + mf.toTreeString());
                                 mf.tidyUp();
-                                System.out.print("\u005cnAnd after expansion:\u005cn====================\u005cn\u005cn" +mf);
+                                System.out.print("\nAnd after expansion:\n====================\n\n" +mf);
                         }
                         else if (args[0].equals("-propertiesfile") || args[0].equals("-pf")) {
                                 PropertiesFile pf = p.parsePropertiesFile(new ModulesFile(), str);
-                                System.out.print("Properties file:\u005cn================\u005cn\u005cn" + pf);
-                                System.out.print("\u005cnTree:\u005cn=====\u005cn" + pf.toTreeString());
+                                System.out.print("Properties file:\n================\n\n" + pf);
+                                System.out.print("\nTree:\n=====\n" + pf.toTreeString());
                                 pf.tidyUp();
-                                System.out.print("\u005cnAnd after expansion:\u005cn====================\u005cn\u005cn" + pf);
+                                System.out.print("\nAnd after expansion:\n====================\n\n" + pf);
                         }
                         else if (args[0].equals("-expression") || args[0].equals("-e")) {
                                 Expression expr = p.parseSingleExpression(str);
                                 System.out.println("Expression: " + expr.toString());
-                                System.out.print("Tree:\u005cn=====\u005cn" + expr.toTreeString());
+                                System.out.print("Tree:\n=====\n" + expr.toTreeString());
                                 expr.typeCheck();
                                 expr.semanticCheck();
                                 System.out.println("Type: " + expr.getType().getTypeString());
@@ -88,7 +88,7 @@ public class PrismParser implements PrismParserConstants {
                                         }
                                 });
                                 System.out.println("LTL formula: " + expr.toString());
-                                System.out.print("Tree:\u005cn=====\u005cn" + expr.toTreeString());
+                                System.out.print("Tree:\n=====\n" + expr.toTreeString());
                                 expr.typeCheck();
                                 //expr.semanticCheck();
                                 System.out.println("Type: " + expr.getType().getTypeString());
@@ -249,29 +249,29 @@ public class PrismParser implements PrismParserConstants {
                 Token t = firstToken;
 
                 // extract any comment from the previous lines of the file
-                if (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\u005c\u005cn\u005c\u005cr]*"))) {
+                if (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\\n\\r]*"))) {
                         // trace back thru special tokens that are comments
                         t = t.specialToken;
-                        while (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\u005c\u005cn\u005c\u005cr]*")))
+                        while (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\\n\\r]*")))
                                 t = t.specialToken;
                         // concatenate comment special tokens
                         while (t != null) {
                                 s = t.image;
                                 // strip any nasty carriage returns
-                                s = s.replaceAll("\u005cr", "");
+                                s = s.replaceAll("\r", "");
                                 // remove "//" and preceding/subsequent spaces/tabs from comments
                                 if (t.kind == PrismParserConstants.COMMENT) {
-                                        while (comment.length() > 0 && (""+comment.charAt(comment.length()-1)).matches("[ \u005ct]"))
+                                        while (comment.length() > 0 && (""+comment.charAt(comment.length()-1)).matches("[ \t]"))
                                                 comment = comment.substring(0,comment.length()-1);
                                         s = s.substring(2);
-                                        s = s.replaceFirst("[ \u005ct]*", "");
+                                        s = s.replaceFirst("[ \t]*", "");
                                 }
                                 comment += s;
                                 t = t.next;
                         }
                 }
                 // remove final new line (if present)
-                if (comment.length() > 0 && (comment.charAt(comment.length()-1) == '\u005cn'))
+                if (comment.length() > 0 && (comment.charAt(comment.length()-1) == '\n'))
                         comment = comment.substring(0,comment.length()-1);
 
                 return comment;
@@ -284,15 +284,15 @@ public class PrismParser implements PrismParserConstants {
                 int i;
                 String s, res = "";
                 // break into lines
-                while ((i = comment.indexOf("\u005cn")) != -1) {
+                while ((i = comment.indexOf("\n")) != -1) {
                         s = comment.substring(0, i);
                         comment = comment.substring(i+1);
                         // add "//" to non-empty lines
                         if (s.trim().length()>0) res += "// " + s;
-                        res += "\u005cn";
+                        res += "\n";
                 }
                 // deal with any trailing characters (with no new line ending them)
-                if (comment.trim().length()>0) res += "// " + comment + "\u005cn";
+                if (comment.trim().length()>0) res += "// " + comment + "\n";
                 return res;
         }
 
@@ -537,7 +537,7 @@ mf.addPlayer(player);
                 mf.setPosition(begin != null? begin: getToken(0), getToken(0));
                 {if ("" != null) return mf;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Properties file
   static final public 
@@ -662,7 +662,7 @@ pf.addProperty(prop);
     jj_consume_token(0);
 pf.setPosition(begin, getToken(0)); {if ("" != null) return pf;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Properties file with optional semicolons - beware of potential ambiguities
   static final public 
@@ -787,7 +787,7 @@ pf.addProperty(prop);
     jj_consume_token(0);
 pf.setPosition(begin, getToken(0)); {if ("" != null) return pf;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Property - expression, with optional name/comment
   static final public 
@@ -808,7 +808,7 @@ begin = getToken(1);
 prop = new Property(expr, name, getPrecedingCommentBlock(begin));
 prop.setPosition(begin, getToken(0)); {if ("" != null) return prop;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // A single expression
   static final public 
@@ -817,7 +817,7 @@ Expression SingleExpression() throws ParseException {Expression ret;
     jj_consume_token(0);
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // A single LTL formula
   static final public 
@@ -826,7 +826,7 @@ Expression SingleLTLFormula() throws ParseException {Expression ret;
     jj_consume_token(0);
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 //-----------------------------------------------------------------------------------
 // Modules file stuff (a few bits of which are reused for property files)
@@ -940,7 +940,7 @@ modelType=ModelType.CSG;
     }
 {if ("" != null) return modelType;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Formula definition
   static final public 
@@ -952,7 +952,7 @@ void FormulaDef(FormulaList formulaList) throws ParseException {ExpressionIdent
     expr = Expression(false, false);
     jj_consume_token(SEMICOLON);
 formulaList.addFormula(name, expr);
-  }
+}
 
 // Label definition
   static final public 
@@ -975,7 +975,7 @@ labelList.addLabel(name, expr);
       jj_consume_token(-1);
       throw new ParseException();
     }
-  }
+}
 
 // Constant definition
   static final public 
@@ -1056,7 +1056,7 @@ type=TypeDouble.getInstance();
     }
     jj_consume_token(SEMICOLON);
 constantList.addConstant(name, expr, type);
-  }
+}
 
 // Global variable declaration
   static final public 
@@ -1065,7 +1065,7 @@ Declaration GlobalDecl() throws ParseException {Declaration decl = null;
     decl = Declaration();
 {if ("" != null) return decl;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Variable declaration
   static final public 
@@ -1093,7 +1093,7 @@ decl.setStart(init);
     jj_consume_token(SEMICOLON);
 decl.setPosition(begin, getToken(0)); {if ("" != null) return decl;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Type part of a declaration
   static final public 
@@ -1133,7 +1133,7 @@ declType = new DeclarationClock();
     }
 declType.setPosition(begin, getToken(0)); {if ("" != null) return declType;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Module
   static final public 
@@ -1189,7 +1189,7 @@ module.addCommand(comm);
     jj_consume_token(ENDMODULE);
 module.setPosition(begin, getToken(0)); module.setNameASTElement(name); {if ("" != null) return module;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Command
   static final public 
@@ -1212,7 +1212,7 @@ comm.setUpdates(updates);
     jj_consume_token(SEMICOLON);
 comm.setPosition(begin, getToken(0)); {if ("" != null) return comm;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Updates
   static final public 
@@ -1293,7 +1293,7 @@ updates.addUpdate(prob, update);
     }
 updates.setPosition(begin, getToken(0)); {if ("" != null) return updates;}
     throw new Error("Missing return statement in function");
-  }
+}
 
   static final public Update Update() throws ParseException {Update update = new Update();
         Token begin = null;
@@ -1328,7 +1328,7 @@ begin = getToken(1);
     }
 update.setPosition(begin, getToken(0)); {if ("" != null) return update;}
     throw new Error("Missing return statement in function");
-  }
+}
 
   static final public void UpdateElement(Update update) throws ParseException {ExpressionIdent var = null;
         Expression expr = null;
@@ -1339,7 +1339,7 @@ update.setPosition(begin, getToken(0)); {if ("" != null) return update;}
     expr = Expression(false, false);
     jj_consume_token(RPARENTH);
 UpdateElement ue = new UpdateElement(var, expr); ue.setPosition(begin, getToken(0)); update.addElement(ue);
-  }
+}
 
 // Module renaming
   static final public 
@@ -1371,7 +1371,7 @@ rm = new RenamedModule(name.getName(), base.getName());
     jj_consume_token(ENDMODULE);
 rm.setPosition(begin, getToken(0)); rm.setNameASTElement(name); rm.setBaseModuleASTElement(base); {if ("" != null) return rm;}
     throw new Error("Missing return statement in function");
-  }
+}
 
   static final public void Rename(RenamedModule rm) throws ParseException {ExpressionIdent id1 = null, id2 = null;
     // NB: have to explicitly include keywords for functions because they can be renamed
@@ -1379,7 +1379,7 @@ rm.setPosition(begin, getToken(0)); rm.setNameASTElement(name); rm.setBaseModule
     jj_consume_token(EQ);
     id2 = IdentifierExpressionMinMax();
 rm.addRename(id1.getName(), id2.getName(), id1, id2);
-  }
+}
 
 // Reward structure
   static final public 
@@ -1466,7 +1466,7 @@ rsi = new RewardStructItem(synchs, guard, value); rsi.setPosition(begin2, getTok
     jj_consume_token(ENDREWARDS);
 rs.setPosition(begin, getToken(0)); {if ("" != null) return rs;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Action list (0 or more comma-separated identifiers)
   static final public 
@@ -1499,7 +1499,7 @@ list.add(s);
     }
 {if ("" != null) return list;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Initial states ("init...endinit" construct)
   static final public 
@@ -1509,7 +1509,7 @@ Expression Init() throws ParseException {Expression expr = null;
     jj_consume_token(ENDINIT);
 {if ("" != null) return expr;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Observable variables list (for partially observable models)
   static final public 
@@ -1539,7 +1539,7 @@ obsVars.addVar(exprVar);
     jj_consume_token(ENDOBSERVABLES);
 obsVars.setPosition(begin, getToken(0)); {if ("" != null) return obsVars;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Individual observable (for partially observable models)
   static final public 
@@ -1556,7 +1556,7 @@ begin = getToken(1);
     jj_consume_token(SEMICOLON);
 Observable obs = new Observable(name, defn); obs.setPosition(begin, getToken(0)); {if ("" != null) return obs;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition ("system...endsystem" construct)
   static final public 
@@ -1573,7 +1573,7 @@ void SystemEndsystem(ModulesFile mf) throws ParseException {String name = null;
     sysdef = SystemDefn();
     jj_consume_token(ENDSYSTEM);
 mf.addSystemDefn(sysdef, name);
-  }
+}
 
 // System definition component
   static final public 
@@ -1581,7 +1581,7 @@ SystemDefn SystemDefn() throws ParseException {SystemDefn ret;
     ret = SystemFullParallel();
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition component (full parallel)
   static final public 
@@ -1611,7 +1611,7 @@ if (par==null || par.getNumOperands() == 1) {
                         {if ("" != null) return par;}
                 }
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition component (interleaved parallel)
   static final public 
@@ -1642,7 +1642,7 @@ if (par==null || par.getNumOperands() == 1) {
                         {if ("" != null) return par;}
                 }
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition component (parallel over set of actions)
   static final public 
@@ -1688,7 +1688,7 @@ if (par==null) {
                         {if ("" != null) return par;}
                 }
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition component (hiding and renaming)
   static final public 
@@ -1773,7 +1773,7 @@ sys = rename;
     }
 sys.setPosition(begin, getToken(0)); {if ("" != null) return sys;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // System definition component (bottom level)
   static final public 
@@ -1808,7 +1808,7 @@ sys = new SystemBrackets(sys);
     }
 sys.setPosition(begin, getToken(0)); {if ("" != null) return sys;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Player definition
   static final public 
@@ -1846,7 +1846,7 @@ player = new Player(name);
     jj_consume_token(ENDPLAYER);
 player.setPosition(begin, getToken(0)); {if ("" != null) return player;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Player definition item (module or [action])
   static final public 
@@ -1869,7 +1869,7 @@ player.addModule(name);
       jj_consume_token(-1);
       throw new ParseException();
     }
-  }
+}
 
 //-----------------------------------------------------------------------------------
 // Expressions.
@@ -1885,7 +1885,7 @@ Expression Expression(boolean prop, boolean pathprop) throws ParseException {Exp
     ret = ExpressionTemporalBinary(prop, pathprop);
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: temporal operators, binary (U, W, R) and unary (X, F, G)
 
@@ -1962,7 +1962,7 @@ exprTemp.setOperand2(expr); exprTemp.setPosition(begin, getToken(0)); ret = expr
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
   static final public Expression ExpressionTemporalUnary(boolean prop, boolean pathprop) throws ParseException {Expression ret, expr;
         ExpressionTemporal exprTemp;
@@ -2055,7 +2055,7 @@ exprTemp.setOperand2(expr); exprTemp.setPosition(begin, getToken(0)); ret = expr
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Time bound for temporal operators
 // (see ExpressionTemporal production for lookahead explanation)
@@ -2298,7 +2298,7 @@ exprTemp.setEqualBounds(lBound);
       jj_consume_token(-1);
       throw new ParseException();
     }
-  }
+}
 
 // Expression: if-then-else, i.e. "cond ? then : else"
   static final public 
@@ -2321,7 +2321,7 @@ ret = new ExpressionITE(ret, left, right); ret.setPosition(begin, getToken(0));
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: implies
   static final public 
@@ -2346,7 +2346,7 @@ ret = new ExpressionBinaryOp(ExpressionBinaryOp.IMPLIES, ret, expr); ret.setPosi
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: if-and-only-iff
   static final public 
@@ -2371,7 +2371,7 @@ ret = new ExpressionBinaryOp(ExpressionBinaryOp.IFF, ret, expr); ret.setPosition
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: or
   static final public 
@@ -2396,7 +2396,7 @@ ret = new ExpressionBinaryOp(ExpressionBinaryOp.OR, ret, expr); ret.setPosition(
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: and
   static final public 
@@ -2421,7 +2421,7 @@ ret = new ExpressionBinaryOp(ExpressionBinaryOp.AND, ret, expr); ret.setPosition
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: not
   static final public 
@@ -2476,7 +2476,7 @@ ret = new ExpressionUnaryOp(ExpressionUnaryOp.NOT, expr); ret.setPosition(begin,
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: equality operators: =, !=
   static final public 
@@ -2503,7 +2503,7 @@ ret = new ExpressionBinaryOp(op, ret, expr); ret.setPosition(begin, getToken(0))
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: relational operators: >, <, >=, <=
   static final public 
@@ -2532,7 +2532,7 @@ ret = new ExpressionBinaryOp(op, ret, expr); ret.setPosition(begin, getToken(0))
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: plus/minus
 
@@ -2581,7 +2581,7 @@ ret = new ExpressionBinaryOp(op, ret, expr); ret.setPosition(begin, getToken(0))
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: times/divide
   static final public 
@@ -2623,7 +2623,7 @@ ret = new ExpressionBinaryOp(op, ret, expr); ret.setPosition(begin, getToken(0))
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: unary minus
   static final public 
@@ -2677,7 +2677,7 @@ ret = new ExpressionUnaryOp(ExpressionUnaryOp.MINUS, expr); ret.setPosition(begi
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Basic expression (top of operator precedence ordering)
   static final public 
@@ -2761,7 +2761,7 @@ Expression ExpressionBasic(boolean prop, boolean pathprop) throws ParseException
     }
 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: function or identifier
 
@@ -2806,7 +2806,7 @@ ret = new ExpressionFunc(s);
     }
 ret.setPosition(begin, getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: min/max function (treated differently because min/max are keywords)
   static final public 
@@ -2835,7 +2835,7 @@ func = new ExpressionFunc(s);
     jj_consume_token(RPARENTH);
 func.setPosition(begin, getToken(0)); {if ("" != null) return func;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: old-style function, i.e. "func(name, ...)"
   static final public 
@@ -2870,7 +2870,7 @@ func = new ExpressionFunc(s); func.setOldStyle(true);
     jj_consume_token(RPARENTH);
 func.setPosition(begin, getToken(0)); {if ("" != null) return func;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Arguments for a function in an expression
   static final public 
@@ -2892,7 +2892,7 @@ func.addOperand(expr);
       expr = Expression(prop, pathprop);
 func.addOperand(expr);
     }
-  }
+}
 
 // Expression: literal
   static final public 
@@ -2902,7 +2902,7 @@ Expression ExpressionLiteral(boolean prop, boolean pathprop) throws ParseExcepti
       jj_consume_token(REG_INT);
 try {
                         int i = Integer.parseInt(getToken(0).image);
-                        ret = new ExpressionLiteral(TypeInt.getInstance(), new Integer(i));
+                        ret = new ExpressionLiteral(TypeInt.getInstance(), Integer.valueOf(i));
                 } catch (NumberFormatException e) {
                         // Need to catch this because some matches for regexp REG_INT
                         // are not valid integers (e.g. too big).
@@ -2928,12 +2928,12 @@ try {
       }
     case TRUE:{
       jj_consume_token(TRUE);
-ret = new ExpressionLiteral(TypeBool.getInstance(), new Boolean(true));
+ret = new ExpressionLiteral(TypeBool.getInstance(), Boolean.valueOf(true));
       break;
       }
     case FALSE:{
       jj_consume_token(FALSE);
-ret = new ExpressionLiteral(TypeBool.getInstance(), new Boolean(false));
+ret = new ExpressionLiteral(TypeBool.getInstance(), Boolean.valueOf(false));
       break;
       }
     default:
@@ -2943,7 +2943,7 @@ ret = new ExpressionLiteral(TypeBool.getInstance(), new Boolean(false));
     }
 ret.setPosition(getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Expression: parentheses
   static final public 
@@ -2954,7 +2954,7 @@ Expression ExpressionParenth(boolean prop, boolean pathprop) throws ParseExcepti
     jj_consume_token(RPARENTH);
 ret = new ExpressionUnaryOp(ExpressionUnaryOp.PARENTH, expr); ret.setPosition(begin, getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 //-----------------------------------------------------------------------------------
 // Property stuff
@@ -3172,7 +3172,7 @@ ret.setModifier(modifier == null ? null : modifier.getName());
                 }
                 else {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Filter for a P/S/R operator
   static final public 
@@ -3215,7 +3215,7 @@ filter.setMaxRequested(true);
     }
 filter.setPosition(begin, getToken(0)); {if ("" != null) return filter;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: steady-state operator S
   static final public 
@@ -3292,7 +3292,7 @@ ret.setModifier(modifier == null ? null : modifier.getName());
                 }
                 else {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: expected reward operator R
   static final public 
@@ -3515,7 +3515,7 @@ ret.setModifier(modifier == null ? null : modifier.getName());
                 }
                 else {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Reward struct index for R operator
   static final public 
@@ -3662,7 +3662,7 @@ exprRew.setRewardStructIndex(index);
                 if (discount != null) {
                         exprRew.setDiscount(discount);
                 }
-  }
+}
 
 // Contents of an R operator
 
@@ -3768,7 +3768,7 @@ ret = expr;
     }
 ret.setPosition(begin, getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: n-player Nash operator
   static final public 
@@ -3886,7 +3886,7 @@ ret.setBound(bound);
                 ret.setRelOp(relOp);
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: probabilistic operator (n-player Nash)
   static final public 
@@ -3902,7 +3902,7 @@ ret.setExpression(expr);
                 ret.setPosition(begin, getToken(0));
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: reward operator (n-player Nash)
   static final public 
@@ -3919,7 +3919,7 @@ ret.setExpression(expr);
                 ret.setPosition(begin, getToken(0));
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Reward) structure index for equilibria
   static final public 
@@ -3978,7 +3978,7 @@ void MultiNashRewardIndexes(ExpressionMultiNashReward exprMultiNash) throws Pars
     }
     jj_consume_token(RBRACE);
 exprMultiNash.setRewardStructIndex(index);
-  }
+}
 
 // (Property) expression: CTL existential operator E
   static final public 
@@ -3994,7 +3994,7 @@ ret.setExpression(expr);
                 ret.setPosition(begin, getToken(0));
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: CTL universal operator A
   static final public 
@@ -4010,7 +4010,7 @@ ret.setExpression(expr);
                 ret.setPosition(begin, getToken(0));
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: ATL strategy operators <<>> and [[]]
   static final public 
@@ -4104,7 +4104,7 @@ ret.addOperand(expr);
 ret.setPosition(begin, getToken(0));
                 {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // List of coalitions (player lists) for a strategy (<<>> or [[]]) operator
   static final public 
@@ -4129,7 +4129,7 @@ coalitions.add(coalition);
     }
 {if ("" != null) return coalitions;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Coalition (player list) for a strategy (<<>> or [[]]) operator
   static final public 
@@ -4174,7 +4174,7 @@ coalition.setPlayers(players);
     }
 {if ("" != null) return coalition;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Single player in a coalition for a strategy (<<>> or [[]]) operator
   static final public 
@@ -4196,7 +4196,7 @@ String ExpressionStrategyCoalitionPlayer() throws ParseException {String s;
 s = getToken(0).image;
 {if ("" != null) return s;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: label (including "init")
   static final public 
@@ -4223,7 +4223,7 @@ s = "init";
     jj_consume_token(DQUOTE);
 ret = new ExpressionLabel(s); ret.setPosition(begin, getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // (Property) expression: filter (using "filter" keyword)
   static final public 
@@ -4286,7 +4286,7 @@ op = "|";
     jj_consume_token(RPARENTH);
 expr = new ExpressionFilter(op, expr2, filter); expr.setPosition(begin, getToken(0)); {if ("" != null) return expr;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 //-----------------------------------------------------------------------------------
 // Miscellaneous stuff
@@ -4298,7 +4298,7 @@ String Identifier() throws ParseException {
     jj_consume_token(REG_IDENT);
 {if ("" != null) return getToken(0).image;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Identifier (returns ExpressionIdent, storing position info)
   static final public 
@@ -4307,7 +4307,7 @@ ExpressionIdent IdentifierExpression() throws ParseException {String ident;
     ident = Identifier();
 ret = new ExpressionIdent(ident); ret.setPosition(getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Identifier or min/max keyword (returns ExpressionIdent, storing position info)
   static final public 
@@ -4335,7 +4335,7 @@ ident="max";
     }
 ret = new ExpressionIdent(ident); ret.setPosition(getToken(0)); {if ("" != null) return ret;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Primed identifier
   static final public 
@@ -4350,7 +4350,7 @@ ExpressionIdent IdentifierPrime() throws ParseException {
                 expr.setPrime(true);
                 {if ("" != null) return expr;}
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Equality operators: =, !=
   static final public 
@@ -4372,7 +4372,7 @@ int EqNeq() throws ParseException {
       throw new ParseException();
     }
     throw new Error("Missing return statement in function");
-  }
+}
 
 // Relational operators: >, <, >=, <=
   static final public 
@@ -4404,7 +4404,7 @@ int LtGt() throws ParseException {
       throw new ParseException();
     }
     throw new Error("Missing return statement in function");
-  }
+}
 
 // For loop
   static final public 
@@ -4436,12 +4436,12 @@ fl.setLHS(s);
                 fl.setPosition(begin, getToken(0));
                 {if ("" != null) return fl;}
     throw new Error("Missing return statement in function");
-  }
+}
 
   static private boolean jj_2_1(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_1(); }
+    try { return (!jj_3_1()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(0, xla); }
   }
@@ -4449,7 +4449,7 @@ fl.setLHS(s);
   static private boolean jj_2_2(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_2(); }
+    try { return (!jj_3_2()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(1, xla); }
   }
@@ -4457,7 +4457,7 @@ fl.setLHS(s);
   static private boolean jj_2_3(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_3(); }
+    try { return (!jj_3_3()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(2, xla); }
   }
@@ -4465,7 +4465,7 @@ fl.setLHS(s);
   static private boolean jj_2_4(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_4(); }
+    try { return (!jj_3_4()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(3, xla); }
   }
@@ -4473,7 +4473,7 @@ fl.setLHS(s);
   static private boolean jj_2_5(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_5(); }
+    try { return (!jj_3_5()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(4, xla); }
   }
@@ -4481,7 +4481,7 @@ fl.setLHS(s);
   static private boolean jj_2_6(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_6(); }
+    try { return (!jj_3_6()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(5, xla); }
   }
@@ -4489,7 +4489,7 @@ fl.setLHS(s);
   static private boolean jj_2_7(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_7(); }
+    try { return (!jj_3_7()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(6, xla); }
   }
@@ -4497,7 +4497,7 @@ fl.setLHS(s);
   static private boolean jj_2_8(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_8(); }
+    try { return (!jj_3_8()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(7, xla); }
   }
@@ -4505,7 +4505,7 @@ fl.setLHS(s);
   static private boolean jj_2_9(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_9(); }
+    try { return (!jj_3_9()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(8, xla); }
   }
@@ -4513,7 +4513,7 @@ fl.setLHS(s);
   static private boolean jj_2_10(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_10(); }
+    try { return (!jj_3_10()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(9, xla); }
   }
@@ -4521,7 +4521,7 @@ fl.setLHS(s);
   static private boolean jj_2_11(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_11(); }
+    try { return (!jj_3_11()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(10, xla); }
   }
@@ -4529,7 +4529,7 @@ fl.setLHS(s);
   static private boolean jj_2_12(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_12(); }
+    try { return (!jj_3_12()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(11, xla); }
   }
@@ -4537,7 +4537,7 @@ fl.setLHS(s);
   static private boolean jj_2_13(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_13(); }
+    try { return (!jj_3_13()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(12, xla); }
   }
@@ -4545,7 +4545,7 @@ fl.setLHS(s);
   static private boolean jj_2_14(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_14(); }
+    try { return (!jj_3_14()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(13, xla); }
   }
@@ -4553,7 +4553,7 @@ fl.setLHS(s);
   static private boolean jj_2_15(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_15(); }
+    try { return (!jj_3_15()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(14, xla); }
   }
@@ -4561,7 +4561,7 @@ fl.setLHS(s);
   static private boolean jj_2_16(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_16(); }
+    try { return (!jj_3_16()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(15, xla); }
   }
@@ -4569,7 +4569,7 @@ fl.setLHS(s);
   static private boolean jj_2_17(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_17(); }
+    try { return (!jj_3_17()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(16, xla); }
   }
@@ -4577,7 +4577,7 @@ fl.setLHS(s);
   static private boolean jj_2_18(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_18(); }
+    try { return (!jj_3_18()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(17, xla); }
   }
@@ -4585,67 +4585,67 @@ fl.setLHS(s);
   static private boolean jj_2_19(int xla)
  {
     jj_la = xla; jj_lastpos = jj_scanpos = token;
-    try { return !jj_3_19(); }
+    try { return (!jj_3_19()); }
     catch(LookaheadSuccess ls) { return true; }
     finally { jj_save(18, xla); }
   }
 
-  static private boolean jj_3R_249()
+  static private boolean jj_3R_ExpressionMultiNash_1905_9_249()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_257()) {
+    if (jj_3R_ExpressionMultiNash_1907_9_257()) {
     jj_scanpos = xsp;
-    if (jj_3R_258()) return true;
+    if (jj_3R_ExpressionMultiNash_1909_9_258()) return true;
     }
     if (jj_scan_token(LPARENTH)) return true;
     xsp = jj_scanpos;
-    if (jj_3R_259()) {
+    if (jj_3R_ExpressionMultiNash_1913_18_259()) {
     jj_scanpos = xsp;
-    if (jj_3R_260()) return true;
+    if (jj_3R_ExpressionMultiNash_1915_17_260()) return true;
     }
-    if (jj_3R_261()) return true;
+    if (jj_3R_ExpressionMultiNash_1917_18_261()) return true;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_261()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionMultiNash_1917_18_261()) { jj_scanpos = xsp; break; }
     }
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_145()
+  static private boolean jj_3R_ExpressionBasic_1514_17_145()
  {
-    if (jj_3R_157()) return true;
+    if (jj_3R_ExpressionLabel_2112_9_157()) return true;
     return false;
   }
 
-  static private boolean jj_3R_144()
+  static private boolean jj_3R_ExpressionBasic_1512_17_144()
  {
-    if (jj_3R_156()) return true;
+    if (jj_3R_ExpressionStrategy_2041_9_156()) return true;
     return false;
   }
 
-  static private boolean jj_3R_143()
+  static private boolean jj_3R_ExpressionBasic_1510_17_143()
  {
-    if (jj_3R_155()) return true;
+    if (jj_3R_ExpressionForAll_2021_9_155()) return true;
     return false;
   }
 
-  static private boolean jj_3R_48()
+  static private boolean jj_3R_SystemFullParallel_1072_71_48()
  {
     if (jj_scan_token(OR)) return true;
     if (jj_scan_token(OR)) return true;
-    if (jj_3R_54()) return true;
+    if (jj_3R_SystemParallel_1117_9_54()) return true;
     return false;
   }
 
-  static private boolean jj_3R_142()
+  static private boolean jj_3R_ExpressionBasic_1508_17_142()
  {
-    if (jj_3R_154()) return true;
+    if (jj_3R_ExpressionExists_2002_9_154()) return true;
     return false;
   }
 
-  static private boolean jj_3R_132()
+  static private boolean jj_3R_ExpressionTimesDivide_1460_64_132()
  {
     if (jj_scan_token(DIVIDE)) return true;
     return false;
@@ -4658,27 +4658,27 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_141()
+  static private boolean jj_3R_ExpressionBasic_1506_17_141()
  {
-    if (jj_3R_153()) return true;
+    if (jj_3R_ExpressionReward_1780_9_153()) return true;
     return false;
   }
 
-  static private boolean jj_3R_140()
+  static private boolean jj_3R_ExpressionBasic_1504_17_140()
  {
-    if (jj_3R_38()) return true;
+    if (jj_3R_ExpressionSS_1736_9_38()) return true;
     return false;
   }
 
-  static private boolean jj_3R_139()
+  static private boolean jj_3R_ExpressionBasic_1502_17_139()
  {
-    if (jj_3R_152()) return true;
+    if (jj_3R_ExpressionProb_1657_9_152()) return true;
     return false;
   }
 
-  static private boolean jj_3R_138()
+  static private boolean jj_3R_ExpressionBasic_1499_17_138()
  {
-    if (jj_3R_151()) return true;
+    if (jj_3R_ExpressionParenth_1633_9_151()) return true;
     return false;
   }
 
@@ -4689,104 +4689,104 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_137()
+  static private boolean jj_3R_ExpressionBasic_1497_17_137()
  {
-    if (jj_3R_150()) return true;
+    if (jj_3R_ExpressionFuncOldStyle_1571_9_150()) return true;
     return false;
   }
 
-  static private boolean jj_3R_229()
+  static private boolean jj_3R_ExpressionRewardContents_1885_11_229()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_64()
+  static private boolean jj_3R_SystemParallel_1119_11_64()
  {
     if (jj_scan_token(OR)) return true;
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_75()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemParallel_1120_65_75()) { jj_scanpos = xsp; break; }
     }
     if (jj_scan_token(RBRACKET)) return true;
     if (jj_scan_token(OR)) return true;
-    if (jj_3R_63()) return true;
+    if (jj_3R_SystemHideRename_1145_9_63()) return true;
     return false;
   }
 
-  static private boolean jj_3R_136()
+  static private boolean jj_3R_ExpressionBasic_1495_17_136()
  {
-    if (jj_3R_149()) return true;
+    if (jj_3R_ExpressionFuncMinMax_1557_9_149()) return true;
     return false;
   }
 
-  static private boolean jj_3R_228()
+  static private boolean jj_3R_ExpressionRewardContents_1882_11_228()
  {
     if (jj_scan_token(F0)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_135()
+  static private boolean jj_3R_ExpressionBasic_1493_17_135()
  {
-    if (jj_3R_148()) return true;
+    if (jj_3R_ExpressionFuncOrIdent_1539_9_148()) return true;
     return false;
   }
 
   static private boolean jj_3_17()
  {
-    if (jj_3R_38()) return true;
+    if (jj_3R_ExpressionSS_1736_9_38()) return true;
     return false;
   }
 
-  static private boolean jj_3R_227()
+  static private boolean jj_3R_ExpressionRewardContents_1881_11_227()
  {
     if (jj_scan_token(Fc)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_54()
+  static private boolean jj_3R_SystemParallel_1117_9_54()
  {
-    if (jj_3R_63()) return true;
+    if (jj_3R_SystemHideRename_1145_9_63()) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_64()) jj_scanpos = xsp;
+    if (jj_3R_SystemParallel_1119_11_64()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_226()
+  static private boolean jj_3R_ExpressionRewardContents_1880_11_226()
  {
     if (jj_scan_token(I)) return true;
     if (jj_scan_token(EQ)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_134()
+  static private boolean jj_3R_ExpressionBasic_1491_17_134()
  {
-    if (jj_3R_147()) return true;
+    if (jj_3R_ExpressionLiteral_1593_9_147()) return true;
     return false;
   }
 
-  static private boolean jj_3R_225()
+  static private boolean jj_3R_ExpressionRewardContents_1879_11_225()
  {
     if (jj_scan_token(C)) return true;
     return false;
   }
 
-  static private boolean jj_3R_224()
+  static private boolean jj_3R_ExpressionRewardContents_1878_11_224()
  {
     if (jj_scan_token(C)) return true;
     if (jj_scan_token(LE)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_128()
+  static private boolean jj_3R_ExpressionPlusMinus_1443_62_128()
  {
     if (jj_scan_token(MINUS)) return true;
     return false;
@@ -4798,47 +4798,47 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_223()
+  static private boolean jj_3R_ExpressionRewardContents_1875_11_223()
  {
     if (jj_scan_token(S)) return true;
     return false;
   }
 
-  static private boolean jj_3R_222()
+  static private boolean jj_3R_ExpressionRewardContents_1874_9_222()
  {
-    if (jj_3R_38()) return true;
+    if (jj_3R_ExpressionSS_1736_9_38()) return true;
     return false;
   }
 
-  static private boolean jj_3R_133()
+  static private boolean jj_3R_ExpressionBasic_1490_9_133()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_134()) {
+    if (jj_3R_ExpressionBasic_1491_17_134()) {
     jj_scanpos = xsp;
-    if (jj_3R_135()) {
+    if (jj_3R_ExpressionBasic_1493_17_135()) {
     jj_scanpos = xsp;
-    if (jj_3R_136()) {
+    if (jj_3R_ExpressionBasic_1495_17_136()) {
     jj_scanpos = xsp;
-    if (jj_3R_137()) {
+    if (jj_3R_ExpressionBasic_1497_17_137()) {
     jj_scanpos = xsp;
-    if (jj_3R_138()) {
+    if (jj_3R_ExpressionBasic_1499_17_138()) {
     jj_scanpos = xsp;
-    if (jj_3R_139()) {
+    if (jj_3R_ExpressionBasic_1502_17_139()) {
     jj_scanpos = xsp;
-    if (jj_3R_140()) {
+    if (jj_3R_ExpressionBasic_1504_17_140()) {
     jj_scanpos = xsp;
-    if (jj_3R_141()) {
+    if (jj_3R_ExpressionBasic_1506_17_141()) {
     jj_scanpos = xsp;
-    if (jj_3R_142()) {
+    if (jj_3R_ExpressionBasic_1508_17_142()) {
     jj_scanpos = xsp;
-    if (jj_3R_143()) {
+    if (jj_3R_ExpressionBasic_1510_17_143()) {
     jj_scanpos = xsp;
-    if (jj_3R_144()) {
+    if (jj_3R_ExpressionBasic_1512_17_144()) {
     jj_scanpos = xsp;
-    if (jj_3R_145()) {
+    if (jj_3R_ExpressionBasic_1514_17_145()) {
     jj_scanpos = xsp;
-    if (jj_3R_146()) return true;
+    if (jj_3R_ExpressionBasic_1516_17_146()) return true;
     }
     }
     }
@@ -4854,25 +4854,25 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_187()
+  static private boolean jj_3R_ExpressionRewardContents_1871_9_187()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_222()) {
+    if (jj_3R_ExpressionRewardContents_1874_9_222()) {
     jj_scanpos = xsp;
-    if (jj_3R_223()) {
+    if (jj_3R_ExpressionRewardContents_1875_11_223()) {
     jj_scanpos = xsp;
-    if (jj_3R_224()) {
+    if (jj_3R_ExpressionRewardContents_1878_11_224()) {
     jj_scanpos = xsp;
-    if (jj_3R_225()) {
+    if (jj_3R_ExpressionRewardContents_1879_11_225()) {
     jj_scanpos = xsp;
-    if (jj_3R_226()) {
+    if (jj_3R_ExpressionRewardContents_1880_11_226()) {
     jj_scanpos = xsp;
-    if (jj_3R_227()) {
+    if (jj_3R_ExpressionRewardContents_1881_11_227()) {
     jj_scanpos = xsp;
-    if (jj_3R_228()) {
+    if (jj_3R_ExpressionRewardContents_1882_11_228()) {
     jj_scanpos = xsp;
-    if (jj_3R_229()) return true;
+    if (jj_3R_ExpressionRewardContents_1885_11_229()) return true;
     }
     }
     }
@@ -4883,70 +4883,70 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_262()
+  static private boolean jj_3R_RewardIndex_1845_33_262()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_130()
+  static private boolean jj_3R_ExpressionUnaryMinus_1478_17_130()
  {
-    if (jj_3R_133()) return true;
+    if (jj_3R_ExpressionBasic_1490_9_133()) return true;
     return false;
   }
 
-  static private boolean jj_3R_188()
+  static private boolean jj_3R_ExpressionReward_1808_69_188()
  {
-    if (jj_3R_52()) return true;
+    if (jj_3R_Filter_1712_9_52()) return true;
     return false;
   }
 
-  static private boolean jj_3R_129()
+  static private boolean jj_3R_ExpressionUnaryMinus_1475_17_129()
  {
     if (jj_scan_token(MINUS)) return true;
-    if (jj_3R_125()) return true;
+    if (jj_3R_ExpressionUnaryMinus_1474_9_125()) return true;
     return false;
   }
 
-  static private boolean jj_3R_47()
+  static private boolean jj_3R_SystemInterleaved_1093_9_47()
  {
-    if (jj_3R_54()) return true;
+    if (jj_3R_SystemParallel_1117_9_54()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_55()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemInterleaved_1095_70_55()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_125()
+  static private boolean jj_3R_ExpressionUnaryMinus_1474_9_125()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_129()) {
+    if (jj_3R_ExpressionUnaryMinus_1475_17_129()) {
     jj_scanpos = xsp;
-    if (jj_3R_130()) return true;
+    if (jj_3R_ExpressionUnaryMinus_1478_17_130()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_131()
+  static private boolean jj_3R_ExpressionTimesDivide_1460_19_131()
  {
     if (jj_scan_token(TIMES)) return true;
     return false;
   }
 
-  static private boolean jj_3R_126()
+  static private boolean jj_3R_ExpressionTimesDivide_1460_17_126()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_131()) {
+    if (jj_3R_ExpressionTimesDivide_1460_19_131()) {
     jj_scanpos = xsp;
-    if (jj_3R_132()) return true;
+    if (jj_3R_ExpressionTimesDivide_1460_64_132()) return true;
     }
-    if (jj_3R_125()) return true;
+    if (jj_3R_ExpressionUnaryMinus_1474_9_125()) return true;
     return false;
   }
 
@@ -4956,44 +4956,44 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_254()
+  static private boolean jj_3R_RewardIndex_1845_11_254()
  {
     if (jj_scan_token(DIVIDE)) return true;
     if (jj_scan_token(LBRACE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_262()) {
+    if (jj_3R_RewardIndex_1845_33_262()) {
     jj_scanpos = xsp;
-    if (jj_3R_263()) return true;
+    if (jj_3R_RewardIndex_1845_101_263()) return true;
     }
     if (jj_scan_token(RBRACE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_253()
+  static private boolean jj_3R_RewardIndex_1842_10_253()
  {
     if (jj_scan_token(COMMA)) return true;
     if (jj_scan_token(DISCOUNT)) return true;
     if (jj_scan_token(EQ)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_250()
+  static private boolean jj_3R_RewardIndex_1840_10_250()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_121()
+  static private boolean jj_3R_ExpressionTimesDivide_1458_9_121()
  {
-    if (jj_3R_125()) return true;
+    if (jj_3R_ExpressionUnaryMinus_1474_9_125()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_126()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionTimesDivide_1460_17_126()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
@@ -5001,152 +5001,152 @@ fl.setLHS(s);
   static private boolean jj_3_2()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
     if (jj_scan_token(COLON)) return true;
     return false;
   }
 
-  static private boolean jj_3R_59()
+  static private boolean jj_3R_LtGt_2222_9_59()
  {
     if (jj_scan_token(LE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_240()
+  static private boolean jj_3R_RewardIndex_1838_9_240()
  {
     if (jj_scan_token(LBRACE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_250()) {
+    if (jj_3R_RewardIndex_1840_10_250()) {
     jj_scanpos = xsp;
-    if (jj_3R_251()) {
+    if (jj_3R_RewardIndex_1840_75_251()) {
     jj_scanpos = xsp;
-    if (jj_3R_252()) return true;
+    if (jj_3R_RewardIndex_1840_125_252()) return true;
     }
     }
     xsp = jj_scanpos;
-    if (jj_3R_253()) jj_scanpos = xsp;
+    if (jj_3R_RewardIndex_1842_10_253()) jj_scanpos = xsp;
     if (jj_scan_token(RBRACE)) return true;
     xsp = jj_scanpos;
-    if (jj_3R_254()) jj_scanpos = xsp;
+    if (jj_3R_RewardIndex_1845_11_254()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_58()
+  static private boolean jj_3R_LtGt_2221_9_58()
  {
     if (jj_scan_token(GE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_57()
+  static private boolean jj_3R_LtGt_2220_9_57()
  {
     if (jj_scan_token(LT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_40()
+  static private boolean jj_3R_SystemFullParallel_1070_9_40()
  {
-    if (jj_3R_47()) return true;
+    if (jj_3R_SystemInterleaved_1093_9_47()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_48()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemFullParallel_1072_71_48()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_127()
+  static private boolean jj_3R_ExpressionPlusMinus_1443_19_127()
  {
     if (jj_scan_token(PLUS)) return true;
     return false;
   }
 
-  static private boolean jj_3R_56()
+  static private boolean jj_3R_LtGt_2219_9_56()
  {
     if (jj_scan_token(GT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_50()
+  static private boolean jj_3R_LtGt_2219_9_50()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_56()) {
+    if (jj_3R_LtGt_2219_9_56()) {
     jj_scanpos = xsp;
-    if (jj_3R_57()) {
+    if (jj_3R_LtGt_2220_9_57()) {
     jj_scanpos = xsp;
-    if (jj_3R_58()) {
+    if (jj_3R_LtGt_2221_9_58()) {
     jj_scanpos = xsp;
-    if (jj_3R_59()) return true;
+    if (jj_3R_LtGt_2222_9_59()) return true;
     }
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_122()
+  static private boolean jj_3R_ExpressionPlusMinus_1443_17_122()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_127()) {
+    if (jj_3R_ExpressionPlusMinus_1443_19_127()) {
     jj_scanpos = xsp;
-    if (jj_3R_128()) return true;
+    if (jj_3R_ExpressionPlusMinus_1443_62_128()) return true;
     }
-    if (jj_3R_121()) return true;
+    if (jj_3R_ExpressionTimesDivide_1458_9_121()) return true;
     return false;
   }
 
-  static private boolean jj_3R_124()
+  static private boolean jj_3R_EqNeq_2210_9_124()
  {
     if (jj_scan_token(NE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_120()
+  static private boolean jj_3R_EqNeq_2209_9_120()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_123()) {
+    if (jj_3R_EqNeq_2209_9_123()) {
     jj_scanpos = xsp;
-    if (jj_3R_124()) return true;
+    if (jj_3R_EqNeq_2210_9_124()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_123()
+  static private boolean jj_3R_EqNeq_2209_9_123()
  {
     if (jj_scan_token(EQ)) return true;
     return false;
   }
 
-  static private boolean jj_3R_118()
+  static private boolean jj_3R_ExpressionPlusMinus_1441_9_118()
  {
-    if (jj_3R_121()) return true;
+    if (jj_3R_ExpressionTimesDivide_1458_9_121()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_122()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionPlusMinus_1443_17_122()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_36()
+  static private boolean jj_3R_SystemDefn_1057_9_36()
  {
-    if (jj_3R_40()) return true;
+    if (jj_3R_SystemFullParallel_1070_9_40()) return true;
     return false;
   }
 
   static private boolean jj_3_7()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_36()) return true;
+    if (jj_3R_SystemDefn_1057_9_36()) return true;
     return false;
   }
 
-  static private boolean jj_3R_246()
+  static private boolean jj_3R_ExpressionReward_1793_26_246()
  {
     if (jj_scan_token(MAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5154,7 +5154,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_245()
+  static private boolean jj_3R_ExpressionReward_1792_26_245()
  {
     if (jj_scan_token(MIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5162,14 +5162,14 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_244()
+  static private boolean jj_3R_ExpressionReward_1791_26_244()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_186()
+  static private boolean jj_3R_ExpressionReward_1806_10_186()
  {
     if (jj_scan_token(RMAXMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5177,13 +5177,13 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_53()
+  static private boolean jj_3R_IdentifierPrime_2190_9_53()
  {
     if (jj_scan_token(REG_IDENTPRIME)) return true;
     return false;
   }
 
-  static private boolean jj_3R_243()
+  static private boolean jj_3R_ExpressionReward_1789_26_243()
  {
     if (jj_scan_token(MAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5191,7 +5191,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_221()
+  static private boolean jj_3R_ExpressionReward_1798_17_221()
  {
     if (jj_scan_token(MAXMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5199,7 +5199,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_185()
+  static private boolean jj_3R_ExpressionReward_1805_10_185()
  {
     if (jj_scan_token(RMAXMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5207,14 +5207,14 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_119()
+  static private boolean jj_3R_ExpressionRelop_1421_11_119()
  {
-    if (jj_3R_50()) return true;
-    if (jj_3R_118()) return true;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_ExpressionPlusMinus_1441_9_118()) return true;
     return false;
   }
 
-  static private boolean jj_3R_242()
+  static private boolean jj_3R_ExpressionReward_1788_26_242()
  {
     if (jj_scan_token(MIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5222,7 +5222,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_220()
+  static private boolean jj_3R_ExpressionReward_1797_17_220()
  {
     if (jj_scan_token(MAXMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5230,7 +5230,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_184()
+  static private boolean jj_3R_ExpressionReward_1804_10_184()
  {
     if (jj_scan_token(RMINMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5238,14 +5238,14 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_241()
+  static private boolean jj_3R_ExpressionReward_1787_26_241()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_219()
+  static private boolean jj_3R_ExpressionReward_1796_17_219()
  {
     if (jj_scan_token(MINMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5253,7 +5253,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_183()
+  static private boolean jj_3R_ExpressionReward_1803_10_183()
  {
     if (jj_scan_token(RMINMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5261,7 +5261,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_218()
+  static private boolean jj_3R_ExpressionReward_1795_17_218()
  {
     if (jj_scan_token(MINMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5269,7 +5269,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_182()
+  static private boolean jj_3R_ExpressionReward_1802_10_182()
  {
     if (jj_scan_token(RMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5277,18 +5277,18 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_116()
+  static private boolean jj_3R_ExpressionRelop_1420_9_116()
  {
-    if (jj_3R_118()) return true;
+    if (jj_3R_ExpressionPlusMinus_1441_9_118()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_119()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionRelop_1421_11_119()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_181()
+  static private boolean jj_3R_ExpressionReward_1801_10_181()
  {
     if (jj_scan_token(RMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -5296,124 +5296,124 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_199()
+  static private boolean jj_3R_ExpressionFilter_2136_56_199()
  {
     if (jj_scan_token(OR)) return true;
     return false;
   }
 
-  static private boolean jj_3R_217()
+  static private boolean jj_3R_ExpressionReward_1791_17_217()
  {
     if (jj_scan_token(MAX)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_244()) {
+    if (jj_3R_ExpressionReward_1791_26_244()) {
     jj_scanpos = xsp;
-    if (jj_3R_245()) {
+    if (jj_3R_ExpressionReward_1792_26_245()) {
     jj_scanpos = xsp;
-    if (jj_3R_246()) return true;
+    if (jj_3R_ExpressionReward_1793_26_246()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_216()
+  static private boolean jj_3R_ExpressionReward_1787_17_216()
  {
     if (jj_scan_token(MIN)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_241()) {
+    if (jj_3R_ExpressionReward_1787_26_241()) {
     jj_scanpos = xsp;
-    if (jj_3R_242()) {
+    if (jj_3R_ExpressionReward_1788_26_242()) {
     jj_scanpos = xsp;
-    if (jj_3R_243()) return true;
+    if (jj_3R_ExpressionReward_1789_26_243()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_215()
+  static private boolean jj_3R_ExpressionReward_1786_17_215()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_214()
+  static private boolean jj_3R_ExpressionReward_1785_18_214()
  {
-    if (jj_3R_50()) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_213()
+  static private boolean jj_3R_ExpressionReward_1784_18_213()
  {
-    if (jj_3R_240()) return true;
+    if (jj_3R_RewardIndex_1838_9_240()) return true;
     return false;
   }
 
-  static private boolean jj_3R_212()
+  static private boolean jj_3R_ExpressionReward_1783_19_212()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_117()
+  static private boolean jj_3R_ExpressionEquality_1407_11_117()
  {
-    if (jj_3R_120()) return true;
-    if (jj_3R_116()) return true;
+    if (jj_3R_EqNeq_2209_9_120()) return true;
+    if (jj_3R_ExpressionRelop_1420_9_116()) return true;
     return false;
   }
 
-  static private boolean jj_3R_44()
+  static private boolean jj_3R_ExpressionSS_1744_55_44()
  {
-    if (jj_3R_52()) return true;
+    if (jj_3R_Filter_1712_9_52()) return true;
     return false;
   }
 
-  static private boolean jj_3R_115()
+  static private boolean jj_3R_ExpressionEquality_1406_9_115()
  {
-    if (jj_3R_116()) return true;
+    if (jj_3R_ExpressionRelop_1420_9_116()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_117()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionEquality_1407_11_117()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_37()
+  static private boolean jj_3R_IdentifierExpression_2168_9_37()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_180()
+  static private boolean jj_3R_ExpressionReward_1782_10_180()
  {
     if (jj_scan_token(R)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_212()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionReward_1783_19_212()) jj_scanpos = xsp;
     xsp = jj_scanpos;
-    if (jj_3R_213()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionReward_1784_18_213()) jj_scanpos = xsp;
     xsp = jj_scanpos;
-    if (jj_3R_214()) {
+    if (jj_3R_ExpressionReward_1785_18_214()) {
     jj_scanpos = xsp;
-    if (jj_3R_215()) {
+    if (jj_3R_ExpressionReward_1786_17_215()) {
     jj_scanpos = xsp;
-    if (jj_3R_216()) {
+    if (jj_3R_ExpressionReward_1787_17_216()) {
     jj_scanpos = xsp;
-    if (jj_3R_217()) {
+    if (jj_3R_ExpressionReward_1791_17_217()) {
     jj_scanpos = xsp;
-    if (jj_3R_218()) {
+    if (jj_3R_ExpressionReward_1795_17_218()) {
     jj_scanpos = xsp;
-    if (jj_3R_219()) {
+    if (jj_3R_ExpressionReward_1796_17_219()) {
     jj_scanpos = xsp;
-    if (jj_3R_220()) {
+    if (jj_3R_ExpressionReward_1797_17_220()) {
     jj_scanpos = xsp;
-    if (jj_3R_221()) return true;
+    if (jj_3R_ExpressionReward_1798_17_221()) return true;
     }
     }
     }
@@ -5424,42 +5424,42 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_114()
+  static private boolean jj_3R_ExpressionNot_1392_17_114()
  {
-    if (jj_3R_115()) return true;
+    if (jj_3R_ExpressionEquality_1406_9_115()) return true;
     return false;
   }
 
-  static private boolean jj_3R_113()
+  static private boolean jj_3R_ExpressionNot_1390_17_113()
  {
     if (jj_scan_token(NOT)) return true;
-    if (jj_3R_111()) return true;
+    if (jj_3R_ExpressionNot_1389_9_111()) return true;
     return false;
   }
 
-  static private boolean jj_3R_106()
+  static private boolean jj_3R_TimeBound_1307_99_106()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_153()
+  static private boolean jj_3R_ExpressionReward_1780_9_153()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_180()) {
+    if (jj_3R_ExpressionReward_1782_10_180()) {
     jj_scanpos = xsp;
-    if (jj_3R_181()) {
+    if (jj_3R_ExpressionReward_1801_10_181()) {
     jj_scanpos = xsp;
-    if (jj_3R_182()) {
+    if (jj_3R_ExpressionReward_1802_10_182()) {
     jj_scanpos = xsp;
-    if (jj_3R_183()) {
+    if (jj_3R_ExpressionReward_1803_10_183()) {
     jj_scanpos = xsp;
-    if (jj_3R_184()) {
+    if (jj_3R_ExpressionReward_1804_10_184()) {
     jj_scanpos = xsp;
-    if (jj_3R_185()) {
+    if (jj_3R_ExpressionReward_1805_10_185()) {
     jj_scanpos = xsp;
-    if (jj_3R_186()) return true;
+    if (jj_3R_ExpressionReward_1806_10_186()) return true;
     }
     }
     }
@@ -5467,169 +5467,169 @@ fl.setLHS(s);
     }
     }
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_187()) return true;
+    if (jj_3R_ExpressionRewardContents_1871_9_187()) return true;
     xsp = jj_scanpos;
-    if (jj_3R_188()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionReward_1808_69_188()) jj_scanpos = xsp;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_104()
+  static private boolean jj_3R_TimeBound_1306_99_104()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_198()
+  static private boolean jj_3R_ExpressionFilter_2136_34_198()
  {
     if (jj_scan_token(AND)) return true;
     return false;
   }
 
-  static private boolean jj_3R_196()
+  static private boolean jj_3R_ExpressionFilter_2135_35_196()
  {
     if (jj_scan_token(MAX)) return true;
     return false;
   }
 
-  static private boolean jj_3R_102()
+  static private boolean jj_3R_TimeBound_1305_99_102()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_100()
+  static private boolean jj_3R_TimeBound_1304_99_100()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_34()
+  static private boolean jj_3R_Identifier_2157_9_34()
  {
     if (jj_scan_token(REG_IDENT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_111()
+  static private boolean jj_3R_ExpressionNot_1389_9_111()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_113()) {
+    if (jj_3R_ExpressionNot_1390_17_113()) {
     jj_scanpos = xsp;
-    if (jj_3R_114()) return true;
+    if (jj_3R_ExpressionNot_1392_17_114()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_194()
+  static private boolean jj_3R_ExpressionLabel_2114_47_194()
  {
     if (jj_scan_token(INIT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_112()
+  static private boolean jj_3R_ExpressionAnd_1377_11_112()
  {
     if (jj_scan_token(AND)) return true;
-    if (jj_3R_111()) return true;
+    if (jj_3R_ExpressionNot_1389_9_111()) return true;
     return false;
   }
 
-  static private boolean jj_3R_201()
+  static private boolean jj_3R_ExpressionFilter_2141_11_201()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_109()
+  static private boolean jj_3R_ExpressionAnd_1376_9_109()
  {
-    if (jj_3R_111()) return true;
+    if (jj_3R_ExpressionNot_1389_9_111()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_112()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionAnd_1377_11_112()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_200()
+  static private boolean jj_3R_ExpressionFilter_2137_11_200()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_197()
+  static private boolean jj_3R_ExpressionFilter_2136_11_197()
  {
     if (jj_scan_token(PLUS)) return true;
     return false;
   }
 
-  static private boolean jj_3R_195()
+  static private boolean jj_3R_ExpressionFilter_2135_11_195()
  {
     if (jj_scan_token(MIN)) return true;
     return false;
   }
 
-  static private boolean jj_3R_193()
+  static private boolean jj_3R_ExpressionLabel_2114_30_193()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_233()
+  static private boolean jj_3R_ExpressionStrategy_2049_93_233()
  {
-    if (jj_3R_249()) return true;
+    if (jj_3R_ExpressionMultiNash_1905_9_249()) return true;
     return false;
   }
 
-  static private boolean jj_3R_110()
+  static private boolean jj_3R_ExpressionOr_1364_11_110()
  {
     if (jj_scan_token(OR)) return true;
-    if (jj_3R_109()) return true;
+    if (jj_3R_ExpressionAnd_1376_9_109()) return true;
     return false;
   }
 
-  static private boolean jj_3R_42()
+  static private boolean jj_3R_ExpressionSS_1741_17_42()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_49()
+  static private boolean jj_3R_ExpressionSS_1739_19_49()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_158()
+  static private boolean jj_3R_ExpressionFilter_2130_9_158()
  {
     if (jj_scan_token(FILTER)) return true;
     if (jj_scan_token(LPARENTH)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_195()) {
+    if (jj_3R_ExpressionFilter_2135_11_195()) {
     jj_scanpos = xsp;
-    if (jj_3R_196()) {
+    if (jj_3R_ExpressionFilter_2135_35_196()) {
     jj_scanpos = xsp;
-    if (jj_3R_197()) {
+    if (jj_3R_ExpressionFilter_2136_11_197()) {
     jj_scanpos = xsp;
-    if (jj_3R_198()) {
+    if (jj_3R_ExpressionFilter_2136_34_198()) {
     jj_scanpos = xsp;
-    if (jj_3R_199()) {
+    if (jj_3R_ExpressionFilter_2136_56_199()) {
     jj_scanpos = xsp;
-    if (jj_3R_200()) return true;
+    if (jj_3R_ExpressionFilter_2137_11_200()) return true;
     }
     }
     }
     }
     }
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     xsp = jj_scanpos;
-    if (jj_3R_201()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionFilter_2141_11_201()) jj_scanpos = xsp;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
@@ -5637,28 +5637,28 @@ fl.setLHS(s);
   static private boolean jj_3_1()
  {
     if (jj_scan_token(MODULE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(EQ)) return true;
     return false;
   }
 
-  static private boolean jj_3R_41()
+  static private boolean jj_3R_ExpressionSS_1739_17_41()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_49()) jj_scanpos = xsp;
-    if (jj_3R_50()) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_ExpressionSS_1739_19_49()) jj_scanpos = xsp;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_107()
+  static private boolean jj_3R_ExpressionOr_1363_9_107()
  {
-    if (jj_3R_109()) return true;
+    if (jj_3R_ExpressionAnd_1376_9_109()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_110()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionOr_1364_11_110()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
@@ -5669,91 +5669,91 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_108()
+  static private boolean jj_3R_ExpressionIff_1351_11_108()
  {
     if (jj_scan_token(IFF)) return true;
-    if (jj_3R_107()) return true;
+    if (jj_3R_ExpressionOr_1363_9_107()) return true;
     return false;
   }
 
-  static private boolean jj_3R_38()
+  static private boolean jj_3R_ExpressionSS_1736_9_38()
  {
     if (jj_scan_token(S)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_41()) {
+    if (jj_3R_ExpressionSS_1739_17_41()) {
     jj_scanpos = xsp;
-    if (jj_3R_42()) return true;
+    if (jj_3R_ExpressionSS_1741_17_42()) return true;
     }
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     xsp = jj_scanpos;
-    if (jj_3R_44()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionSS_1744_55_44()) jj_scanpos = xsp;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_97()
+  static private boolean jj_3R_ExpressionIff_1350_9_97()
  {
-    if (jj_3R_107()) return true;
+    if (jj_3R_ExpressionOr_1363_9_107()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_108()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionIff_1351_11_108()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_157()
+  static private boolean jj_3R_ExpressionLabel_2112_9_157()
  {
     if (jj_scan_token(DQUOTE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_193()) {
+    if (jj_3R_ExpressionLabel_2114_30_193()) {
     jj_scanpos = xsp;
-    if (jj_3R_194()) return true;
+    if (jj_3R_ExpressionLabel_2114_47_194()) return true;
     }
     if (jj_scan_token(DQUOTE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_179()
+  static private boolean jj_3R_ExpressionProb_1683_51_179()
  {
-    if (jj_3R_52()) return true;
+    if (jj_3R_Filter_1712_9_52()) return true;
     return false;
   }
 
-  static private boolean jj_3R_72()
+  static private boolean jj_3R_Filter_1715_19_72()
  {
     if (jj_scan_token(MAX)) return true;
     return false;
   }
 
-  static private boolean jj_3R_71()
+  static private boolean jj_3R_Filter_1714_19_71()
  {
     if (jj_scan_token(MIN)) return true;
     return false;
   }
 
-  static private boolean jj_3R_98()
+  static private boolean jj_3R_ExpressionImplies_1338_11_98()
  {
     if (jj_scan_token(IMPLIES)) return true;
-    if (jj_3R_97()) return true;
+    if (jj_3R_ExpressionIff_1350_9_97()) return true;
     return false;
   }
 
-  static private boolean jj_3R_87()
+  static private boolean jj_3R_ExpressionImplies_1337_9_87()
  {
-    if (jj_3R_97()) return true;
+    if (jj_3R_ExpressionIff_1350_9_97()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_98()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionImplies_1338_11_98()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_273()
+  static private boolean jj_3R_ExpressionStrategyCoalitionPlayer_2098_9_273()
  {
     Token xsp;
     xsp = jj_scanpos;
@@ -5764,245 +5764,245 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_62()
+  static private boolean jj_3R_Filter_1713_11_62()
  {
     if (jj_scan_token(LBRACE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_71()) {
+    if (jj_3R_Filter_1714_19_71()) {
     jj_scanpos = xsp;
-    if (jj_3R_72()) return true;
+    if (jj_3R_Filter_1715_19_72()) return true;
     }
     if (jj_scan_token(RBRACE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_88()
+  static private boolean jj_3R_ExpressionITE_1323_17_88()
  {
     if (jj_scan_token(QMARK)) return true;
-    if (jj_3R_87()) return true;
+    if (jj_3R_ExpressionImplies_1337_9_87()) return true;
     if (jj_scan_token(COLON)) return true;
-    if (jj_3R_80()) return true;
+    if (jj_3R_ExpressionITE_1321_9_80()) return true;
     return false;
   }
 
-  static private boolean jj_3R_52()
+  static private boolean jj_3R_Filter_1712_9_52()
  {
     if (jj_scan_token(LBRACE)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RBRACE)) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_62()) { jj_scanpos = xsp; break; }
+      if (jj_3R_Filter_1713_11_62()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
   static private boolean jj_3_14()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(LPARENTH)) return true;
     return false;
   }
 
   static private boolean jj_3_13()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(LPARENTH)) return true;
     return false;
   }
 
   static private boolean jj_3_12()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(LPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_232()
+  static private boolean jj_3R_ExpressionStrategy_2049_51_232()
  {
-    if (jj_3R_153()) return true;
+    if (jj_3R_ExpressionReward_1780_9_153()) return true;
     return false;
   }
 
   static private boolean jj_3_11()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(LPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_274()
+  static private boolean jj_3R_ExpressionStrategyCoalition_2085_13_274()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_273()) return true;
+    if (jj_3R_ExpressionStrategyCoalitionPlayer_2098_9_273()) return true;
     return false;
   }
 
-  static private boolean jj_3R_80()
+  static private boolean jj_3R_ExpressionITE_1321_9_80()
  {
-    if (jj_3R_87()) return true;
+    if (jj_3R_ExpressionImplies_1337_9_87()) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_88()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionITE_1323_17_88()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_264()
+  static private boolean jj_3R_ExpressionStrategyCoalition_2084_11_264()
  {
-    if (jj_3R_273()) return true;
+    if (jj_3R_ExpressionStrategyCoalitionPlayer_2098_9_273()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_274()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionStrategyCoalition_2085_13_274()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_256()
+  static private boolean jj_3R_ExpressionStrategyCoalition_2084_9_256()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_264()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionStrategyCoalition_2084_11_264()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_255()
+  static private boolean jj_3R_ExpressionStrategyCoalition_2082_11_255()
  {
     if (jj_scan_token(TIMES)) return true;
     return false;
   }
 
-  static private boolean jj_3R_105()
+  static private boolean jj_3R_TimeBound_1307_20_105()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     return false;
   }
 
-  static private boolean jj_3R_103()
+  static private boolean jj_3R_TimeBound_1306_20_103()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     return false;
   }
 
-  static private boolean jj_3R_247()
+  static private boolean jj_3R_ExpressionStrategyCoalition_2082_9_247()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_255()) {
+    if (jj_3R_ExpressionStrategyCoalition_2082_11_255()) {
     jj_scanpos = xsp;
-    if (jj_3R_256()) return true;
+    if (jj_3R_ExpressionStrategyCoalition_2084_9_256()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_46()
+  static private boolean jj_3R_Update_906_36_46()
  {
     if (jj_scan_token(AND)) return true;
-    if (jj_3R_45()) return true;
+    if (jj_3R_UpdateElement_919_9_45()) return true;
     return false;
   }
 
-  static private boolean jj_3R_101()
+  static private boolean jj_3R_TimeBound_1305_20_101()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     return false;
   }
 
-  static private boolean jj_3R_99()
+  static private boolean jj_3R_TimeBound_1304_20_99()
  {
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     return false;
   }
 
-  static private boolean jj_3R_94()
+  static private boolean jj_3R_TimeBound_1309_11_94()
  {
     if (jj_scan_token(EQ)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_93()
+  static private boolean jj_3R_TimeBound_1308_11_93()
  {
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_92()
+  static private boolean jj_3R_TimeBound_1307_11_92()
  {
     if (jj_scan_token(GT)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_105()) {
+    if (jj_3R_TimeBound_1307_20_105()) {
     jj_scanpos = xsp;
-    if (jj_3R_106()) return true;
+    if (jj_3R_TimeBound_1307_99_106()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_91()
+  static private boolean jj_3R_TimeBound_1306_11_91()
  {
     if (jj_scan_token(GE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_103()) {
+    if (jj_3R_TimeBound_1306_20_103()) {
     jj_scanpos = xsp;
-    if (jj_3R_104()) return true;
+    if (jj_3R_TimeBound_1306_99_104()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_90()
+  static private boolean jj_3R_TimeBound_1305_11_90()
  {
     if (jj_scan_token(LT)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_101()) {
+    if (jj_3R_TimeBound_1305_20_101()) {
     jj_scanpos = xsp;
-    if (jj_3R_102()) return true;
+    if (jj_3R_TimeBound_1305_99_102()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_89()
+  static private boolean jj_3R_TimeBound_1304_11_89()
  {
     if (jj_scan_token(LE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_99()) {
+    if (jj_3R_TimeBound_1304_20_99()) {
     jj_scanpos = xsp;
-    if (jj_3R_100()) return true;
+    if (jj_3R_TimeBound_1304_99_100()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_248()
+  static private boolean jj_3R_ExpressionStrategyCoalitionList_2069_10_248()
  {
     if (jj_scan_token(COLON)) return true;
-    if (jj_3R_247()) return true;
+    if (jj_3R_ExpressionStrategyCoalition_2082_9_247()) return true;
     return false;
   }
 
-  static private boolean jj_3R_81()
+  static private boolean jj_3R_TimeBound_1304_9_81()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_89()) {
+    if (jj_3R_TimeBound_1304_11_89()) {
     jj_scanpos = xsp;
-    if (jj_3R_90()) {
+    if (jj_3R_TimeBound_1305_11_90()) {
     jj_scanpos = xsp;
-    if (jj_3R_91()) {
+    if (jj_3R_TimeBound_1306_11_91()) {
     jj_scanpos = xsp;
-    if (jj_3R_92()) {
+    if (jj_3R_TimeBound_1307_11_92()) {
     jj_scanpos = xsp;
-    if (jj_3R_93()) {
+    if (jj_3R_TimeBound_1308_11_93()) {
     jj_scanpos = xsp;
-    if (jj_3R_94()) return true;
+    if (jj_3R_TimeBound_1309_11_94()) return true;
     }
     }
     }
@@ -6011,7 +6011,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_239()
+  static private boolean jj_3R_ExpressionProb_1668_26_239()
  {
     if (jj_scan_token(MAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6019,28 +6019,28 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_230()
+  static private boolean jj_3R_ExpressionStrategyCoalitionList_2068_9_230()
  {
-    if (jj_3R_247()) return true;
+    if (jj_3R_ExpressionStrategyCoalition_2082_9_247()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_248()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionStrategyCoalitionList_2069_10_248()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_45()
+  static private boolean jj_3R_UpdateElement_919_9_45()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_53()) return true;
+    if (jj_3R_IdentifierPrime_2190_9_53()) return true;
     if (jj_scan_token(EQ)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_238()
+  static private boolean jj_3R_ExpressionProb_1667_26_238()
  {
     if (jj_scan_token(MIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6048,14 +6048,14 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_237()
+  static private boolean jj_3R_ExpressionProb_1666_26_237()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_178()
+  static private boolean jj_3R_ExpressionProb_1681_10_178()
  {
     if (jj_scan_token(PMAXMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6063,13 +6063,13 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_66()
+  static private boolean jj_3R_ExpressionTemporalUnary_1291_17_66()
  {
-    if (jj_3R_80()) return true;
+    if (jj_3R_ExpressionITE_1321_9_80()) return true;
     return false;
   }
 
-  static private boolean jj_3R_236()
+  static private boolean jj_3R_ExpressionProb_1664_26_236()
  {
     if (jj_scan_token(MAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6077,7 +6077,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_211()
+  static private boolean jj_3R_ExpressionProb_1673_17_211()
  {
     if (jj_scan_token(MAXMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6085,7 +6085,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_177()
+  static private boolean jj_3R_ExpressionProb_1680_10_177()
  {
     if (jj_scan_token(PMAXMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6093,7 +6093,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_235()
+  static private boolean jj_3R_ExpressionProb_1663_26_235()
  {
     if (jj_scan_token(MIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6101,7 +6101,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_210()
+  static private boolean jj_3R_ExpressionProb_1672_17_210()
  {
     if (jj_scan_token(MAXMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6109,7 +6109,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_176()
+  static private boolean jj_3R_ExpressionProb_1679_10_176()
  {
     if (jj_scan_token(PMINMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6117,20 +6117,20 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_79()
+  static private boolean jj_3R_ExpressionTemporalUnary_1287_19_79()
  {
-    if (jj_3R_81()) return true;
+    if (jj_3R_TimeBound_1304_9_81()) return true;
     return false;
   }
 
-  static private boolean jj_3R_234()
+  static private boolean jj_3R_ExpressionProb_1662_26_234()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_209()
+  static private boolean jj_3R_ExpressionProb_1671_17_209()
  {
     if (jj_scan_token(MINMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6138,7 +6138,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_175()
+  static private boolean jj_3R_ExpressionProb_1678_10_175()
  {
     if (jj_scan_token(PMINMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6146,13 +6146,13 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_78()
+  static private boolean jj_3R_ExpressionTemporalUnary_1286_19_78()
  {
     if (jj_scan_token(G)) return true;
     return false;
   }
 
-  static private boolean jj_3R_208()
+  static private boolean jj_3R_ExpressionProb_1670_17_208()
  {
     if (jj_scan_token(MINMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6160,7 +6160,7 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_174()
+  static private boolean jj_3R_ExpressionProb_1677_10_174()
  {
     if (jj_scan_token(PMAX)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6168,13 +6168,13 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_77()
+  static private boolean jj_3R_ExpressionTemporalUnary_1285_19_77()
  {
     if (jj_scan_token(F)) return true;
     return false;
   }
 
-  static private boolean jj_3R_173()
+  static private boolean jj_3R_ExpressionProb_1676_10_173()
  {
     if (jj_scan_token(PMIN)) return true;
     if (jj_scan_token(EQ)) return true;
@@ -6182,190 +6182,190 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_76()
+  static private boolean jj_3R_ExpressionTemporalUnary_1284_19_76()
  {
     if (jj_scan_token(X)) return true;
     return false;
   }
 
-  static private boolean jj_3R_204()
+  static private boolean jj_3R_ExpressionProb_1660_25_204()
  {
-    if (jj_3R_50()) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_203()
+  static private boolean jj_3R_ExpressionProb_1659_26_203()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_37()) return true;
+    if (jj_3R_IdentifierExpression_2168_9_37()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_207()
+  static private boolean jj_3R_ExpressionProb_1666_17_207()
  {
     if (jj_scan_token(MAX)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_237()) {
+    if (jj_3R_ExpressionProb_1666_26_237()) {
     jj_scanpos = xsp;
-    if (jj_3R_238()) {
+    if (jj_3R_ExpressionProb_1667_26_238()) {
     jj_scanpos = xsp;
-    if (jj_3R_239()) return true;
+    if (jj_3R_ExpressionProb_1668_26_239()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_39()
+  static private boolean jj_3R_Update_906_10_39()
  {
-    if (jj_3R_45()) return true;
+    if (jj_3R_UpdateElement_919_9_45()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_46()) { jj_scanpos = xsp; break; }
+      if (jj_3R_Update_906_36_46()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_65()
+  static private boolean jj_3R_ExpressionTemporalUnary_1282_17_65()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_76()) {
+    if (jj_3R_ExpressionTemporalUnary_1284_19_76()) {
     jj_scanpos = xsp;
-    if (jj_3R_77()) {
+    if (jj_3R_ExpressionTemporalUnary_1285_19_77()) {
     jj_scanpos = xsp;
-    if (jj_3R_78()) return true;
+    if (jj_3R_ExpressionTemporalUnary_1286_19_78()) return true;
     }
     }
     xsp = jj_scanpos;
-    if (jj_3R_79()) jj_scanpos = xsp;
-    if (jj_3R_60()) return true;
+    if (jj_3R_ExpressionTemporalUnary_1287_19_79()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionTemporalUnary_1280_9_60()) return true;
     return false;
   }
 
   static private boolean jj_3_5()
  {
-    if (jj_3R_35()) return true;
+    if (jj_3R_Update_904_9_35()) return true;
     return false;
   }
 
-  static private boolean jj_3R_206()
+  static private boolean jj_3R_ExpressionProb_1662_17_206()
  {
     if (jj_scan_token(MIN)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_234()) {
+    if (jj_3R_ExpressionProb_1662_26_234()) {
     jj_scanpos = xsp;
-    if (jj_3R_235()) {
+    if (jj_3R_ExpressionProb_1663_26_235()) {
     jj_scanpos = xsp;
-    if (jj_3R_236()) return true;
+    if (jj_3R_ExpressionProb_1664_26_236()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_192()
+  static private boolean jj_3R_ExpressionStrategy_2051_11_192()
  {
-    if (jj_3R_151()) return true;
+    if (jj_3R_ExpressionParenth_1633_9_151()) return true;
     return false;
   }
 
-  static private boolean jj_3R_35()
+  static private boolean jj_3R_Update_904_9_35()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_39()) {
+    if (jj_3R_Update_906_10_39()) {
     jj_scanpos = xsp;
     if (jj_scan_token(76)) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_205()
+  static private boolean jj_3R_ExpressionProb_1661_17_205()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_231()
+  static private boolean jj_3R_ExpressionStrategy_2049_11_231()
  {
-    if (jj_3R_152()) return true;
+    if (jj_3R_ExpressionProb_1657_9_152()) return true;
     return false;
   }
 
-  static private boolean jj_3R_277()
+  static private boolean jj_3R_MultiNashRewardIndexes_1985_73_277()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_191()
+  static private boolean jj_3R_ExpressionStrategy_2049_9_191()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_231()) {
+    if (jj_3R_ExpressionStrategy_2049_11_231()) {
     jj_scanpos = xsp;
-    if (jj_3R_232()) {
+    if (jj_3R_ExpressionStrategy_2049_51_232()) {
     jj_scanpos = xsp;
-    if (jj_3R_233()) return true;
+    if (jj_3R_ExpressionStrategy_2049_93_233()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_190()
+  static private boolean jj_3R_ExpressionStrategy_2045_11_190()
  {
     if (jj_scan_token(DLBRACKET)) return true;
-    if (jj_3R_230()) return true;
+    if (jj_3R_ExpressionStrategyCoalitionList_2068_9_230()) return true;
     if (jj_scan_token(DRBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_60()
+  static private boolean jj_3R_ExpressionTemporalUnary_1280_9_60()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_65()) {
+    if (jj_3R_ExpressionTemporalUnary_1282_17_65()) {
     jj_scanpos = xsp;
-    if (jj_3R_66()) return true;
+    if (jj_3R_ExpressionTemporalUnary_1291_17_66()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_189()
+  static private boolean jj_3R_ExpressionStrategy_2044_10_189()
  {
     if (jj_scan_token(DLT)) return true;
-    if (jj_3R_230()) return true;
+    if (jj_3R_ExpressionStrategyCoalitionList_2068_9_230()) return true;
     if (jj_scan_token(DGT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_172()
+  static private boolean jj_3R_ExpressionProb_1659_10_172()
  {
     if (jj_scan_token(P)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_203()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionProb_1659_26_203()) jj_scanpos = xsp;
     xsp = jj_scanpos;
-    if (jj_3R_204()) {
+    if (jj_3R_ExpressionProb_1660_25_204()) {
     jj_scanpos = xsp;
-    if (jj_3R_205()) {
+    if (jj_3R_ExpressionProb_1661_17_205()) {
     jj_scanpos = xsp;
-    if (jj_3R_206()) {
+    if (jj_3R_ExpressionProb_1662_17_206()) {
     jj_scanpos = xsp;
-    if (jj_3R_207()) {
+    if (jj_3R_ExpressionProb_1666_17_207()) {
     jj_scanpos = xsp;
-    if (jj_3R_208()) {
+    if (jj_3R_ExpressionProb_1670_17_208()) {
     jj_scanpos = xsp;
-    if (jj_3R_209()) {
+    if (jj_3R_ExpressionProb_1671_17_209()) {
     jj_scanpos = xsp;
-    if (jj_3R_210()) {
+    if (jj_3R_ExpressionProb_1672_17_210()) {
     jj_scanpos = xsp;
-    if (jj_3R_211()) return true;
+    if (jj_3R_ExpressionProb_1673_17_211()) return true;
     }
     }
     }
@@ -6376,57 +6376,57 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_70()
+  static private boolean jj_3R_ExpressionTemporalBinary_1266_19_70()
  {
-    if (jj_3R_81()) return true;
+    if (jj_3R_TimeBound_1304_9_81()) return true;
     return false;
   }
 
-  static private boolean jj_3R_69()
+  static private boolean jj_3R_ExpressionTemporalBinary_1265_19_69()
  {
     if (jj_scan_token(R)) return true;
     return false;
   }
 
-  static private boolean jj_3R_156()
+  static private boolean jj_3R_ExpressionStrategy_2041_9_156()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_189()) {
+    if (jj_3R_ExpressionStrategy_2044_10_189()) {
     jj_scanpos = xsp;
-    if (jj_3R_190()) return true;
+    if (jj_3R_ExpressionStrategy_2045_11_190()) return true;
     }
     xsp = jj_scanpos;
-    if (jj_3R_191()) {
+    if (jj_3R_ExpressionStrategy_2049_9_191()) {
     jj_scanpos = xsp;
-    if (jj_3R_192()) return true;
+    if (jj_3R_ExpressionStrategy_2051_11_192()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_68()
+  static private boolean jj_3R_ExpressionTemporalBinary_1264_19_68()
  {
     if (jj_scan_token(W)) return true;
     return false;
   }
 
-  static private boolean jj_3R_152()
+  static private boolean jj_3R_ExpressionProb_1657_9_152()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_172()) {
+    if (jj_3R_ExpressionProb_1659_10_172()) {
     jj_scanpos = xsp;
-    if (jj_3R_173()) {
+    if (jj_3R_ExpressionProb_1676_10_173()) {
     jj_scanpos = xsp;
-    if (jj_3R_174()) {
+    if (jj_3R_ExpressionProb_1677_10_174()) {
     jj_scanpos = xsp;
-    if (jj_3R_175()) {
+    if (jj_3R_ExpressionProb_1678_10_175()) {
     jj_scanpos = xsp;
-    if (jj_3R_176()) {
+    if (jj_3R_ExpressionProb_1679_10_176()) {
     jj_scanpos = xsp;
-    if (jj_3R_177()) {
+    if (jj_3R_ExpressionProb_1680_10_177()) {
     jj_scanpos = xsp;
-    if (jj_3R_178()) return true;
+    if (jj_3R_ExpressionProb_1681_10_178()) return true;
     }
     }
     }
@@ -6434,111 +6434,111 @@ fl.setLHS(s);
     }
     }
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     xsp = jj_scanpos;
-    if (jj_3R_179()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionProb_1683_51_179()) jj_scanpos = xsp;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_67()
+  static private boolean jj_3R_ExpressionTemporalBinary_1263_19_67()
  {
     if (jj_scan_token(U)) return true;
     return false;
   }
 
-  static private boolean jj_3R_61()
+  static private boolean jj_3R_ExpressionTemporalBinary_1261_17_61()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_67()) {
+    if (jj_3R_ExpressionTemporalBinary_1263_19_67()) {
     jj_scanpos = xsp;
-    if (jj_3R_68()) {
+    if (jj_3R_ExpressionTemporalBinary_1264_19_68()) {
     jj_scanpos = xsp;
-    if (jj_3R_69()) return true;
+    if (jj_3R_ExpressionTemporalBinary_1265_19_69()) return true;
     }
     }
     xsp = jj_scanpos;
-    if (jj_3R_70()) jj_scanpos = xsp;
-    if (jj_3R_60()) return true;
+    if (jj_3R_ExpressionTemporalBinary_1266_19_70()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionTemporalUnary_1280_9_60()) return true;
     return false;
   }
 
-  static private boolean jj_3R_202()
+  static private boolean jj_3R_ExpressionFuncArgs_1583_72_202()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_171()
+  static private boolean jj_3R_ExpressionFuncOldStyle_1571_83_171()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_51()
+  static private boolean jj_3R_ExpressionTemporalBinary_1258_9_51()
  {
-    if (jj_3R_60()) return true;
+    if (jj_3R_ExpressionTemporalUnary_1280_9_60()) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_61()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionTemporalBinary_1261_17_61()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_268()
+  static private boolean jj_3R_ExpressionMultiNash_1909_122_268()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_155()
+  static private boolean jj_3R_ExpressionForAll_2021_9_155()
  {
     if (jj_scan_token(A)) return true;
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_266()
+  static private boolean jj_3R_ExpressionMultiNash_1907_122_266()
  {
     if (jj_scan_token(EQ)) return true;
     if (jj_scan_token(QMARK)) return true;
     return false;
   }
 
-  static private boolean jj_3R_151()
+  static private boolean jj_3R_ExpressionParenth_1633_9_151()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_170()
+  static private boolean jj_3R_ExpressionFuncOldStyle_1571_60_170()
  {
     if (jj_scan_token(MAX)) return true;
     return false;
   }
 
-  static private boolean jj_3R_162()
+  static private boolean jj_3R_ExpressionLiteral_1620_9_162()
  {
     if (jj_scan_token(FALSE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_154()
+  static private boolean jj_3R_ExpressionExists_2002_9_154()
  {
     if (jj_scan_token(E)) return true;
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_161()
+  static private boolean jj_3R_ExpressionLiteral_1618_9_161()
  {
     if (jj_scan_token(TRUE)) return true;
     return false;
@@ -6550,223 +6550,223 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_43()
+  static private boolean jj_3R_Expression_1228_9_43()
  {
-    if (jj_3R_51()) return true;
+    if (jj_3R_ExpressionTemporalBinary_1258_9_51()) return true;
     return false;
   }
 
-  static private boolean jj_3R_160()
+  static private boolean jj_3R_ExpressionLiteral_1607_9_160()
  {
     if (jj_scan_token(REG_DOUBLE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_95()
+  static private boolean jj_3R_SystemHideRename_1150_81_95()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_276()
+  static private boolean jj_3R_MultiNashRewardIndexes_1985_10_276()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_275()
+  static private boolean jj_3R_MultiNashRewardIndexes_1984_9_275()
  {
     if (jj_scan_token(LBRACE)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_276()) {
+    if (jj_3R_MultiNashRewardIndexes_1985_10_276()) {
     jj_scanpos = xsp;
-    if (jj_3R_277()) return true;
+    if (jj_3R_MultiNashRewardIndexes_1985_73_277()) return true;
     }
     if (jj_scan_token(RBRACE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_169()
+  static private boolean jj_3R_ExpressionFuncOldStyle_1571_37_169()
  {
     if (jj_scan_token(MIN)) return true;
     return false;
   }
 
-  static private boolean jj_3R_159()
+  static private boolean jj_3R_ExpressionLiteral_1594_9_159()
  {
     if (jj_scan_token(REG_INT)) return true;
     return false;
   }
 
-  static private boolean jj_3R_147()
+  static private boolean jj_3R_ExpressionLiteral_1593_9_147()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_159()) {
+    if (jj_3R_ExpressionLiteral_1594_9_159()) {
     jj_scanpos = xsp;
-    if (jj_3R_160()) {
+    if (jj_3R_ExpressionLiteral_1607_9_160()) {
     jj_scanpos = xsp;
-    if (jj_3R_161()) {
+    if (jj_3R_ExpressionLiteral_1618_9_161()) {
     jj_scanpos = xsp;
-    if (jj_3R_162()) return true;
+    if (jj_3R_ExpressionLiteral_1620_9_162()) return true;
     }
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_167()
+  static private boolean jj_3R_ExpressionFuncMinMax_1557_42_167()
  {
     if (jj_scan_token(MAX)) return true;
     return false;
   }
 
-  static private boolean jj_3R_168()
+  static private boolean jj_3R_ExpressionFuncArgs_1583_9_168()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_202()) { jj_scanpos = xsp; break; }
+      if (jj_3R_ExpressionFuncArgs_1583_72_202()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
 
-  static private boolean jj_3R_270()
+  static private boolean jj_3R_ExpressionMultiNashReward_1964_9_270()
  {
     if (jj_scan_token(R)) return true;
-    if (jj_3R_275()) return true;
+    if (jj_3R_MultiNashRewardIndexes_1984_9_275()) return true;
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_187()) return true;
+    if (jj_3R_ExpressionRewardContents_1871_9_187()) return true;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_252()
+  static private boolean jj_3R_RewardIndex_1840_125_252()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_150()
+  static private boolean jj_3R_ExpressionFuncOldStyle_1571_9_150()
  {
     if (jj_scan_token(FUNC)) return true;
     if (jj_scan_token(LPARENTH)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_169()) {
+    if (jj_3R_ExpressionFuncOldStyle_1571_37_169()) {
     jj_scanpos = xsp;
-    if (jj_3R_170()) {
+    if (jj_3R_ExpressionFuncOldStyle_1571_60_170()) {
     jj_scanpos = xsp;
-    if (jj_3R_171()) return true;
+    if (jj_3R_ExpressionFuncOldStyle_1571_83_171()) return true;
     }
     }
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_168()) return true;
+    if (jj_3R_ExpressionFuncArgs_1583_9_168()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_84()
+  static private boolean jj_3R_SystemAtomic_1180_10_84()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_36()) return true;
+    if (jj_3R_SystemDefn_1057_9_36()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_83()
+  static private boolean jj_3R_SystemAtomic_1178_10_83()
  {
     if (jj_scan_token(DQUOTE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(DQUOTE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_269()
+  static private boolean jj_3R_ExpressionMultiNashProb_1942_9_269()
  {
     if (jj_scan_token(P)) return true;
     if (jj_scan_token(LBRACKET)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     if (jj_scan_token(RBRACKET)) return true;
     return false;
   }
 
-  static private boolean jj_3R_166()
+  static private boolean jj_3R_ExpressionFuncMinMax_1557_11_166()
  {
     if (jj_scan_token(MIN)) return true;
     return false;
   }
 
-  static private boolean jj_3R_82()
+  static private boolean jj_3R_SystemAtomic_1176_9_82()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_75()
+  static private boolean jj_3R_SystemParallel_1120_65_75()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_149()
+  static private boolean jj_3R_ExpressionFuncMinMax_1557_9_149()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_166()) {
+    if (jj_3R_ExpressionFuncMinMax_1557_11_166()) {
     jj_scanpos = xsp;
-    if (jj_3R_167()) return true;
+    if (jj_3R_ExpressionFuncMinMax_1557_42_167()) return true;
     }
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_168()) return true;
+    if (jj_3R_ExpressionFuncArgs_1583_9_168()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_73()
+  static private boolean jj_3R_SystemAtomic_1173_9_73()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_82()) {
+    if (jj_3R_SystemAtomic_1176_9_82()) {
     jj_scanpos = xsp;
-    if (jj_3R_83()) {
+    if (jj_3R_SystemAtomic_1178_10_83()) {
     jj_scanpos = xsp;
-    if (jj_3R_84()) return true;
+    if (jj_3R_SystemAtomic_1180_10_84()) return true;
     }
     }
     return false;
   }
 
-  static private boolean jj_3R_263()
+  static private boolean jj_3R_RewardIndex_1845_101_263()
  {
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_272()
+  static private boolean jj_3R_ExpressionMultiNash_1920_25_272()
  {
-    if (jj_3R_270()) return true;
+    if (jj_3R_ExpressionMultiNashReward_1964_9_270()) return true;
     return false;
   }
 
-  static private boolean jj_3R_271()
+  static private boolean jj_3R_ExpressionMultiNash_1918_26_271()
  {
-    if (jj_3R_269()) return true;
+    if (jj_3R_ExpressionMultiNashProb_1942_9_269()) return true;
     return false;
   }
 
-  static private boolean jj_3R_96()
+  static private boolean jj_3R_SystemHideRename_1157_19_96()
  {
     if (jj_scan_token(COMMA)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(RENAME)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
@@ -6778,91 +6778,91 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_165()
+  static private boolean jj_3R_ExpressionFuncOrIdent_1544_11_165()
  {
     if (jj_scan_token(LPARENTH)) return true;
-    if (jj_3R_168()) return true;
+    if (jj_3R_ExpressionFuncArgs_1583_9_168()) return true;
     if (jj_scan_token(RPARENTH)) return true;
     return false;
   }
 
-  static private boolean jj_3R_261()
+  static private boolean jj_3R_ExpressionMultiNash_1917_18_261()
  {
     if (jj_scan_token(PLUS)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_271()) {
+    if (jj_3R_ExpressionMultiNash_1918_26_271()) {
     jj_scanpos = xsp;
-    if (jj_3R_272()) return true;
+    if (jj_3R_ExpressionMultiNash_1920_25_272()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_164()
+  static private boolean jj_3R_ExpressionFuncOrIdent_1541_11_164()
  {
-    if (jj_3R_53()) return true;
+    if (jj_3R_IdentifierPrime_2190_9_53()) return true;
     return false;
   }
 
-  static private boolean jj_3R_163()
+  static private boolean jj_3R_ExpressionFuncOrIdent_1540_11_163()
  {
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     return false;
   }
 
-  static private boolean jj_3R_260()
+  static private boolean jj_3R_ExpressionMultiNash_1915_17_260()
  {
-    if (jj_3R_270()) return true;
+    if (jj_3R_ExpressionMultiNashReward_1964_9_270()) return true;
     return false;
   }
 
-  static private boolean jj_3R_259()
+  static private boolean jj_3R_ExpressionMultiNash_1913_18_259()
  {
-    if (jj_3R_269()) return true;
+    if (jj_3R_ExpressionMultiNashProb_1942_9_269()) return true;
     return false;
   }
 
-  static private boolean jj_3R_55()
+  static private boolean jj_3R_SystemInterleaved_1095_70_55()
  {
     if (jj_scan_token(OR)) return true;
     if (jj_scan_token(OR)) return true;
     if (jj_scan_token(OR)) return true;
-    if (jj_3R_40()) return true;
+    if (jj_3R_SystemFullParallel_1070_9_40()) return true;
     return false;
   }
 
-  static private boolean jj_3R_86()
+  static private boolean jj_3R_SystemHideRename_1154_11_86()
  {
     if (jj_scan_token(LBRACE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     if (jj_scan_token(RENAME)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_96()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemHideRename_1157_19_96()) { jj_scanpos = xsp; break; }
     }
     if (jj_scan_token(RBRACE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_148()
+  static private boolean jj_3R_ExpressionFuncOrIdent_1539_9_148()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_163()) {
+    if (jj_3R_ExpressionFuncOrIdent_1540_11_163()) {
     jj_scanpos = xsp;
-    if (jj_3R_164()) return true;
+    if (jj_3R_ExpressionFuncOrIdent_1541_11_164()) return true;
     }
     xsp = jj_scanpos;
-    if (jj_3R_165()) jj_scanpos = xsp;
+    if (jj_3R_ExpressionFuncOrIdent_1544_11_165()) jj_scanpos = xsp;
     return false;
   }
 
-  static private boolean jj_3R_267()
+  static private boolean jj_3R_ExpressionMultiNash_1909_16_267()
  {
-    if (jj_3R_50()) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
@@ -6872,34 +6872,34 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_265()
+  static private boolean jj_3R_ExpressionMultiNash_1907_16_265()
  {
-    if (jj_3R_50()) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_LtGt_2219_9_50()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
-  static private boolean jj_3R_85()
+  static private boolean jj_3R_SystemHideRename_1148_9_85()
  {
     if (jj_scan_token(DIVIDE)) return true;
     if (jj_scan_token(LBRACE)) return true;
-    if (jj_3R_34()) return true;
+    if (jj_3R_Identifier_2157_9_34()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_95()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemHideRename_1150_81_95()) { jj_scanpos = xsp; break; }
     }
     if (jj_scan_token(RBRACE)) return true;
     return false;
   }
 
-  static private boolean jj_3R_74()
+  static private boolean jj_3R_SystemHideRename_1148_9_74()
  {
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_85()) {
+    if (jj_3R_SystemHideRename_1148_9_85()) {
     jj_scanpos = xsp;
-    if (jj_3R_86()) return true;
+    if (jj_3R_SystemHideRename_1154_11_86()) return true;
     }
     return false;
   }
@@ -6911,13 +6911,13 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_63()
+  static private boolean jj_3R_SystemHideRename_1145_9_63()
  {
-    if (jj_3R_73()) return true;
+    if (jj_3R_SystemAtomic_1173_9_73()) return true;
     Token xsp;
     while (true) {
       xsp = jj_scanpos;
-      if (jj_3R_74()) { jj_scanpos = xsp; break; }
+      if (jj_3R_SystemHideRename_1148_9_74()) { jj_scanpos = xsp; break; }
     }
     return false;
   }
@@ -6929,41 +6929,41 @@ fl.setLHS(s);
     return false;
   }
 
-  static private boolean jj_3R_258()
+  static private boolean jj_3R_ExpressionMultiNash_1909_9_258()
  {
     if (jj_scan_token(MAX)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_267()) {
+    if (jj_3R_ExpressionMultiNash_1909_16_267()) {
     jj_scanpos = xsp;
-    if (jj_3R_268()) return true;
+    if (jj_3R_ExpressionMultiNash_1909_122_268()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_257()
+  static private boolean jj_3R_ExpressionMultiNash_1907_9_257()
  {
     if (jj_scan_token(MIN)) return true;
     Token xsp;
     xsp = jj_scanpos;
-    if (jj_3R_265()) {
+    if (jj_3R_ExpressionMultiNash_1907_16_265()) {
     jj_scanpos = xsp;
-    if (jj_3R_266()) return true;
+    if (jj_3R_ExpressionMultiNash_1907_122_266()) return true;
     }
     return false;
   }
 
-  static private boolean jj_3R_146()
+  static private boolean jj_3R_ExpressionBasic_1516_17_146()
  {
-    if (jj_3R_158()) return true;
+    if (jj_3R_ExpressionFilter_2130_9_158()) return true;
     return false;
   }
 
-  static private boolean jj_3R_251()
+  static private boolean jj_3R_RewardIndex_1840_75_251()
  {
     if (jj_scan_token(DISCOUNT)) return true;
     if (jj_scan_token(EQ)) return true;
-    if (jj_3R_43()) return true;
+    if (jj_3R_Expression_1228_9_43()) return true;
     return false;
   }
 
@@ -6985,193 +6985,206 @@ fl.setLHS(s);
   static private int[] jj_la1_2;
   static private int[] jj_la1_3;
   static {
-      jj_la1_init_0();
-      jj_la1_init_1();
-      jj_la1_init_2();
-      jj_la1_init_3();
-   }
-   private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0xa08023c0,0x208023c0,0x80000000,0x3404048,0x0,0x3404048,0x3404048,0x0,0x3404048,0x2000,0x0,0x100,0x2380,0x1010,0x1010,0x0,0x40,0x0,0x80000000,0x30,0x0,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44000000,0x0,0x47404008,0x47404008,0x47404008,0x47404008,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x3404008,0x0,0x0,0x0,0x0,0x0,0x0,0x3404008,0x3404008,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47404808,0x0,0x47404008,0x0,0x0,0x5f404408,0x0,0x0,0x0,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,};
-   }
-   private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x7f80e888,0x3f002088,0x4080c800,0xa87f0448,0x0,0xa87f0448,0xa87f0448,0x0,0xa87f0448,0x4000000,0x2080,0x0,0x17002080,0x4,0x4,0x28000000,0x28000000,0x0,0x0,0x4,0x0,0x1,0x0,0x0,0x807f1440,0x0,0x0,0x0,0x807f1440,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x807f1440,0x807f1440,0x807f1440,0x807f1440,0x807f1440,0x0,0x0,0x0,0x0,0x0,0x0,0x807f0440,0x0,0x0,0x0,0x0,0x0,0x0,0x807f0440,0x807f0440,0x0,0x0,0x440,0x440,0x0,0x0,0x0,0x440,0x440,0x770,0x7f0000,0x0,0x0,0x440,0x0,0x0,0x0,0x0,0x0,0x440,0x440,0x770,0x80000000,0x0,0x807f1440,0x0,0x807f1440,0x0,0x0,0x807f1442,0x0,0x0,0x440,0x400000,0x400000,0x0,0x807f1440,0x0,0x807f0440,0x807f0440,0x0,0x0,0x0,0x0,0x0,0x0,0x440,0x0,0x440,0x0,0x0,0x0,};
-   }
-   private static void jj_la1_init_2() {
-      jj_la1_2 = new int[] {0xf80,0xb80,0x400,0x2200907f,0x400000,0x2200907f,0x2200907f,0x400000,0x2200907f,0x0,0x0,0x80,0xb80,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x0,0x8000000,0x0,0x2200907f,0x10000,0x2001000,0x800000,0x2a00907f,0x8000000,0x800000,0x0,0x800000,0x800000,0x80000000,0x800000,0x800000,0x80000000,0x2000000,0x800000,0x8000000,0x8000000,0x6020,0x8000000,0x6020,0x0,0x8000000,0x2200907f,0x2200907f,0x2200907f,0x2200907f,0x2200907f,0x8000000,0x0,0x40000,0x80000,0x20000,0x10000,0x2200907f,0x0,0x0,0x0,0x0,0x0,0x0,0x2200107f,0x2200107f,0x0,0x2000000,0x0,0x0,0x800000,0x1000,0x2000000,0x0,0x0,0x0,0x0,0x80000000,0x80000000,0x0,0x2000000,0x2000000,0x80000000,0x2000000,0x80000000,0x0,0x0,0x0,0x3f,0x80000000,0x2200907f,0x800000,0x2200907f,0x0,0x40,0x2200907f,0x0,0x0,0x0,0x20,0x20,0x0,0x2200907f,0x20000000,0x3f,0x200003f,0x200000,0x800000,0x0,0x0,0x0,0x0,0x30000,0x800000,0x0,0x0,0x0,0x200000,};
-   }
-   private static void jj_la1_init_3() {
-      jj_la1_3 = new int[] {0x0,0x0,0x0,0x1f0420,0x0,0x1f0420,0x1f0420,0x0,0x1f0420,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x100000,0x0,0x0,0x200,0x1f0420,0x0,0x0,0x0,0x1f0420,0x0,0x0,0x100000,0x0,0x0,0x1000,0x0,0x0,0x1000,0x110000,0x0,0x100000,0x100000,0x0,0x19a,0x0,0x0,0x19a,0x1f0420,0x1f0420,0x1f0420,0x1f0420,0x1f0420,0x19a,0x8000,0x0,0x0,0x0,0x0,0x1f0420,0x6,0x198,0x600,0x600,0x1800,0x1800,0x1f0420,0x1f0020,0x180000,0x0,0x0,0x100000,0x0,0x60000,0x0,0x2,0x2,0x19a,0x0,0x0,0x0,0x0,0x0,0x19a,0x0,0x0,0x0,0x2,0x2,0x19a,0x0,0x0,0x1f0420,0x0,0x1f0420,0x1000,0x0,0x1f0420,0x19a,0x19a,0x0,0x0,0x0,0x200,0x1f0420,0x20,0x0,0x0,0x0,0x0,0x120000,0x800,0x120000,0x100000,0x100200,0x0,0x100000,0x6,0x198,0x0,};
-   }
+	   jj_la1_init_0();
+	   jj_la1_init_1();
+	   jj_la1_init_2();
+	   jj_la1_init_3();
+	}
+	private static void jj_la1_init_0() {
+	   jj_la1_0 = new int[] {0xa08023c0,0x208023c0,0x80000000,0x3404048,0x0,0x3404048,0x3404048,0x0,0x3404048,0x2000,0x0,0x100,0x2380,0x1010,0x1010,0x0,0x40,0x0,0x80000000,0x30,0x0,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x44000000,0x0,0x47404008,0x47404008,0x47404008,0x47404008,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x3404008,0x0,0x0,0x0,0x0,0x0,0x0,0x3404008,0x3404008,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x47404808,0x0,0x47404008,0x0,0x0,0x5f404408,0x0,0x0,0x0,0x0,0x0,0x0,0x47404008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,};
+	}
+	private static void jj_la1_init_1() {
+	   jj_la1_1 = new int[] {0x7f80e888,0x3f002088,0x4080c800,0xa87f0448,0x0,0xa87f0448,0xa87f0448,0x0,0xa87f0448,0x4000000,0x2080,0x0,0x17002080,0x4,0x4,0x28000000,0x28000000,0x0,0x0,0x4,0x0,0x1,0x0,0x0,0x807f1440,0x0,0x0,0x0,0x807f1440,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x807f1440,0x807f1440,0x807f1440,0x807f1440,0x807f1440,0x0,0x0,0x0,0x0,0x0,0x0,0x807f0440,0x0,0x0,0x0,0x0,0x0,0x0,0x807f0440,0x807f0440,0x0,0x0,0x440,0x440,0x0,0x0,0x0,0x440,0x440,0x770,0x7f0000,0x0,0x0,0x440,0x0,0x0,0x0,0x0,0x0,0x440,0x440,0x770,0x80000000,0x0,0x807f1440,0x0,0x807f1440,0x0,0x0,0x807f1442,0x0,0x0,0x440,0x400000,0x400000,0x0,0x807f1440,0x0,0x807f0440,0x807f0440,0x0,0x0,0x0,0x0,0x0,0x0,0x440,0x0,0x440,0x0,0x0,0x0,};
+	}
+	private static void jj_la1_init_2() {
+	   jj_la1_2 = new int[] {0xf80,0xb80,0x400,0x2200907f,0x400000,0x2200907f,0x2200907f,0x400000,0x2200907f,0x0,0x0,0x80,0xb80,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x0,0x8000000,0x0,0x2200907f,0x10000,0x2001000,0x800000,0x2a00907f,0x8000000,0x800000,0x0,0x800000,0x800000,0x80000000,0x800000,0x800000,0x80000000,0x2000000,0x800000,0x8000000,0x8000000,0x6020,0x8000000,0x6020,0x0,0x8000000,0x2200907f,0x2200907f,0x2200907f,0x2200907f,0x2200907f,0x8000000,0x0,0x40000,0x80000,0x20000,0x10000,0x2200907f,0x0,0x0,0x0,0x0,0x0,0x0,0x2200107f,0x2200107f,0x0,0x2000000,0x0,0x0,0x800000,0x1000,0x2000000,0x0,0x0,0x0,0x0,0x80000000,0x80000000,0x0,0x2000000,0x2000000,0x80000000,0x2000000,0x80000000,0x0,0x0,0x0,0x3f,0x80000000,0x2200907f,0x800000,0x2200907f,0x0,0x40,0x2200907f,0x0,0x0,0x0,0x20,0x20,0x0,0x2200907f,0x20000000,0x3f,0x200003f,0x200000,0x800000,0x0,0x0,0x0,0x0,0x30000,0x800000,0x0,0x0,0x0,0x200000,};
+	}
+	private static void jj_la1_init_3() {
+	   jj_la1_3 = new int[] {0x0,0x0,0x0,0x1f0420,0x0,0x1f0420,0x1f0420,0x0,0x1f0420,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x100000,0x0,0x0,0x200,0x1f0420,0x0,0x0,0x0,0x1f0420,0x0,0x0,0x100000,0x0,0x0,0x1000,0x0,0x0,0x1000,0x110000,0x0,0x100000,0x100000,0x0,0x19a,0x0,0x0,0x19a,0x1f0420,0x1f0420,0x1f0420,0x1f0420,0x1f0420,0x19a,0x8000,0x0,0x0,0x0,0x0,0x1f0420,0x6,0x198,0x600,0x600,0x1800,0x1800,0x1f0420,0x1f0020,0x180000,0x0,0x0,0x100000,0x0,0x60000,0x0,0x2,0x2,0x19a,0x0,0x0,0x0,0x0,0x0,0x19a,0x0,0x0,0x0,0x2,0x2,0x19a,0x0,0x0,0x1f0420,0x0,0x1f0420,0x1000,0x0,0x1f0420,0x19a,0x19a,0x0,0x0,0x0,0x200,0x1f0420,0x20,0x0,0x0,0x0,0x0,0x120000,0x800,0x120000,0x100000,0x100200,0x0,0x100000,0x6,0x198,0x0,};
+	}
   static final private JJCalls[] jj_2_rtns = new JJCalls[19];
   static private boolean jj_rescan = false;
   static private int jj_gc = 0;
 
   /** Constructor with InputStream. */
   public PrismParser(java.io.InputStream stream) {
-     this(stream, null);
+	  this(stream, null);
   }
   /** Constructor with InputStream and supplied encoding */
   public PrismParser(java.io.InputStream stream, String encoding) {
-    if (jj_initialized_once) {
-      System.out.println("ERROR: Second call to constructor of static parser.  ");
-      System.out.println("       You must either use ReInit() or set the JavaCC option STATIC to false");
-      System.out.println("       during parser generation.");
-      throw new Error();
-    }
-    jj_initialized_once = true;
-    try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
-    token_source = new PrismParserTokenManager(jj_input_stream);
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	 if (jj_initialized_once) {
+	   System.out.println("ERROR: Second call to constructor of static parser.  ");
+	   System.out.println("	   You must either use ReInit() or set the JavaCC option STATIC to false");
+	   System.out.println("	   during parser generation.");
+	   throw new Error();
+	 }
+	 jj_initialized_once = true;
+	 try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+	 token_source = new PrismParserTokenManager(jj_input_stream);
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   /** Reinitialise. */
   static public void ReInit(java.io.InputStream stream) {
-     ReInit(stream, null);
+	  ReInit(stream, null);
   }
   /** Reinitialise. */
   static public void ReInit(java.io.InputStream stream, String encoding) {
-    try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
-    token_source.ReInit(jj_input_stream);
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	 try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+	 token_source.ReInit(jj_input_stream);
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   /** Constructor. */
   public PrismParser(java.io.Reader stream) {
-    if (jj_initialized_once) {
-      System.out.println("ERROR: Second call to constructor of static parser. ");
-      System.out.println("       You must either use ReInit() or set the JavaCC option STATIC to false");
-      System.out.println("       during parser generation.");
-      throw new Error();
-    }
-    jj_initialized_once = true;
-    jj_input_stream = new SimpleCharStream(stream, 1, 1);
-    token_source = new PrismParserTokenManager(jj_input_stream);
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	 if (jj_initialized_once) {
+	   System.out.println("ERROR: Second call to constructor of static parser. ");
+	   System.out.println("	   You must either use ReInit() or set the JavaCC option STATIC to false");
+	   System.out.println("	   during parser generation.");
+	   throw new Error();
+	 }
+	 jj_initialized_once = true;
+	 jj_input_stream = new SimpleCharStream(stream, 1, 1);
+	 token_source = new PrismParserTokenManager(jj_input_stream);
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   /** Reinitialise. */
   static public void ReInit(java.io.Reader stream) {
-    jj_input_stream.ReInit(stream, 1, 1);
-    token_source.ReInit(jj_input_stream);
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	if (jj_input_stream == null) {
+	   jj_input_stream = new SimpleCharStream(stream, 1, 1);
+	} else {
+	   jj_input_stream.ReInit(stream, 1, 1);
+	}
+	if (token_source == null) {
+ token_source = new PrismParserTokenManager(jj_input_stream);
+	}
+
+	 token_source.ReInit(jj_input_stream);
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   /** Constructor with generated Token Manager. */
   public PrismParser(PrismParserTokenManager tm) {
-    if (jj_initialized_once) {
-      System.out.println("ERROR: Second call to constructor of static parser. ");
-      System.out.println("       You must either use ReInit() or set the JavaCC option STATIC to false");
-      System.out.println("       during parser generation.");
-      throw new Error();
-    }
-    jj_initialized_once = true;
-    token_source = tm;
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	 if (jj_initialized_once) {
+	   System.out.println("ERROR: Second call to constructor of static parser. ");
+	   System.out.println("	   You must either use ReInit() or set the JavaCC option STATIC to false");
+	   System.out.println("	   during parser generation.");
+	   throw new Error();
+	 }
+	 jj_initialized_once = true;
+	 token_source = tm;
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   /** Reinitialise. */
   public void ReInit(PrismParserTokenManager tm) {
-    token_source = tm;
-    token = new Token();
-    jj_ntk = -1;
-    jj_gen = 0;
-    for (int i = 0; i < 119; i++) jj_la1[i] = -1;
-    for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+	 token_source = tm;
+	 token = new Token();
+	 jj_ntk = -1;
+	 jj_gen = 0;
+	 for (int i = 0; i < 119; i++) jj_la1[i] = -1;
+	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
   static private Token jj_consume_token(int kind) throws ParseException {
-    Token oldToken;
-    if ((oldToken = token).next != null) token = token.next;
-    else token = token.next = token_source.getNextToken();
-    jj_ntk = -1;
-    if (token.kind == kind) {
-      jj_gen++;
-      if (++jj_gc > 100) {
-        jj_gc = 0;
-        for (int i = 0; i < jj_2_rtns.length; i++) {
-          JJCalls c = jj_2_rtns[i];
-          while (c != null) {
-            if (c.gen < jj_gen) c.first = null;
-            c = c.next;
-          }
-        }
-      }
-      return token;
-    }
-    token = oldToken;
-    jj_kind = kind;
-    throw generateParseException();
+	 Token oldToken;
+	 if ((oldToken = token).next != null) token = token.next;
+	 else token = token.next = token_source.getNextToken();
+	 jj_ntk = -1;
+	 if (token.kind == kind) {
+	   jj_gen++;
+	   if (++jj_gc > 100) {
+		 jj_gc = 0;
+		 for (int i = 0; i < jj_2_rtns.length; i++) {
+		   JJCalls c = jj_2_rtns[i];
+		   while (c != null) {
+			 if (c.gen < jj_gen) c.first = null;
+			 c = c.next;
+		   }
+		 }
+	   }
+	   return token;
+	 }
+	 token = oldToken;
+	 jj_kind = kind;
+	 throw generateParseException();
   }
 
   @SuppressWarnings("serial")
-  static private final class LookaheadSuccess extends java.lang.Error { }
-  static final private LookaheadSuccess jj_ls = new LookaheadSuccess();
-  static private boolean jj_scan_token(int kind) {
-    if (jj_scanpos == jj_lastpos) {
-      jj_la--;
-      if (jj_scanpos.next == null) {
-        jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
-      } else {
-        jj_lastpos = jj_scanpos = jj_scanpos.next;
-      }
-    } else {
-      jj_scanpos = jj_scanpos.next;
-    }
-    if (jj_rescan) {
-      int i = 0; Token tok = token;
-      while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
-      if (tok != null) jj_add_error_token(kind, i);
+  static private final class LookaheadSuccess extends java.lang.Error {
+    @Override
+    public Throwable fillInStackTrace() {
+      return this;
     }
-    if (jj_scanpos.kind != kind) return true;
-    if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
-    return false;
+  }
+  static private final LookaheadSuccess jj_ls = new LookaheadSuccess();
+  static private boolean jj_scan_token(int kind) {
+	 if (jj_scanpos == jj_lastpos) {
+	   jj_la--;
+	   if (jj_scanpos.next == null) {
+		 jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+	   } else {
+		 jj_lastpos = jj_scanpos = jj_scanpos.next;
+	   }
+	 } else {
+	   jj_scanpos = jj_scanpos.next;
+	 }
+	 if (jj_rescan) {
+	   int i = 0; Token tok = token;
+	   while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+	   if (tok != null) jj_add_error_token(kind, i);
+	 }
+	 if (jj_scanpos.kind != kind) return true;
+	 if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+	 return false;
   }
 
 
 /** Get the next Token. */
   static final public Token getNextToken() {
-    if (token.next != null) token = token.next;
-    else token = token.next = token_source.getNextToken();
-    jj_ntk = -1;
-    jj_gen++;
-    return token;
+	 if (token.next != null) token = token.next;
+	 else token = token.next = token_source.getNextToken();
+	 jj_ntk = -1;
+	 jj_gen++;
+	 return token;
   }
 
 /** Get the specific Token. */
   static final public Token getToken(int index) {
-    Token t = token;
-    for (int i = 0; i < index; i++) {
-      if (t.next != null) t = t.next;
-      else t = t.next = token_source.getNextToken();
-    }
-    return t;
+	 Token t = token;
+	 for (int i = 0; i < index; i++) {
+	   if (t.next != null) t = t.next;
+	   else t = t.next = token_source.getNextToken();
+	 }
+	 return t;
   }
 
   static private int jj_ntk_f() {
-    if ((jj_nt=token.next) == null)
-      return (jj_ntk = (token.next=token_source.getNextToken()).kind);
-    else
-      return (jj_ntk = jj_nt.kind);
+	 if ((jj_nt=token.next) == null)
+	   return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+	 else
+	   return (jj_ntk = jj_nt.kind);
   }
 
   static private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
@@ -7181,71 +7194,91 @@ fl.setLHS(s);
   static private int jj_endpos;
 
   static private void jj_add_error_token(int kind, int pos) {
-    if (pos >= 100) return;
-    if (pos == jj_endpos + 1) {
-      jj_lasttokens[jj_endpos++] = kind;
-    } else if (jj_endpos != 0) {
-      jj_expentry = new int[jj_endpos];
-      for (int i = 0; i < jj_endpos; i++) {
-        jj_expentry[i] = jj_lasttokens[i];
-      }
-      jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) {
-        int[] oldentry = (int[])(it.next());
-        if (oldentry.length == jj_expentry.length) {
-          for (int i = 0; i < jj_expentry.length; i++) {
-            if (oldentry[i] != jj_expentry[i]) {
-              continue jj_entries_loop;
-            }
-          }
-          jj_expentries.add(jj_expentry);
-          break jj_entries_loop;
-        }
-      }
-      if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
-    }
+	 if (pos >= 100) {
+		return;
+	 }
+
+	 if (pos == jj_endpos + 1) {
+	   jj_lasttokens[jj_endpos++] = kind;
+	 } else if (jj_endpos != 0) {
+	   jj_expentry = new int[jj_endpos];
+
+	   for (int i = 0; i < jj_endpos; i++) {
+		 jj_expentry[i] = jj_lasttokens[i];
+	   }
+
+	   for (int[] oldentry : jj_expentries) {
+		 if (oldentry.length == jj_expentry.length) {
+		   boolean isMatched = true;
+
+		   for (int i = 0; i < jj_expentry.length; i++) {
+			 if (oldentry[i] != jj_expentry[i]) {
+			   isMatched = false;
+			   break;
+			 }
+
+		   }
+		   if (isMatched) {
+			 jj_expentries.add(jj_expentry);
+			 break;
+		   }
+		 }
+	   }
+
+	   if (pos != 0) {
+		 jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+	   }
+	 }
   }
 
   /** Generate ParseException. */
   static public ParseException generateParseException() {
-    jj_expentries.clear();
-    boolean[] la1tokens = new boolean[119];
-    if (jj_kind >= 0) {
-      la1tokens[jj_kind] = true;
-      jj_kind = -1;
-    }
-    for (int i = 0; i < 119; i++) {
-      if (jj_la1[i] == jj_gen) {
-        for (int j = 0; j < 32; j++) {
-          if ((jj_la1_0[i] & (1<<j)) != 0) {
-            la1tokens[j] = true;
-          }
-          if ((jj_la1_1[i] & (1<<j)) != 0) {
-            la1tokens[32+j] = true;
-          }
-          if ((jj_la1_2[i] & (1<<j)) != 0) {
-            la1tokens[64+j] = true;
-          }
-          if ((jj_la1_3[i] & (1<<j)) != 0) {
-            la1tokens[96+j] = true;
-          }
-        }
-      }
-    }
-    for (int i = 0; i < 119; i++) {
-      if (la1tokens[i]) {
-        jj_expentry = new int[1];
-        jj_expentry[0] = i;
-        jj_expentries.add(jj_expentry);
-      }
-    }
-    jj_endpos = 0;
-    jj_rescan_token();
-    jj_add_error_token(0, 0);
-    int[][] exptokseq = new int[jj_expentries.size()][];
-    for (int i = 0; i < jj_expentries.size(); i++) {
-      exptokseq[i] = jj_expentries.get(i);
-    }
-    return new ParseException(token, exptokseq, tokenImage);
+	 jj_expentries.clear();
+	 boolean[] la1tokens = new boolean[119];
+	 if (jj_kind >= 0) {
+	   la1tokens[jj_kind] = true;
+	   jj_kind = -1;
+	 }
+	 for (int i = 0; i < 119; i++) {
+	   if (jj_la1[i] == jj_gen) {
+		 for (int j = 0; j < 32; j++) {
+		   if ((jj_la1_0[i] & (1<<j)) != 0) {
+			 la1tokens[j] = true;
+		   }
+		   if ((jj_la1_1[i] & (1<<j)) != 0) {
+			 la1tokens[32+j] = true;
+		   }
+		   if ((jj_la1_2[i] & (1<<j)) != 0) {
+			 la1tokens[64+j] = true;
+		   }
+		   if ((jj_la1_3[i] & (1<<j)) != 0) {
+			 la1tokens[96+j] = true;
+		   }
+		 }
+	   }
+	 }
+	 for (int i = 0; i < 119; i++) {
+	   if (la1tokens[i]) {
+		 jj_expentry = new int[1];
+		 jj_expentry[0] = i;
+		 jj_expentries.add(jj_expentry);
+	   }
+	 }
+	 jj_endpos = 0;
+	 jj_rescan_token();
+	 jj_add_error_token(0, 0);
+	 int[][] exptokseq = new int[jj_expentries.size()][];
+	 for (int i = 0; i < jj_expentries.size(); i++) {
+	   exptokseq[i] = jj_expentries.get(i);
+	 }
+	 return new ParseException(token, exptokseq, tokenImage);
+  }
+
+  static private boolean trace_enabled;
+
+/** Trace enabled. */
+  static final public boolean trace_enabled() {
+	 return trace_enabled;
   }
 
   /** Enable tracing. */
@@ -7257,56 +7290,61 @@ fl.setLHS(s);
   }
 
   static private void jj_rescan_token() {
-    jj_rescan = true;
-    for (int i = 0; i < 19; i++) {
-    try {
-      JJCalls p = jj_2_rtns[i];
-      do {
-        if (p.gen > jj_gen) {
-          jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
-          switch (i) {
-            case 0: jj_3_1(); break;
-            case 1: jj_3_2(); break;
-            case 2: jj_3_3(); break;
-            case 3: jj_3_4(); break;
-            case 4: jj_3_5(); break;
-            case 5: jj_3_6(); break;
-            case 6: jj_3_7(); break;
-            case 7: jj_3_8(); break;
-            case 8: jj_3_9(); break;
-            case 9: jj_3_10(); break;
-            case 10: jj_3_11(); break;
-            case 11: jj_3_12(); break;
-            case 12: jj_3_13(); break;
-            case 13: jj_3_14(); break;
-            case 14: jj_3_15(); break;
-            case 15: jj_3_16(); break;
-            case 16: jj_3_17(); break;
-            case 17: jj_3_18(); break;
-            case 18: jj_3_19(); break;
-          }
-        }
-        p = p.next;
-      } while (p != null);
-      } catch(LookaheadSuccess ls) { }
-    }
-    jj_rescan = false;
+	 jj_rescan = true;
+	 for (int i = 0; i < 19; i++) {
+	   try {
+		 JJCalls p = jj_2_rtns[i];
+
+		 do {
+		   if (p.gen > jj_gen) {
+			 jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+			 switch (i) {
+			   case 0: jj_3_1(); break;
+			   case 1: jj_3_2(); break;
+			   case 2: jj_3_3(); break;
+			   case 3: jj_3_4(); break;
+			   case 4: jj_3_5(); break;
+			   case 5: jj_3_6(); break;
+			   case 6: jj_3_7(); break;
+			   case 7: jj_3_8(); break;
+			   case 8: jj_3_9(); break;
+			   case 9: jj_3_10(); break;
+			   case 10: jj_3_11(); break;
+			   case 11: jj_3_12(); break;
+			   case 12: jj_3_13(); break;
+			   case 13: jj_3_14(); break;
+			   case 14: jj_3_15(); break;
+			   case 15: jj_3_16(); break;
+			   case 16: jj_3_17(); break;
+			   case 17: jj_3_18(); break;
+			   case 18: jj_3_19(); break;
+			 }
+		   }
+		   p = p.next;
+		 } while (p != null);
+
+		 } catch(LookaheadSuccess ls) { }
+	 }
+	 jj_rescan = false;
   }
 
   static private void jj_save(int index, int xla) {
-    JJCalls p = jj_2_rtns[index];
-    while (p.gen > jj_gen) {
-      if (p.next == null) { p = p.next = new JJCalls(); break; }
-      p = p.next;
-    }
-    p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+	 JJCalls p = jj_2_rtns[index];
+	 while (p.gen > jj_gen) {
+	   if (p.next == null) { p = p.next = new JJCalls(); break; }
+	   p = p.next;
+	 }
+
+	 p.gen = jj_gen + xla - jj_la; 
+	 p.first = token;
+	 p.arg = xla;
   }
 
   static final class JJCalls {
-    int gen;
-    Token first;
-    int arg;
-    JJCalls next;
+	 int gen;
+	 Token first;
+	 int arg;
+	 JJCalls next;
   }
 
 }
diff --git a/prism/src/parser/PrismParser.jj b/prism/src/parser/PrismParser.jj
index 50363888b2664bb8f92eb518a7a5f463d3e445ea..ddb74df0d3b17dae65ba1ba22443587a0c470915 100644
--- a/prism/src/parser/PrismParser.jj
+++ b/prism/src/parser/PrismParser.jj
@@ -1594,7 +1594,7 @@ Expression ExpressionLiteral(boolean prop, boolean pathprop) :
 	<REG_INT> {
 		try {
 			int i = Integer.parseInt(getToken(0).image);
-			ret = new ExpressionLiteral(TypeInt.getInstance(), new Integer(i));
+			ret = new ExpressionLiteral(TypeInt.getInstance(), Integer.valueOf(i));
 		} catch (NumberFormatException e) {
 			// Need to catch this because some matches for regexp REG_INT
 			// are not valid integers (e.g. too big).
@@ -1615,9 +1615,9 @@ Expression ExpressionLiteral(boolean prop, boolean pathprop) :
 			// NB: can't call generateParseException() here; it crashes
 		}}
 	|
-	<TRUE> { ret = new ExpressionLiteral(TypeBool.getInstance(), new Boolean(true)); }
+	<TRUE> { ret = new ExpressionLiteral(TypeBool.getInstance(), Boolean.valueOf(true)); }
 	|
-	<FALSE> { ret = new ExpressionLiteral(TypeBool.getInstance(), new Boolean(false)); }
+	<FALSE> { ret = new ExpressionLiteral(TypeBool.getInstance(), Boolean.valueOf(false)); }
 	)
 	{ ret.setPosition(getToken(0)); return ret; }
 }
diff --git a/prism/src/parser/PrismParserTokenManager.java b/prism/src/parser/PrismParserTokenManager.java
index c6d4311e972cc615700058f822f1574634d47573..0603e9e1b0bd00e0915b12f3faae8b9f4123e48b 100644
--- a/prism/src/parser/PrismParserTokenManager.java
+++ b/prism/src/parser/PrismParserTokenManager.java
@@ -16,7 +16,8 @@ import prism.ModelType;
 import prism.PrismLangException;
 
 /** Token Manager. */
-@SuppressWarnings("unused")public class PrismParserTokenManager implements PrismParserConstants {
+@SuppressWarnings ("unused")
+public class PrismParserTokenManager implements PrismParserConstants {
 
   /** Debug output. */
   public static  java.io.PrintStream debugStream = System.out;
@@ -1255,21 +1256,6 @@ static private int jjMoveNfa_0(int startState, int curPos)
       catch(java.io.IOException e) { return curPos; }
    }
 }
-static final int[] jjnextStates = {
-   10, 11, 12, 18, 2, 3, 5, 20, 21, 22, 13, 14, 16, 17, 
-};
-private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
-{
-   switch(hiByte)
-   {
-      case 0:
-         return ((jjbitVec2[i2] & l2) != 0L);
-      default :
-         if ((jjbitVec0[i1] & l1) != 0L)
-            return true;
-         return false;
-   }
-}
 
 /** Token literal values. */
 public static final String[] jjstrLiteralImages = {
@@ -1322,6 +1308,21 @@ static protected Token jjFillToken()
 
    return t;
 }
+static final int[] jjnextStates = {
+   10, 11, 12, 18, 2, 3, 5, 20, 21, 22, 13, 14, 16, 17, 
+};
+private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+{
+   switch(hiByte)
+   {
+      case 0:
+         return ((jjbitVec2[i2] & l2) != 0L);
+      default :
+         if ((jjbitVec0[i1] & l1) != 0L)
+            return true;
+         return false;
+   }
+}
 
 static int curLexState = 0;
 static int defaultLexState = 0;
@@ -1344,7 +1345,7 @@ public static Token getNextToken()
    {
       curChar = input_stream.BeginToken();
    }
-   catch(java.io.IOException e)
+   catch(Exception e)
    {
       jjmatchedKind = 0;
       jjmatchedPos = -1;
@@ -1409,6 +1410,31 @@ public static Token getNextToken()
   }
 }
 
+static void SkipLexicalActions(Token matchedToken)
+{
+   switch(jjmatchedKind)
+   {
+      default :
+         break;
+   }
+}
+static void MoreLexicalActions()
+{
+   jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
+   switch(jjmatchedKind)
+   {
+      default :
+         break;
+   }
+}
+static void TokenLexicalActions(Token matchedToken)
+{
+   switch(jjmatchedKind)
+   {
+      default :
+         break;
+   }
+}
 static private void jjCheckNAdd(int state)
 {
    if (jjrounds[state] != jjround)
@@ -1452,9 +1478,14 @@ static private void jjCheckNAddStates(int start, int end)
   }
 
   /** Reinitialise parser. */
+  
   static public void ReInit(SimpleCharStream stream)
   {
-    jjmatchedPos = jjnewStateCnt = 0;
+
+
+    jjmatchedPos =
+    jjnewStateCnt =
+    0;
     curLexState = defaultLexState;
     input_stream = stream;
     ReInitRounds();
@@ -1470,13 +1501,14 @@ static private void jjCheckNAddStates(int start, int end)
 
   /** Reinitialise parser. */
   static public void ReInit(SimpleCharStream stream, int lexState)
+  
   {
     ReInit(stream);
     SwitchTo(lexState);
   }
 
   /** Switch to specified lex state. */
-  static public void SwitchTo(int lexState)
+  public static void SwitchTo(int lexState)
   {
     if (lexState >= 1 || lexState < 0)
       throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
@@ -1484,10 +1516,20 @@ static private void jjCheckNAddStates(int start, int end)
       curLexState = lexState;
   }
 
+
 /** Lexer state names. */
 public static final String[] lexStateNames = {
    "DEFAULT",
 };
+
+/** Lex State array. */
+public static final int[] jjnewLexState = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
+};
 static final long[] jjtoToken = {
    0xfffffffffffffff9L, 0x7fffffffffffffL, 
 };
@@ -1496,12 +1538,17 @@ static final long[] jjtoSkip = {
 };
 static final long[] jjtoSpecial = {
    0x6L, 0x0L, 
+};
+static final long[] jjtoMore = {
+   0x0L, 0x0L, 
 };
     static protected SimpleCharStream  input_stream;
 
     static private final int[] jjrounds = new int[23];
     static private final int[] jjstateSet = new int[2 * 23];
-
-    
-    static protected char curChar;
+    private static final StringBuilder jjimage = new StringBuilder();
+    private static StringBuilder image = jjimage;
+    private static int jjimageLen;
+    private static int lengthOfMatch;
+    static protected int curChar;
 }
diff --git a/prism/src/parser/SimpleCharStream.java b/prism/src/parser/SimpleCharStream.java
index 73d4c7f4511d4bbcb5fee020622c2fd98ba07cd2..889869b61a2792fe9db9df0668694d8f023a9366 100644
--- a/prism/src/parser/SimpleCharStream.java
+++ b/prism/src/parser/SimpleCharStream.java
@@ -1,4 +1,4 @@
-/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 6.0 */
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 7.0 */
 /* JavaCCOptions:STATIC=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
 package parser;
 
@@ -30,13 +30,14 @@ public class SimpleCharStream
   static protected char[] buffer;
   static protected int maxNextCharInd = 0;
   static protected int inBuf = 0;
-  static protected int tabSize = 8;
-  static protected boolean trackLineColumn = false;
+  static protected int tabSize = 1;
+  static protected boolean trackLineColumn = true;
 
   static public void setTabSize(int i) { tabSize = i; }
   static public int getTabSize() { return tabSize; }
 
 
+
   static protected void ExpandBuff(boolean wrapAround)
   {
     char[] newbuffer = new char[bufsize + 2048];
@@ -471,8 +472,7 @@ public class SimpleCharStream
     line = bufline[j];
     column = bufcolumn[j];
   }
-
   static boolean getTrackLineColumn() { return trackLineColumn; }
-  static void setTrackLineColumn(boolean trackLineColumn) { trackLineColumn = trackLineColumn; }
+  static void setTrackLineColumn(boolean tlc) { trackLineColumn = tlc; }
 }
-/* JavaCC - OriginalChecksum=81090b92d191fc568cf66cb10dcab351 (do not edit this line) */
+/* JavaCC - OriginalChecksum=312d39869f3bfc3a64bf5e66d85fffff (do not edit this line) */
diff --git a/prism/src/parser/Token.java b/prism/src/parser/Token.java
index acc5cc30821a5afdae639a645d3927aa342e335c..49f0d85dc26b8426c5a88671a5e02817fd448237 100644
--- a/prism/src/parser/Token.java
+++ b/prism/src/parser/Token.java
@@ -1,5 +1,5 @@
-/* Generated By:JavaCC: Do not edit this line. Token.java Version 6.0 */
-/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 7.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COLUMN=true,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
 package parser;
 
 /**
@@ -97,6 +97,7 @@ public class Token implements java.io.Serializable {
   /**
    * Returns the image.
    */
+  @Override
   public String toString()
   {
     return image;
@@ -128,4 +129,4 @@ public class Token implements java.io.Serializable {
   }
 
 }
-/* JavaCC - OriginalChecksum=7d751264ae152bf5b6855a5f69a4ab67 (do not edit this line) */
+/* JavaCC - OriginalChecksum=a6e99fa366f40513e8823da999781b1f (do not edit this line) */
diff --git a/prism/src/parser/TokenMgrError.java b/prism/src/parser/TokenMgrError.java
index bc8ef8be22169e51e019c248dbe588138617261e..84cacc7804110fe836fb56e11afb8a61293376a6 100644
--- a/prism/src/parser/TokenMgrError.java
+++ b/prism/src/parser/TokenMgrError.java
@@ -1,4 +1,4 @@
-/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 6.0 */
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 7.0 */
 /* JavaCCOptions: */
 package parser;
 
@@ -20,22 +20,22 @@ public class TokenMgrError extends Error
   /**
    * Lexical error occurred.
    */
-  static final int LEXICAL_ERROR = 0;
+  public static final int LEXICAL_ERROR = 0;
 
   /**
    * An attempt was made to create a second instance of a static token manager.
    */
-  static final int STATIC_LEXER_ERROR = 1;
+  public static final int STATIC_LEXER_ERROR = 1;
 
   /**
    * Tried to change to an invalid lexical state.
    */
-  static final int INVALID_LEXICAL_STATE = 2;
+  public static final int INVALID_LEXICAL_STATE = 2;
 
   /**
    * Detected (and bailed out of) an infinite loop in the token manager.
    */
-  static final int LOOP_DETECTED = 3;
+  public static final int LOOP_DETECTED = 3;
 
   /**
    * Indicates the reason why the exception is thrown. It will have
@@ -48,13 +48,11 @@ public class TokenMgrError extends Error
    * equivalents in the given string
    */
   protected static final String addEscapes(String str) {
-    StringBuffer retval = new StringBuffer();
+    StringBuilder retval = new StringBuilder();
     char ch;
     for (int i = 0; i < str.length(); i++) {
       switch (str.charAt(i))
       {
-        case 0 :
-          continue;
         case '\b':
           retval.append("\\b");
           continue;
@@ -104,11 +102,12 @@ public class TokenMgrError extends Error
    *    curchar     : the offending character
    * Note: You can customize the lexical error message by modifying this method.
    */
-  protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+  protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar) {
+    char curChar1 = (char)curChar;
     return("Lexical error at line " +
           errorLine + ", column " +
           errorColumn + ".  Encountered: " +
-          (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+          (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar1)) + "\"") + " (" + curChar + "), ") +
           "after : \"" + addEscapes(errorAfter) + "\"");
   }
 
@@ -121,6 +120,7 @@ public class TokenMgrError extends Error
    *
    * from this method for such cases in the release version of your parser.
    */
+  @Override
   public String getMessage() {
     return super.getMessage();
   }
@@ -140,8 +140,8 @@ public class TokenMgrError extends Error
   }
 
   /** Full Constructor. */
-  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
-    this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+  public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) {
+    this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
   }
 }
-/* JavaCC - OriginalChecksum=6c4705c2bebd89492711d8a1e624d837 (do not edit this line) */
+/* JavaCC - OriginalChecksum=247c264b970b850ff3f25ab9073c801d (do not edit this line) */
diff --git a/prism/src/parser/Values.java b/prism/src/parser/Values.java
index aea591b1818b52f2bd8657027af778c67b524404..5ff528b33485f618ad5f5fcc85584bfa17ade874 100644
--- a/prism/src/parser/Values.java
+++ b/prism/src/parser/Values.java
@@ -496,7 +496,7 @@ public class Values implements Cloneable //implements Comparable
 		String s;
 		
 		if (o instanceof Double) {
-			s = PrismUtils.formatDouble((double)o);
+			s = PrismUtils.formatDouble(12, (double) o);
 		} else {
 			s = String.valueOf(o);
 		}
diff --git a/prism/src/prism/IntegerBound.java b/prism/src/prism/IntegerBound.java
index 8640bc05f9b6c24d70ce16a2333431a16377127a..1fa824262a88818a73a44f486c590357b550979c 100644
--- a/prism/src/prism/IntegerBound.java
+++ b/prism/src/prism/IntegerBound.java
@@ -49,12 +49,12 @@ public class IntegerBound
 	{
 		// normalize
 		if (lower_strict && lower != null) {
-			lowest = new Integer(lower+1);
+			lowest = Integer.valueOf(lower+1);
 		} else {
 			lowest = lower;
 		}
 		if (upper_strict && upper != null) {
-			highest = new Integer(upper-1);
+			highest = Integer.valueOf(upper-1);
 		} else {
 			highest = upper;
 		}
diff --git a/prism/src/prism/Model.java b/prism/src/prism/Model.java
index 3f909aa334b6f25fa922f6f38b59aff94bd2e867..d15f73b697fd80c9d05dc761875ce3677a5643b3 100644
--- a/prism/src/prism/Model.java
+++ b/prism/src/prism/Model.java
@@ -33,6 +33,8 @@ import jdd.*;
 import odd.*;
 import parser.*;
 
+import static prism.PrismSettings.DEFAULT_EXPORT_MODEL_PRECISION;
+
 public interface Model
 {
 	ModelType getModelType();
@@ -199,33 +201,79 @@ public interface Model
 	void findDeadlocks(boolean fix);
 	
 	void printTrans();
+
 	void printTrans01();
-	public void printTransInfo(PrismLog log);
-	public void printTransInfo(PrismLog log, boolean extra);
-	void exportToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException;
-	
+
+	void printTransInfo(PrismLog log);
+
+	void printTransInfo(PrismLog log, boolean extra);
+
+	default void exportToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	{
+		exportToFile(exportType, explicit, file, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	void exportToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException;
+
 	/**
 	 * Export (non-zero) state rewards for one reward structure of the model.
 	 * @param r Index of reward structure to export (0-indexed)
 	 * @param exportType The format in which to export
 	 * @param file File to export to (if null, print to the log instead)
 	 */
-	void exportStateRewardsToFile(int r, int exportType, File file) throws FileNotFoundException, PrismException;
-	
+	default void exportStateRewardsToFile(int r, int exportType, File file) throws FileNotFoundException, PrismException
+	{
+		exportStateRewardsToFile(r, exportType, file, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export (non-zero) state rewards for one reward structure of the model.
+	 * @param r Index of reward structure to export (0-indexed)
+	 * @param exportType The format in which to export
+	 * @param file File to export to (if null, print to the log instead)
+	 * @param precision number of significant digits >= 1
+	 */
+	void exportStateRewardsToFile(int r, int exportType, File file, int precision) throws FileNotFoundException, PrismException;
+
 	@Deprecated
-	String exportStateRewardsToFile(int exportType, File file) throws FileNotFoundException, PrismException;
-	
+	default String exportStateRewardsToFile(int exportType, File file) throws FileNotFoundException, PrismException
+	{
+		return exportStateRewardsToFile(exportType, file, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	@Deprecated
+	String exportStateRewardsToFile(int exportType, File file, int precision) throws FileNotFoundException, PrismException;
+
+	/**
+	 * Export (non-zero) transition rewards for one reward structure of the model.
+	 * @param r Index of reward structure to export (0-indexed)
+	 * @param exportType The format in which to export
+	 * @param ordered Do the entries need to be printed in order?
+	 * @param file File to export to (if null, print to the log instead)
+	 */
+	default void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file) throws FileNotFoundException, PrismException
+	{
+		exportTransRewardsToFile(r, exportType, ordered, file, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
 	/**
 	 * Export (non-zero) transition rewards for one reward structure of the model.
 	 * @param r Index of reward structure to export (0-indexed)
 	 * @param exportType The format in which to export
 	 * @param ordered Do the entries need to be printed in order?
 	 * @param file File to export to (if null, print to the log instead)
+	 * @param precision number of significant digits >= 1
 	 */
-	void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file) throws FileNotFoundException, PrismException;
+	void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file, int precision) throws FileNotFoundException, PrismException;
+
+	@Deprecated
+	default String exportTransRewardsToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	{
+		return exportTransRewardsToFile(exportType, explicit, file, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
 
 	@Deprecated
-	String exportTransRewardsToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException;
+	String exportTransRewardsToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException;
 
 	void exportStates(int exportType, PrismLog log);
 
diff --git a/prism/src/prism/NondetModel.java b/prism/src/prism/NondetModel.java
index 46715248d33cb5570f85d6724631d090c24f59b9..5674b2bc40c457ee4fbce0b6d3a209d1f541dcfa 100644
--- a/prism/src/prism/NondetModel.java
+++ b/prism/src/prism/NondetModel.java
@@ -411,28 +411,29 @@ public class NondetModel extends ProbModel
 
 	// export transition matrix to a file
 
-	public void exportToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	@Override
+	public void exportToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		if (!explicit) {
 			// can only do explicit (sparse matrix based) export for mdps
 		} else {
 			PrismSparse.ExportMDP(trans, transActions, getSynchs(), getTransSymbol(), allDDRowVars, allDDColVars, allDDNondetVars, odd, exportType,
-					(file != null) ? file.getPath() : null);
+					(file != null) ? file.getPath() : null, precision);
 		}
 	}
 
 	@Override
-	public void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file) throws FileNotFoundException, PrismException
+	public void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		if (!ordered) {
 			// can only do explicit (sparse matrix based) export for mdps
 		} else {
-			PrismSparse.ExportSubMDP(trans, transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, allDDNondetVars, odd, exportType, (file == null) ? null : file.getPath());
+			PrismSparse.ExportSubMDP(trans, transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, allDDNondetVars, odd, exportType, (file == null) ? null : file.getPath(), precision);
 		}
 	}
 
 	@Deprecated
-	public String exportTransRewardsToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	public String exportTransRewardsToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		// export transition rewards matrix to a file
 		// returns string containing files used if there were more than 1, null otherwise
@@ -450,7 +451,7 @@ public class NondetModel extends ProbModel
 			if (!explicit) {
 				// can only do explicit (sparse matrix based) export for mdps
 			} else {
-				PrismSparse.ExportSubMDP(trans, transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, allDDNondetVars, odd, exportType, filename);
+				PrismSparse.ExportSubMDP(trans, transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, allDDNondetVars, odd, exportType, filename, precision);
 			}
 		}
 		return (allFilenames.length() > 0) ? allFilenames : null;
diff --git a/prism/src/prism/NondetModelChecker.java b/prism/src/prism/NondetModelChecker.java
index 1ef4fd4966a183e7c4799954d6be9b8465e66b98..468519c39aff7ed189017c36f547a80f4b989855 100644
--- a/prism/src/prism/NondetModelChecker.java
+++ b/prism/src/prism/NondetModelChecker.java
@@ -43,7 +43,6 @@ import jdd.JDD;
 import jdd.JDDNode;
 import jdd.JDDVars;
 import mtbdd.PrismMTBDD;
-import odd.ODDUtils;
 import parser.BooleanUtils;
 import parser.ast.Coalition;
 import parser.ast.Expression;
@@ -60,7 +59,6 @@ import parser.type.TypeBool;
 import parser.type.TypeDouble;
 import parser.type.TypePathBool;
 import parser.type.TypePathDouble;
-import prism.Accuracy.AccuracyLevel;
 import prism.LTLModelChecker.LTLProduct;
 import sparse.PrismSparse;
 import strat.MDStrategyIV;
@@ -879,8 +877,9 @@ public class NondetModelChecker extends NonProbModelChecker
 		// Output product, if required
 		if (prism.getExportProductTrans()) {
 			try {
+				int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 				mainLog.println("\nExporting product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"...");
-				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()));
+				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()), precision);
 			} catch (FileNotFoundException e) {
 				mainLog.printWarning("Could not export product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"");
 			}
@@ -1281,8 +1280,9 @@ public class NondetModelChecker extends NonProbModelChecker
 		// Output product, if required
 		if (prism.getExportProductTrans()) {
 			try {
+				int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 				mainLog.println("\nExporting product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"...");
-				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()));
+				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()), precision);
 			} catch (FileNotFoundException e) {
 				mainLog.printWarning("Could not export product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"");
 			}
@@ -1555,8 +1555,9 @@ public class NondetModelChecker extends NonProbModelChecker
 		// Output product, if required
 		if (prism.getExportProductTrans()) {
 			try {
+				int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 				mainLog.println("\nExporting product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"...");
-				modelProduct.getProductModel().exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()));
+				modelProduct.getProductModel().exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()), precision);
 			} catch (FileNotFoundException e) {
 				mainLog.printWarning("Could not export product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"");
 			}
@@ -2022,8 +2023,9 @@ public class NondetModelChecker extends NonProbModelChecker
 
 					if (false) {
 						try {
-							model.exportToFile(Prism.EXPORT_DOT, true, new File("model.dot"));
-							transform.getTransformedModel().exportToFile(Prism.EXPORT_DOT, true, new File("quotient.dot"));
+							int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
+							model.exportToFile(Prism.EXPORT_DOT, true, new File("model.dot"), precision);
+							transform.getTransformedModel().exportToFile(Prism.EXPORT_DOT, true, new File("quotient.dot"), precision);
 						} catch (FileNotFoundException e) {
 						}
 					}
diff --git a/prism/src/prism/Preprocessor.java b/prism/src/prism/Preprocessor.java
index 1d30dcff1dde04cf0da10380b9c4b94d6e0d5745..8bffc3edd97b582140805eff7ebc2fa5e5ea46b2 100644
--- a/prism/src/prism/Preprocessor.java
+++ b/prism/src/prism/Preprocessor.java
@@ -334,11 +334,11 @@ public class Preprocessor
 		varTypes.add(TypeInt.getInstance());
 		varScopes.add(stack.size());
 		if (expr != null) {
-			values.addValue(name, new Integer(expr.evaluateInt(null, values)));
+			values.addValue(name, Integer.valueOf(expr.evaluateInt(null, values)));
 		} else {
 			if (params.length <= paramCounter + 1)
 				throw new PrismException("No value provided for undefined preprocessor constant \"" + name + "\"");
-			values.addValue(name, new Integer(Integer.parseInt(params[++paramCounter])));
+			values.addValue(name, Integer.valueOf(Integer.parseInt(params[++paramCounter])));
 		}
 		// move to next statement
 		pc++;
@@ -370,7 +370,7 @@ public class Preprocessor
 		varNames.add(fl.getLHS());
 		varTypes.add(TypeInt.getInstance());
 		varScopes.add(stack.size());
-		values.addValue(fl.getLHS(), new Integer(fl.getFrom().evaluateInt(null, values)));
+		values.addValue(fl.getLHS(), Integer.valueOf(fl.getFrom().evaluateInt(null, values)));
 		// if for loop trivially not satisfied, set output flag to false
 		if (fl.getFrom().evaluateInt(null, values) > fl.getTo().evaluateInt(null, values)) {
 			outputEnabled = false;
@@ -429,7 +429,7 @@ public class Preprocessor
 				// if loop is not finished...
 				if (i <= fl.getTo().evaluateInt(null, values)) {
 					// update value of loop counter
-					values.setValue(fl.getLHS(), new Integer(i));
+					values.setValue(fl.getLHS(), Integer.valueOf(i));
 					// add "between" character to text
 					output += fl.getBetween();
 					// go back to start of loop
diff --git a/prism/src/prism/Prism.java b/prism/src/prism/Prism.java
index f9335c9061993df8143c635f0ee13ab8f85d619d..52473a943426a923f48adc4eb61d873d3ef9fce8 100644
--- a/prism/src/prism/Prism.java
+++ b/prism/src/prism/Prism.java
@@ -831,7 +831,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 	{
 		return settings.getInteger(PrismSettings.PRISM_GRID_RESOLUTION);
 	}
-	
+
 	public boolean getVerbose()
 	{
 		return settings.getBoolean(PrismSettings.PRISM_VERBOSE);
@@ -1217,184 +1217,6 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 	// Utility methods
 	//------------------------------------------------------------------------------
 
-	/**
-	 * Compare two version numbers of PRISM (strings).
-	 * Example ordering: { "1", "2.0", "2.1.alpha", "2.1.alpha.r5555", "2.1.alpha.r5557", "2.1.beta", "2.1.beta4", "2.1", "2.1.dev", "2.1.dev.r6666", "2.1.dev1", "2.1.dev2", "2.1.2", "2.9", "3", "3.4"};
-	 * Returns: 1 if v1&gt;v2, -1 if v1&lt;v2, 0 if v1=v2
-	 */
-	public static int compareVersions(String v1, String v2)
-	{
-		String ss1[], ss2[], tmp[];
-		int i, n, x;
-		double s1 = 0, s2 = 0;
-		boolean s1num, s2num;
-
-		// Exactly equal
-		if (v1.equals(v2))
-			return 0;
-		// Otherwise split into sections
-		ss1 = v1.split("\\.");
-		ss2 = v2.split("\\.");
-		// Pad if one is shorter
-		n = Math.max(ss1.length, ss2.length);
-		if (ss1.length < n) {
-			tmp = new String[n];
-			for (i = 0; i < ss1.length; i++)
-				tmp[i] = ss1[i];
-			for (i = ss1.length; i < n; i++)
-				tmp[i] = "";
-			ss1 = tmp;
-		}
-		if (ss2.length < n) {
-			tmp = new String[n];
-			for (i = 0; i < ss2.length; i++)
-				tmp[i] = ss2[i];
-			for (i = ss2.length; i < n; i++)
-				tmp[i] = "";
-			ss2 = tmp;
-		}
-		// Loop through sections of string
-		for (i = 0; i < n; i++) {
-			// 2.1.alpha < 2.1, etc.
-			// 2.1.alpha < 2.1.alpha2 < 2.1.alpha3, etc.
-			// so replace alphax with -10000+x
-			if (ss1[i].matches("alpha.*")) {
-				try {
-					if (ss1[i].length() == 5)
-						x = 0;
-					else
-						x = Integer.parseInt(ss1[i].substring(5));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss1[i] = "" + (-10000 + x);
-			}
-			if (ss2[i].matches("alpha.*")) {
-				try {
-					if (ss2[i].length() == 5)
-						x = 0;
-					else
-						x = Integer.parseInt(ss2[i].substring(5));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss2[i] = "" + (-10000 + x);
-			}
-			// 2.1.beta < 2.1, etc.
-			// 2.1.beta < 2.1.beta2 < 2.1.beta3, etc.
-			// so replace betax with -100+x
-			if (ss1[i].matches("beta.*")) {
-				try {
-					if (ss1[i].length() == 4)
-						x = 0;
-					else
-						x = Integer.parseInt(ss1[i].substring(4));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss1[i] = "" + (-100 + x);
-			}
-			if (ss2[i].matches("beta.*")) {
-				try {
-					if (ss2[i].length() == 4)
-						x = 0;
-					else
-						x = Integer.parseInt(ss2[i].substring(4));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss2[i] = "" + (-100 + x);
-			}
-			// 2 < 2.1, etc.
-			// so treat 2 as 2.0
-			if (ss1[i].equals(""))
-				ss1[i] = "0";
-			if (ss2[i].equals(""))
-				ss2[i] = "0";
-			// 2.1 < 2.1.dev, etc.
-			// 2.1.dev < 2.1.dev2 < 2.1.dev3, etc.
-			// so replace devx with 0.5+x/1000
-			if (ss1[i].matches("dev.*")) {
-				try {
-					if (ss1[i].length() == 3)
-						x = 0;
-					else
-						x = Integer.parseInt(ss1[i].substring(3));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss1[i] = "" + (0.5 + x / 1000.0);
-			}
-			if (ss2[i].matches("dev.*")) {
-				try {
-					if (ss2[i].length() == 3)
-						x = 0;
-					else
-						x = Integer.parseInt(ss2[i].substring(3));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss2[i] = "" + (0.5 + x / 1000.0);
-			}
-			// replace rx (e.g. as in 4.0.alpha.r5555) with x
-			if (ss1[i].matches("r.*")) {
-				try {
-					x = Integer.parseInt(ss1[i].substring(1));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss1[i] = "" + x;
-			}
-			if (ss2[i].matches("r.*")) {
-				try {
-					x = Integer.parseInt(ss2[i].substring(1));
-				} catch (NumberFormatException e) {
-					x = 0;
-				}
-				ss2[i] = "" + x;
-			}
-			// See if strings are integers
-			try {
-				s1num = true;
-				s1 = Double.parseDouble(ss1[i]);
-			} catch (NumberFormatException e) {
-				s1num = false;
-			}
-			try {
-				s2num = true;
-				s2 = Double.parseDouble(ss2[i]);
-			} catch (NumberFormatException e) {
-				s2num = false;
-			}
-			if (s1num && s2num) {
-				if (s1 < s2)
-					return -1;
-				if (s1 > s2)
-					return 1;
-				if (s1 == s2)
-					continue;
-			}
-		}
-
-		return 0;
-	}
-
-	/*// Simple test harness for compareVersions
-	public static void main(String[] args)
-	{
-		 String v[] =  { "1", "2.0", "2.1.alpha", "2.1.alpha.r5555", "2.1.alpha.r5557", "2.1.beta", "2.1.beta4", "2.1", "2.1.dev", "2.1.dev.r6666", "2.1.dev1", "2.1.dev2", "2.1.2", "2.9", "3", "3.4"};
-		 for (int i = 0; i < v.length; i++) {
-			 for (int j = 0; j < v.length; j++) {
-				 int d = compareVersions(v[i], v[j]);
-				 System.out.print(d == 1 ? ">" : d==0 ? "=" : d==-1 ? "<" : "?");
-				 if (d != compareVersions(""+i, ""+j))
-					 System.out.print("ERR(" + v[i] + "," + v[j] + ")");
-					 
-			 }
-			 System.out.println();
-		 }
-	}*/
-
 	/**
 	 * Get access to the list of all PRISM language keywords.
 	 */
@@ -2524,21 +2346,22 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 		mainLog.println(getDestinationStringForFile(file));
 
 		// do export
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		if (!getExplicit()) {
-			currentModel.exportToFile(exportType, ordered, file);
+			currentModel.exportToFile(exportType, ordered, file, precision);
 		} else {
 			PrismLog tmpLog = getPrismLogForFile(file);
 			switch (exportType) {
 			case Prism.EXPORT_PLAIN:
-				currentModelExpl.exportToPrismExplicitTra(tmpLog);
+				currentModelExpl.exportToPrismExplicitTra(tmpLog, precision);
 				break;
 			case Prism.EXPORT_MATLAB:
 				throw new PrismNotSupportedException("Export not yet supported");
 			case Prism.EXPORT_DOT:
-				currentModelExpl.exportToDotFile(tmpLog);
+				currentModelExpl.exportToDotFile(tmpLog, precision);
 				break;
 			case Prism.EXPORT_DOT_STATES:
-				currentModelExpl.exportToDotFile(tmpLog, null, true);
+				currentModelExpl.exportToDotFile(tmpLog, null, true, precision);
 				break;
 			case Prism.EXPORT_MRMC:
 			case Prism.EXPORT_ROWS:
@@ -2592,6 +2415,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 		mainLog.println(getDestinationStringForFile(file));
 
 		// Do export, writing to multiple files if necessary
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		List <String> files = new ArrayList<>();
 		for (int r = 0; r < numRewardStructs; r++) {
 			String filename = (file != null) ? file.getPath() : null;
@@ -2601,12 +2425,12 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 			}
 			File fileToUse = (filename == null) ? null : new File(filename);
 			if (!getExplicit()) {
-				currentModel.exportStateRewardsToFile(r, exportType, fileToUse);
+				currentModel.exportStateRewardsToFile(r, exportType, fileToUse, precision);
 			} else {
 				PrismLog out = getPrismLogForFile(fileToUse);
 				explicit.StateModelChecker mcExpl = createModelCheckerExplicit(null);
 				try {
-					((explicit.ProbModelChecker) mcExpl).exportStateRewardsToFile(currentModelExpl, r, exportType, out);
+					((explicit.ProbModelChecker) mcExpl).exportStateRewardsToFile(currentModelExpl, r, exportType, out, precision);
 				} catch (PrismNotSupportedException e) {
 					mainLog.println("\nReward export failed: " + e.getMessage());
 				}
@@ -2668,6 +2492,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 		mainLog.println(getDestinationStringForFile(file));
 
 		// Do export, writing to multiple files if necessary
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		List <String> files = new ArrayList<>();
 		for (int r = 0; r < numRewardStructs; r++) {
 			String filename = (file != null) ? file.getPath() : null;
@@ -2677,7 +2502,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 			}
 			File fileToUse = (filename == null) ? null : new File(filename);
 			if (!getExplicit()) {
-				currentModel.exportTransRewardsToFile(r, exportType, ordered, fileToUse);
+				currentModel.exportTransRewardsToFile(r, exportType, ordered, fileToUse, precision);
 			} else {
 				// Not implemented yet
 			}
@@ -3584,6 +3409,7 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 		mainLog.println(getDestinationStringForFile(file));
 
 		// Export to file (or use main log)
+		int precision = settings.getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 		tmpLog = getPrismLogForFile(file);
 		switch (exportType) {
 		case ACTIONS:
@@ -3593,10 +3419,10 @@ public class Prism extends PrismComponent implements PrismSettingsListener
 			strat.exportIndices(tmpLog);
 			break;
 		case INDUCED_MODEL:
-			strat.exportInducedModel(tmpLog);
+			strat.exportInducedModel(tmpLog, precision);
 			break;
 		case DOT_FILE:
-			strat.exportDotFile(tmpLog);
+			strat.exportDotFile(tmpLog, precision);
 			break;
 		}
 		if (file != null)
diff --git a/prism/src/prism/PrismCL.java b/prism/src/prism/PrismCL.java
index c81b4c98d6788c5826fc84ac5b5ef864d2008095..d9511f40ce953e9e3e3a72d5a3ded35d9e9be83d 100644
--- a/prism/src/prism/PrismCL.java
+++ b/prism/src/prism/PrismCL.java
@@ -1487,6 +1487,7 @@ public class PrismCL implements PrismModelListener
 						errorAndExit("No file/options specified for -" + sw + " switch");
 					}
 				}
+				// process -exportmodelprecision in PrismSettings
 				// export transition matrix to file
 				else if (sw.equals("exporttrans")) {
 					if (i < args.length - 1) {
@@ -2521,6 +2522,7 @@ public class PrismCL implements PrismModelListener
 		mainLog.println("-exportresults <file[:options]>  Export the results of model checking to a file");
 		mainLog.println("-exportvector <file>  .......... Export results of model checking for all states to a file");
 		mainLog.println("-exportmodel <files[:options]> . Export the built model to file(s)");
+		mainLog.println("-exportmodelprecision <n>....... Export probabilities/rewards with n significant decimal places");
 		mainLog.println("-exporttrans <file> ............ Export the transition matrix to a file");
 		mainLog.println("-exportstaterewards <file> ..... Export the state rewards vector to a file");
 		mainLog.println("-exporttransrewards <file> ..... Export the transition rewards matrix to a file");
diff --git a/prism/src/prism/PrismNative.cc b/prism/src/prism/PrismNative.cc
index c90df83a19744767f20df08aea45e9469b2b90a6..a1281cfc7f231cf1d215d463d03519dbb0b7b7e9 100644
--- a/prism/src/prism/PrismNative.cc
+++ b/prism/src/prism/PrismNative.cc
@@ -44,6 +44,7 @@ EXPORT double lin_eq_method_param;
 EXPORT int term_crit;
 EXPORT double term_crit_param;
 EXPORT int max_iters;
+EXPORT int export_model_precision;
 // use "compact modified" sparse matrix storage?
 EXPORT bool compact;
 // sparse bits info
@@ -134,6 +135,13 @@ JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetMaxIters(JNIEnv *env, jclas
 
 //------------------------------------------------------------------------------
 
+JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetExportModelPrecision(JNIEnv *env, jclass cls, jint prec)
+{
+	export_model_precision = prec;
+}
+
+//------------------------------------------------------------------------------
+
 JNIEXPORT void JNICALL Java_prism_PrismNative_PN_1SetSBMaxMem(JNIEnv *env, jclass cls, jint sbmm)
 {
 	sb_max_mem = sbmm;
diff --git a/prism/src/prism/PrismNative.java b/prism/src/prism/PrismNative.java
index e96683486570de3d4b540669ee7b48289ba32e08..eaf9723b72e58623ae0968f3b231e9ff3ede841f 100644
--- a/prism/src/prism/PrismNative.java
+++ b/prism/src/prism/PrismNative.java
@@ -125,6 +125,14 @@ public class PrismNative
 		PN_SetMaxIters(i);
 	}
 
+	private static native void PN_SetExportModelPrecision(int prec);
+	public static void setExportModelPrecision(int prec)
+	{
+		if (prec < 1)
+			throw new IllegalArgumentException("Precision has to be >= 1; got " + prec);
+		PN_SetExportModelPrecision(prec);
+	}
+
 	private static native void PN_SetSBMaxMem(int i);
 	public static void setSBMaxMem(int i)
 	{
diff --git a/prism/src/prism/PrismSettings.java b/prism/src/prism/PrismSettings.java
index 815dcf0a9bd4e0cf88d60c7bcdf254adba34b97c..c7dbb15071986be4a16261f1e12c7897d1420676 100644
--- a/prism/src/prism/PrismSettings.java
+++ b/prism/src/prism/PrismSettings.java
@@ -34,6 +34,7 @@ import java.awt.*;
 
 import javax.swing.*;
 
+import common.iterable.Range;
 import explicit.QuantAbstractRefine;
 
 import java.util.regex.*;
@@ -53,7 +54,7 @@ public class PrismSettings implements Observer
 	public static final Font DEFAULT_FONT = new Font("monospaced", Font.PLAIN, 12);
 	public static final FontColorPair DEFAULT_FONT_COLOUR = new FontColorPair(new Font("monospaced", Font.PLAIN, 12), Color.black);
 	public static final File DEFAULT_FILE = null;
-	
+
 	//Type Constants
 	public static final String STRING_TYPE = "s";
 	public static final String INTEGER_TYPE = "i";
@@ -66,7 +67,11 @@ public class PrismSettings implements Observer
 	public static final String CHOICE_TYPE = "ch";
 	public static final String FONT_COLOUR_TYPE = "fct";
 	public static final String FILE_TYPE = "fi";
-	
+
+	// Constraint constants
+	public static final Range RANGE_EXPORT_DOUBLE_PRECISION = Range.closed(1, 17);
+	public static final int DEFAULT_EXPORT_MODEL_PRECISION = 17;
+
 	//Property Constant Keys
 	//======================
 	
@@ -96,7 +101,8 @@ public class PrismSettings implements Observer
 	public static final	String PRISM_MAX_ITERS						= "prism.maxIters";//"prism.maxIterations";
 	public static final String PRISM_EXPORT_ITERATIONS				= "prism.exportIterations";
 	public static final	String PRISM_GRID_RESOLUTION				= "prism.gridResolution";
-	
+	public static final String PRISM_EXPORT_MODEL_PRECISION         = "prism.exportmodelprecision";
+
 	public static final	String PRISM_CUDD_MAX_MEM					= "prism.cuddMaxMem";
 	public static final	String PRISM_CUDD_EPSILON					= "prism.cuddEpsilon";
 	public static final	String PRISM_DD_EXTRA_STATE_VARS				= "prism.ddExtraStateVars";
@@ -263,7 +269,7 @@ public class PrismSettings implements Observer
 																			"Which engine (hybrid, sparse, MTBDD, explicit) should be used for model checking." },
 			{ CHOICE_TYPE,		PRISM_HEURISTIC,						"Heuristic mode",							"4.5",			"None",																		"None,Speed,Memory",																		
 																			"Which heuristic mode to use for picking engines/settings (none, speed, memory)." },
-			{ BOOLEAN_TYPE,		PRISM_EXACT_ENABLED,					"Do exact model checking",			"4.2.1",			new Boolean(false),															"",
+			{ BOOLEAN_TYPE,		PRISM_EXACT_ENABLED,					"Do exact model checking",			"4.2.1",			Boolean.valueOf(false),															"",
 																			"Perform exact model checking." },
 																			
 			{ CHOICE_TYPE,		PRISM_PTA_METHOD,						"PTA model checking method",			"3.3",			"Stochastic games",																	"Digital clocks,Stochastic games,Backwards reachability",																
@@ -275,7 +281,7 @@ public class PrismSettings implements Observer
 			// NUMERICAL SOLUTION OPTIONS:
 			{ CHOICE_TYPE,		PRISM_LIN_EQ_METHOD,					"Linear equations method",				"2.1",			"Jacobi",																	"Power,Jacobi,Gauss-Seidel,Backwards Gauss-Seidel,Pseudo-Gauss-Seidel,Backwards Pseudo-Gauss-Seidel,JOR,SOR,Backwards SOR,Pseudo-SOR,Backwards Pseudo-SOR",
 																			"Which iterative method to use when solving linear equation systems." },
-			{ DOUBLE_TYPE,		PRISM_LIN_EQ_METHOD_PARAM,				"Over-relaxation parameter",			"2.1",			new Double(0.9),															"",																							
+			{ DOUBLE_TYPE,		PRISM_LIN_EQ_METHOD_PARAM,				"Over-relaxation parameter",			"2.1",			Double.valueOf(0.9),															"",																							
 																			"Over-relaxation parameter for iterative numerical methods such as JOR/SOR." },
 			{ BOOLEAN_TYPE,		PRISM_TOPOLOGICAL_VI,				"Use topological value iteration",				"4.3.1",		false,																		"",
 																			"Use topological value iteration in iterative numerical methods."},
@@ -291,32 +297,34 @@ public class PrismSettings implements Observer
 																			"Which method to use when solving multi-objective queries on Markov decision processes." },
 			{ CHOICE_TYPE,		PRISM_TERM_CRIT,						"Termination criteria",					"2.1",			"Relative",																	"Absolute,Relative",																		
 																			"Criteria to use for checking termination of iterative numerical methods." },
-			{ DOUBLE_TYPE,		PRISM_TERM_CRIT_PARAM,					"Termination epsilon",					"2.1",			new Double(1.0E-6),															"0.0,",																						
+			{ DOUBLE_TYPE,		PRISM_TERM_CRIT_PARAM,					"Termination epsilon",					"2.1",			Double.valueOf(1.0E-6),															"0.0,",																						
 																			"Epsilon value to use for checking termination of iterative numerical methods." },
-			{ INTEGER_TYPE,		PRISM_MAX_ITERS,						"Termination max. iterations",			"2.1",			new Integer(10000),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MAX_ITERS,						"Termination max. iterations",			"2.1",			Integer.valueOf(10000),															"0,",																						
 																			"Maximum number of iterations to perform if iterative methods do not converge." },
 			{ BOOLEAN_TYPE,		PRISM_EXPORT_ITERATIONS,				"Export iterations (debug/visualisation)",			"4.3.1",			false,														"",
 																			"Export solution vectors for iteration algorithms to iterations.html"},
-			{ INTEGER_TYPE,		PRISM_GRID_RESOLUTION,					"Fixed grid resolution",			    "4.5",			new Integer(10),															"1,",																						
+			{ INTEGER_TYPE,		PRISM_GRID_RESOLUTION,					"Fixed grid resolution",			    "4.5",			Integer.valueOf(10),															"1,",																						
 																			"The resolution for the fixed grid approximation algorithm for POMDPs." },
+			{ INTEGER_TYPE,		PRISM_EXPORT_MODEL_PRECISION,			"Precision of model export",			"4.7dev",			17,																		RANGE_EXPORT_DOUBLE_PRECISION.min() + "-" + RANGE_EXPORT_DOUBLE_PRECISION.max(),
+																			"Export probabilities/rewards with n significant decimal places"},
 			// MODEL CHECKING OPTIONS:
-			{ BOOLEAN_TYPE,		PRISM_PRECOMPUTATION,					"Use precomputation",					"2.1",			new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_PRECOMPUTATION,					"Use precomputation",					"2.1",			Boolean.valueOf(true),															"",																							
 																			"Whether to use model checking precomputation algorithms (Prob0, Prob1, etc.), where optional." },
-			{ BOOLEAN_TYPE,		PRISM_PROB0,							"Use Prob0 precomputation",				"4.0.2",		new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_PROB0,							"Use Prob0 precomputation",				"4.0.2",		Boolean.valueOf(true),															"",																							
 																			"Whether to use model checking precomputation algorithm Prob0 (if precomputation enabled)." },
-			{ BOOLEAN_TYPE,		PRISM_PROB1,							"Use Prob1 precomputation",				"4.0.2",		new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_PROB1,							"Use Prob1 precomputation",				"4.0.2",		Boolean.valueOf(true),															"",																							
 																			"Whether to use model checking precomputation algorithm Prob1 (if precomputation enabled)." },
-			{ BOOLEAN_TYPE,		PRISM_PRE_REL,							"Use predecessor relation",		"4.2.1",		new Boolean(true),											"",
+			{ BOOLEAN_TYPE,		PRISM_PRE_REL,							"Use predecessor relation",		"4.2.1",		Boolean.valueOf(true),											"",
 																			"Whether to use a pre-computed predecessor relation in several algorithms." },
-			{ BOOLEAN_TYPE,		PRISM_FAIRNESS,							"Use fairness",							"2.1",			new Boolean(false),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_FAIRNESS,							"Use fairness",							"2.1",			Boolean.valueOf(false),															"",																							
 																			"Constrain to fair adversaries when model checking MDPs." },
-			{ BOOLEAN_TYPE,		PRISM_FIX_DEADLOCKS,					"Automatically fix deadlocks",			"4.0.3",		new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_FIX_DEADLOCKS,					"Automatically fix deadlocks",			"4.0.3",		Boolean.valueOf(true),															"",																							
 																			"Automatically fix deadlocks, where necessary, when constructing probabilistic models." },
-			{ BOOLEAN_TYPE,		PRISM_DO_PROB_CHECKS,					"Do probability/rate checks",			"2.1",			new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_DO_PROB_CHECKS,					"Do probability/rate checks",			"2.1",			Boolean.valueOf(true),															"",																							
 																			"Perform sanity checks on model probabilities/rates when constructing probabilistic models." },
-			{ DOUBLE_TYPE,		PRISM_SUM_ROUND_OFF,					"Probability sum threshold",					"2.1",			new Double(1.0E-5),													"0.0,",
+			{ DOUBLE_TYPE,		PRISM_SUM_ROUND_OFF,					"Probability sum threshold",					"2.1",			Double.valueOf(1.0E-5),													"0.0,",
 																			"Round-off threshold for places where doubles are summed and compared to integers (e.g. checking that probabilities sum to 1 in an update)." },							
-			{ BOOLEAN_TYPE,		PRISM_DO_SS_DETECTION,					"Use steady-state detection",			"2.1",			new Boolean(true),															"0,",																						
+			{ BOOLEAN_TYPE,		PRISM_DO_SS_DETECTION,					"Use steady-state detection",			"2.1",			Boolean.valueOf(true),															"0,",																						
 																			"Use steady-state detection during CTMC transient probability computation." },
 			{ CHOICE_TYPE,		PRISM_SCC_METHOD,						"SCC decomposition method",				"3.2",			"Lockstep",																	"Xie-Beerel,Lockstep,SCC-Find",																
 																			"Which algorithm to use for (symbolic) decomposition of a graph into strongly connected components (SCCs)." },
@@ -324,68 +332,68 @@ public class PrismSettings implements Observer
 																			"Parameters for symmetry reduction (format: \"i j\" where i and j are the number of modules before and after the symmetric ones; empty string means symmetry reduction disabled)." },
 			{ STRING_TYPE,		PRISM_AR_OPTIONS,						"Abstraction refinement options",		"3.3",			"",																	"",																
 																			"Various options passed to the asbtraction-refinement engine (e.g. for PTA model checking)." },
-			{ BOOLEAN_TYPE,		PRISM_PATH_VIA_AUTOMATA,				"All path formulas via automata",			"4.2.1",			new Boolean(false),									"",
+			{ BOOLEAN_TYPE,		PRISM_PATH_VIA_AUTOMATA,				"All path formulas via automata",			"4.2.1",			Boolean.valueOf(false),									"",
 																			"Handle all path formulas via automata constructions." },
-			{ BOOLEAN_TYPE,		PRISM_NO_DA_SIMPLIFY,				"Do not simplify deterministic automata",			"4.3",			new Boolean(false),									"",
+			{ BOOLEAN_TYPE,		PRISM_NO_DA_SIMPLIFY,				"Do not simplify deterministic automata",			"4.3",			Boolean.valueOf(false),									"",
 																			"Do not attempt to simplify deterministic automata, acceptance conditions (for debugging)." },
 
 			// MULTI-OBJECTIVE MODEL CHECKING OPTIONS:
-			{ INTEGER_TYPE,		PRISM_MULTI_MAX_POINTS,					"Max. multi-objective corner points",			"4.0.3",			new Integer(50),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MAX_POINTS,					"Max. multi-objective corner points",			"4.0.3",			Integer.valueOf(50),															"0,",																						
 																			"Maximum number of corner points to explore if (value iteration based) multi-objective model checking does not converge." },
-			{ DOUBLE_TYPE,		PRISM_PARETO_EPSILON,					"Pareto approximation threshold",			"4.0.3",			new Double(1.0E-2),															"0.0,",																						
+			{ DOUBLE_TYPE,		PRISM_PARETO_EPSILON,					"Pareto approximation threshold",			"4.0.3",			Double.valueOf(1.0E-2),															"0.0,",																						
 																			"Determines to what precision the Pareto curve will be approximated." },
 			{ STRING_TYPE,		PRISM_EXPORT_PARETO_FILENAME,			"Pareto curve export filename",			"4.0.3",			"",															"0,",																						
 																			"If non-empty, any Pareto curve generated will be exported to this file." },
 			// MULTI-OBJECTIVE SYNTHESIS:
-			{ BOOLEAN_TYPE,		PRISM_MULTI_GAUSS_SEIDEL,							"Use Gauss-Seidel value iteration for solving multi-objective SGs.",				"4.0.3",		new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_MULTI_GAUSS_SEIDEL,							"Use Gauss-Seidel value iteration for solving multi-objective SGs.",				"4.0.3",		Boolean.valueOf(true),															"",																							
 																			"Use Gauss-Seidel value iteration for solving multi-objective SGs. Only used for cumulative total rewards (Pareto set computation and strategy synthesis), and for strategy synthesis of average and ratio rewards." },
-			{ INTEGER_TYPE,		PRISM_MULTI_MAX_C_ITER,					"Max. iterations for conjunctive query",			"4.0.3",			new Integer(500),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MAX_C_ITER,					"Max. iterations for conjunctive query",			"4.0.3",			Integer.valueOf(500),															"0,",																						
 																			"Maximum number of iterations performed to solve conjunctive queries using value iteration. The same value is used for computing the conjunctions involved in mixed queries." },
-			{ INTEGER_TYPE,		PRISM_MULTI_MAX_R_ITER,					"Max. iterations for ratio rewards",			"4.0.3",			new Integer(500),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MAX_R_ITER,					"Max. iterations for ratio rewards",			"4.0.3",			Integer.valueOf(500),															"0,",																						
 																			"Maximum number of iterations performed to compute the Pareto sets for ratio rewards using value iteration." },
-			{ INTEGER_TYPE,		PRISM_MULTI_MAX_D_ITER,					"Max. iterations for disjunctive query",			"4.0.3",			new Integer(500),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MAX_D_ITER,					"Max. iterations for disjunctive query",			"4.0.3",			Integer.valueOf(500),															"0,",																						
 																			"Maximum number of iterations performed to solve disjunctive queries using value iteration. The same value is used for computing the disjunctions involved in mixed queries." },
-			{ INTEGER_TYPE,		PRISM_MULTI_D_ITER_OFFSET,					"Disjunctive query iteration offset",			"4.0.3",			new Integer(1),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_D_ITER_OFFSET,					"Disjunctive query iteration offset",			"4.0.3",			Integer.valueOf(1),															"0,",																						
 																			"Start the disjunctive iteration at this iteration." },
-			{ INTEGER_TYPE,		PRISM_MULTI_MIN_M,					"Minimum Box Size (M)",			"4.0.3",			new Integer(2),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MIN_M,					"Minimum Box Size (M)",			"4.0.3",			Integer.valueOf(2),															"0,",																						
 																			"Set the minimum size for the box for multi-objective mean-payoff objectives." },
-			{ INTEGER_TYPE,		PRISM_MULTI_MAX_M,					"Maximum Box Size (M)",			"4.0.3",			new Integer(16),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_MAX_M,					"Maximum Box Size (M)",			"4.0.3",			Integer.valueOf(16),															"0,",																						
 																			"Set the maximum size for the box for multi-objective mean-payoff objectives." },
-			{ BOOLEAN_TYPE,		PRISM_MULTI_ROUNDING,							"Use rounding in multi-objective engine",				"4.0.3",		new Boolean(false),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_MULTI_ROUNDING,							"Use rounding in multi-objective engine",				"4.0.3",		Boolean.valueOf(false),															"",																							
 																			"Whether to use rounding in the multi-objective games engine" },
-			{ INTEGER_TYPE,		PRISM_MULTI_BASELINE_ACCURACY,					"Baseline accuracy for conjunctive query value iteration",			"4.0.3",			new Integer(200),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_MULTI_BASELINE_ACCURACY,					"Baseline accuracy for conjunctive query value iteration",			"4.0.3",			Integer.valueOf(200),															"0,",																						
 																			"Value iteration starts computing points rounded to the maximum reward in each dimension divided by the baseline accuracy, and this accuracy is increased by the increase factor after every iteration." },
-			{ DOUBLE_TYPE,		PRISM_MULTI_INCREASE_FACTOR,					"Increase factor for conjunctive query value iteration",			"4.0.3",			new Double(1.01),															"0,",																						
+			{ DOUBLE_TYPE,		PRISM_MULTI_INCREASE_FACTOR,					"Increase factor for conjunctive query value iteration",			"4.0.3",			Double.valueOf(1.01),															"0,",																						
 																			"Accuracy of conjunctive query value iteration is increased by the increase factor after every iteration." },
 			// CSG ZERO-SUM LP SCALE FACTOR
-			{ DOUBLE_TYPE,		PRISM_ZS_LP_SCALE_FACTOR, 					"Scale factor for LPs",			"4.5", 				new Double(1.0), 			"1,",
+			{ DOUBLE_TYPE,		PRISM_ZS_LP_SCALE_FACTOR, 					"Scale factor for LPs",			"4.5", 				Double.valueOf(1.0), 			"1,",
 																			"Scale factor used when building linear programs for solving matrix games"},
 
 			// OUTPUT OPTIONS:
-			{ BOOLEAN_TYPE,		PRISM_VERBOSE,							"Verbose output",						"2.1",		new Boolean(false),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_VERBOSE,							"Verbose output",						"2.1",		Boolean.valueOf(false),															"",																							
 																			"Display verbose output to log." },
-			{ BOOLEAN_TYPE,		PRISM_EXTRA_DD_INFO,					"Extra MTBDD information",				"3.1.1",		new Boolean(false),															"0,",																						
+			{ BOOLEAN_TYPE,		PRISM_EXTRA_DD_INFO,					"Extra MTBDD information",				"3.1.1",		Boolean.valueOf(false),															"0,",																						
 																			"Display extra information about (MT)BDDs used during and after model construction." },
-			{ BOOLEAN_TYPE,		PRISM_EXTRA_REACH_INFO,					"Extra reachability information",		"3.1.1",		new Boolean(false),															"0,",																						
+			{ BOOLEAN_TYPE,		PRISM_EXTRA_REACH_INFO,					"Extra reachability information",		"3.1.1",		Boolean.valueOf(false),															"0,",																						
 																			"Display extra information about progress of reachability during model construction." },
 			// SPARSE/HYBRID/MTBDD OPTIONS:
-			{ BOOLEAN_TYPE,		PRISM_COMPACT,							"Use compact schemes",					"2.1",			new Boolean(true),															"",																							
+			{ BOOLEAN_TYPE,		PRISM_COMPACT,							"Use compact schemes",					"2.1",			Boolean.valueOf(true),															"",																							
 																			"Use additional optimisations for compressing sparse matrices and vectors with repeated values." },
-			{ INTEGER_TYPE,		PRISM_NUM_SB_LEVELS,					"Hybrid sparse levels",					"2.1",			new Integer(-1),															"-1,",																						
+			{ INTEGER_TYPE,		PRISM_NUM_SB_LEVELS,					"Hybrid sparse levels",					"2.1",			Integer.valueOf(-1),															"-1,",																						
 																			"Number of MTBDD levels ascended when adding sparse matrices to hybrid engine data structures (-1 means use default)." },
-			{ INTEGER_TYPE,		PRISM_SB_MAX_MEM,						"Hybrid sparse memory (KB)",			"2.1",			new Integer(1024),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_SB_MAX_MEM,						"Hybrid sparse memory (KB)",			"2.1",			Integer.valueOf(1024),															"0,",																						
 																			"Maximum memory usage when adding sparse matrices to hybrid engine data structures (KB)." },
-			{ INTEGER_TYPE,		PRISM_NUM_SOR_LEVELS,					"Hybrid GS levels",						"2.1",			new Integer(-1),															"-1,",																						
+			{ INTEGER_TYPE,		PRISM_NUM_SOR_LEVELS,					"Hybrid GS levels",						"2.1",			Integer.valueOf(-1),															"-1,",																						
 																			"Number of MTBDD levels descended for hybrid engine data structures block division with GS/SOR." },
-			{ INTEGER_TYPE,		PRISM_SOR_MAX_MEM,						"Hybrid GS memory (KB)",				"2.1",			new Integer(1024),															"0,",																						
+			{ INTEGER_TYPE,		PRISM_SOR_MAX_MEM,						"Hybrid GS memory (KB)",				"2.1",			Integer.valueOf(1024),															"0,",																						
 																			"Maximum memory usage for hybrid engine data structures block division with GS/SOR (KB)." },
 			{ STRING_TYPE,		PRISM_CUDD_MAX_MEM,						"CUDD max. memory",				"4.2.1",			new String("1g"),														"",																						
 																			"Maximum memory available to CUDD (underlying BDD/MTBDD library), e.g. 125k, 50m, 4g. Note: Restart PRISM after changing this." },
-			{ DOUBLE_TYPE,		PRISM_CUDD_EPSILON,						"CUDD epsilon",							"2.1",			new Double(1.0E-15),														"0.0,",																						
+			{ DOUBLE_TYPE,		PRISM_CUDD_EPSILON,						"CUDD epsilon",							"2.1",			Double.valueOf(1.0E-15),														"0.0,",																						
 																			"Epsilon value used by CUDD (underlying BDD/MTBDD library) for terminal cache comparisons." },
-			{ INTEGER_TYPE,		PRISM_DD_EXTRA_STATE_VARS,				"Extra DD state var allocation",		"4.3.1",			new Integer(20),														"",
+			{ INTEGER_TYPE,		PRISM_DD_EXTRA_STATE_VARS,				"Extra DD state var allocation",		"4.3.1",			Integer.valueOf(20),														"",
 																			"Number of extra DD state variables preallocated for use in model transformation." },
-			{ INTEGER_TYPE,		PRISM_DD_EXTRA_ACTION_VARS,				"Extra DD action var allocation",		"4.3.1",			new Integer(20),														"",
+			{ INTEGER_TYPE,		PRISM_DD_EXTRA_ACTION_VARS,				"Extra DD action var allocation",		"4.3.1",			Integer.valueOf(20),														"",
 																			"Number of extra DD action variables preallocated for use in model transformation." },
 
 
@@ -394,9 +402,9 @@ public class PrismSettings implements Observer
 																			"Type of adversary to generate and export during MDP model checking" },
 			{ STRING_TYPE,		PRISM_EXPORT_ADV_FILENAME,				"Adversary export filename",			"3.3",			"adv.tra",																	"",															
 																			"Name of file for MDP adversary export (if enabled)" },
-			{ BOOLEAN_TYPE,		PRISM_GENERATE_STRATEGY,				"Generate Strategy",			"4.1",			new Boolean(false),																	"",															
+			{ BOOLEAN_TYPE,		PRISM_GENERATE_STRATEGY,				"Generate Strategy",			"4.1",			Boolean.valueOf(false),																	"",															
 																			"Generate an optimal strategy when model checking an MDP/game" },
-			{ BOOLEAN_TYPE,		PRISM_IMPLEMENT_STRATEGY,				"Implements Strategy",			"4.1",			new Boolean(false),																	"",
+			{ BOOLEAN_TYPE,		PRISM_IMPLEMENT_STRATEGY,				"Implements Strategy",			"4.1",			Boolean.valueOf(false),																	"",
 
 																			"Use composition verification/synythesis methods." },																		
 			// LTL2DA TOOLS
@@ -407,11 +415,11 @@ public class PrismSettings implements Observer
 																			"The syntax for LTL formulas passed to the external LTL->DA tool."},
 
 			// DEBUG / SANITY CHECK OPTIONS:
-			{ BOOLEAN_TYPE,		PRISM_JDD_SANITY_CHECKS,					"Do BDD sanity checks",			"4.3.1",			new Boolean(false),		"",
+			{ BOOLEAN_TYPE,		PRISM_JDD_SANITY_CHECKS,					"Do BDD sanity checks",			"4.3.1",			Boolean.valueOf(false),		"",
 																			"Perform internal sanity checks during computations (can cause significant slow-down)." },
 
 			// PARAMETRIC MODEL CHECKING
-			{ BOOLEAN_TYPE,		PRISM_PARAM_ENABLED,					"Do parametric model checking",			"4.1",			new Boolean(false),															"",
+			{ BOOLEAN_TYPE,		PRISM_PARAM_ENABLED,					"Do parametric model checking",			"4.1",			Boolean.valueOf(false),															"",
 																			"Perform parametric model checking." },
 			{ STRING_TYPE,		PRISM_PARAM_PRECISION,					"Parametric model checking precision",	"4.1",			"5/100",																	"",
 																			"Maximal volume of area to remain undecided in each step when performing parametric model checking." },
@@ -423,47 +431,47 @@ public class PrismSettings implements Observer
 																			"Type of representation for functions used during parametric model checking." },
 			{ CHOICE_TYPE,		PRISM_PARAM_ELIM_ORDER,					"Parametric model checking state elimination order",			"4.1",			"Backward",																		"Arbitrary,Forward,Forward-reversed,Backward,Backward-reversed,Random",
 																			"Order in which states are eliminated during unbounded parametric model checking analysis." },
-			{ INTEGER_TYPE,		PRISM_PARAM_RANDOM_POINTS,				"Parametric model checking random evaluations",		"4.1",			new Integer(5),																"",
+			{ INTEGER_TYPE,		PRISM_PARAM_RANDOM_POINTS,				"Parametric model checking random evaluations",		"4.1",			Integer.valueOf(5),																"",
 																			"Number of random points to evaluate per region to increase chance of correctness during parametric model checking." },
-			{ BOOLEAN_TYPE,		PRISM_PARAM_SUBSUME_REGIONS,			"Parametric model checking region subsumption",				"4.1",			new Boolean(true),															"",
+			{ BOOLEAN_TYPE,		PRISM_PARAM_SUBSUME_REGIONS,			"Parametric model checking region subsumption",				"4.1",			Boolean.valueOf(true),															"",
 																			"Subsume adjacent regions during parametric model checking." },
-			{ DOUBLE_TYPE,		PRISM_PARAM_DAG_MAX_ERROR,				"Parametric model checking max. DAG error",	"4.1",			new Double(1E-100),															"",
+			{ DOUBLE_TYPE,		PRISM_PARAM_DAG_MAX_ERROR,				"Parametric model checking max. DAG error",	"4.1",			Double.valueOf(1E-100),															"",
 																			"Maximal error probability (i.e. maximum probability of of a wrong result) in DAG function representation used for parametric model checking." },
 			
 			// FAST ADAPTIVE UNIFORMISATION																
-			{ DOUBLE_TYPE,      PRISM_FAU_EPSILON,						"FAU epsilon",		 					"4.1",   	 	new Double(1E-6),     													"",
+			{ DOUBLE_TYPE,      PRISM_FAU_EPSILON,						"FAU epsilon",		 					"4.1",   	 	Double.valueOf(1E-6),     													"",
 																			"For fast adaptive uniformisation (FAU), decides how much probability may be lost due to truncation of birth process." },
-			{ DOUBLE_TYPE,      PRISM_FAU_DELTA,						"FAU cut off delta", 					"4.1",   	 	new Double(1E-12),     													"",
+			{ DOUBLE_TYPE,      PRISM_FAU_DELTA,						"FAU cut off delta", 					"4.1",   	 	Double.valueOf(1E-12),     													"",
 																			"For fast adaptive uniformisation (FAU), states whose probability is below this value will be removed." },
-			{ INTEGER_TYPE,     PRISM_FAU_ARRAYTHRESHOLD,				"FAU array threshold", 					"4.1",   	 	new Integer(100),    	 													"",
+			{ INTEGER_TYPE,     PRISM_FAU_ARRAYTHRESHOLD,				"FAU array threshold", 					"4.1",   	 	Integer.valueOf(100),    	 													"",
 																			"For fast adaptive uniformisation (FAU), after this number of iterations without changes to the state space, storage is switched to a faster, fixed-size data structure." },
-			{ INTEGER_TYPE,     PRISM_FAU_INTERVALS,					"FAU time intervals",					"4.1",   	 	new Integer(1),     														"",
+			{ INTEGER_TYPE,     PRISM_FAU_INTERVALS,					"FAU time intervals",					"4.1",   	 	Integer.valueOf(1),     														"",
 																			"For fast adaptive uniformisation (FAU), the time period is split into this number of of intervals." },
-			{ DOUBLE_TYPE,      PRISM_FAU_INITIVAL,						"FAU initial time interval",			"4.1",   	 	new Double(1.0),     														"",	
+			{ DOUBLE_TYPE,      PRISM_FAU_INITIVAL,						"FAU initial time interval",			"4.1",   	 	Double.valueOf(1.0),     														"",	
 																			"For fast adaptive uniformisation (FAU), the length of initial time interval to analyse." },
 		},
 		{
-			{ INTEGER_TYPE,		SIMULATOR_DEFAULT_NUM_SAMPLES,			"Default number of samples",			"4.0",		new Integer(1000),			"1,",
+			{ INTEGER_TYPE,		SIMULATOR_DEFAULT_NUM_SAMPLES,			"Default number of samples",			"4.0",		Integer.valueOf(1000),			"1,",
 																			"Default number of samples when using approximate (simulation-based) model checking (CI/ACI/APMC methods)." },
-			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_CONFIDENCE,			"Default confidence parameter",			"4.0",		new Double(0.01),			"0,1",
+			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_CONFIDENCE,			"Default confidence parameter",			"4.0",		Double.valueOf(0.01),			"0,1",
 																			"Default value for the 'confidence' parameter when using approximate (simulation-based) model checking (CI/ACI/APMC/SPRT methods). For CI/ACI, this means that the corresponding 'confidence level' is 100 x (1 - confidence)%; for APMC, this is the probability of the 'approximation' being exceeded; for SPRT, this is the acceptable probability for type I/II errors." },
-			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_WIDTH,				"Default width of confidence interval",	"4.0",		new Double(0.05),			"0,",
+			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_WIDTH,				"Default width of confidence interval",	"4.0",		Double.valueOf(0.05),			"0,",
 																			"Default (half-)width of the confidence interval when using approximate (simulation-based) model checking (CI/ACI/SPRT methods). For SPRT, this refers to the 'indifference' parameter." },
-			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_APPROX,				"Default approximation parameter",		"4.0",		new Double(0.05),			"0,",
+			{ DOUBLE_TYPE,		SIMULATOR_DEFAULT_APPROX,				"Default approximation parameter",		"4.0",		Double.valueOf(0.05),			"0,",
 																			"Default value for the 'approximation' parameter when using approximate (simulation-based) model checking (APMC method)." },
-			{ LONG_TYPE,		SIMULATOR_DEFAULT_MAX_PATH,				"Default maximum path length",			"2.1",		new Long(10000),			"1,",
+			{ LONG_TYPE,		SIMULATOR_DEFAULT_MAX_PATH,				"Default maximum path length",			"2.1",		Long.valueOf(10000),			"1,",
 																			"Default maximum path length when using approximate (simulation-based) model checking." },
-			{ BOOLEAN_TYPE,		SIMULATOR_DECIDE,						"Decide S^2=0 or not automatically",	"4.0",		new	Boolean(true),			"",
+			{ BOOLEAN_TYPE,		SIMULATOR_DECIDE,						"Decide S^2=0 or not automatically",	"4.0",		Boolean.valueOf(true),			"",
 																			"Let PRISM choose whether, after a certain number of iterations, the standard error is null or not." },
-			{ INTEGER_TYPE,		SIMULATOR_ITERATIONS_TO_DECIDE,			"Number of iterations to decide",		"4.0",		new	Integer(10000),			"1,",
+			{ INTEGER_TYPE,		SIMULATOR_ITERATIONS_TO_DECIDE,			"Number of iterations to decide",		"4.0",		Integer.valueOf(10000),			"1,",
 																			"Number of iterations to decide whether the standard error is null or not." },
-			{ DOUBLE_TYPE,		SIMULATOR_MAX_REWARD,					"Maximum reward",						"4.0",		new	Double(1000.0),			"1,",
+			{ DOUBLE_TYPE,		SIMULATOR_MAX_REWARD,					"Maximum reward",						"4.0",		Double.valueOf(1000.0),			"1,",
 																			"Maximum reward for CI/ACI methods. It helps these methods in displaying the progress in case of rewards computation." },
-			{ BOOLEAN_TYPE,		SIMULATOR_SIMULTANEOUS,					"Check properties simultaneously",		"2.1",		new Boolean(true),			"",
+			{ BOOLEAN_TYPE,		SIMULATOR_SIMULTANEOUS,					"Check properties simultaneously",		"2.1",		Boolean.valueOf(true),			"",
 																			"Check multiple properties simultaneously over the same set of execution paths (simulator only)." },
 			{ CHOICE_TYPE,		SIMULATOR_FIELD_CHOICE,					"Values used in dialog",				"2.1",		"Last used values",			"Last used values,Always use defaults",
 																			"How to choose values for the simulation dialog: remember previously used values or revert to the defaults each time." },
-			{ BOOLEAN_TYPE,		SIMULATOR_NEW_PATH_ASK_VIEW,			"Ask for view configuration",			"2.1",		new Boolean(false),			"",
+			{ BOOLEAN_TYPE,		SIMULATOR_NEW_PATH_ASK_VIEW,			"Ask for view configuration",			"2.1",		Boolean.valueOf(false),			"",
 																			"Display dialog with display options when creating a new simulation path." },
 			{ CHOICE_TYPE,		SIMULATOR_RENDER_ALL_VALUES,			"Path render style",					"3.2",		"Render all values",		"Render changes,Render all values",
 																			"Display style for paths in the simulator user interface: only show variable values when they change, or show all values regardless." },
@@ -471,11 +479,11 @@ public class PrismSettings implements Observer
 																			"File specifying the network profile used by the distributed PRISM simulator." }
 		},
 		{
-		    { BOOLEAN_TYPE,		MODEL_AUTO_PARSE,						"Auto parse",							"2.1",			new Boolean(true),															"",																							"Parse PRISM models automatically as they are loaded/edited in the text editor." },
-			{ BOOLEAN_TYPE,		MODEL_AUTO_MANUAL,						"Manual parse for large models",		"2.1",			new Boolean(true),															"",																							"Disable automatic model parsing when loading large PRISM models." },
-			{ INTEGER_TYPE,		MODEL_PARSE_DELAY,						"Parse delay (ms)",						"2.1",			new Integer(1000),															"0,",																						"Time delay (after typing has finished) before an automatic re-parse of the model is performed." },
+			{ BOOLEAN_TYPE,		MODEL_AUTO_PARSE,						"Auto parse",							"2.1",			Boolean.valueOf(true),															"",																							"Parse PRISM models automatically as they are loaded/edited in the text editor." },
+			{ BOOLEAN_TYPE,		MODEL_AUTO_MANUAL,						"Manual parse for large models",		"2.1",			Boolean.valueOf(true),															"",																							"Disable automatic model parsing when loading large PRISM models." },
+			{ INTEGER_TYPE,		MODEL_PARSE_DELAY,						"Parse delay (ms)",						"2.1",			Integer.valueOf(1000),															"0,",																						"Time delay (after typing has finished) before an automatic re-parse of the model is performed." },
 			{ FONT_COLOUR_TYPE,	MODEL_PRISM_EDITOR_FONT,				"PRISM editor font",					"2.1",			new FontColorPair(new Font("monospaced", Font.PLAIN, 12), Color.black),		"",																							"Font used in the PRISM model text editor." },
-			{ BOOLEAN_TYPE,		MODEL_SHOW_LINE_NUMBERS,				"PRISM editor line numbers",            "3.2",    new Boolean(true),															"",																							"Enable or disable line numbers in the PRISM model text editor" },
+			{ BOOLEAN_TYPE,		MODEL_SHOW_LINE_NUMBERS,				"PRISM editor line numbers",            "3.2",    Boolean.valueOf(true),															"",																							"Enable or disable line numbers in the PRISM model text editor" },
 			{ COLOUR_TYPE,		MODEL_PRISM_EDITOR_BG_COLOUR,			"PRISM editor background",				"2.1",			new Color(255,255,255),														"",																							"Background colour for the PRISM model text editor." },
 			{ COLOUR_TYPE,		MODEL_PRISM_EDITOR_NUMERIC_COLOUR,		"PRISM editor numeric colour",			"2.1",			new Color(0,0,255),															"",																							"Syntax highlighting colour for numerical values in the PRISM model text editor." },
 			{ CHOICE_TYPE,		MODEL_PRISM_EDITOR_NUMERIC_STYLE,		"PRISM editor numeric style",			"2.1",			"Plain",																	"Plain,Italic,Bold,Bold Italic",															"Syntax highlighting style for numerical values in the PRISM model text editor." },
@@ -494,16 +502,16 @@ public class PrismSettings implements Observer
 			{ FONT_COLOUR_TYPE,	PROPERTIES_FONT,						"Display font",							"2.1",			new FontColorPair(new Font("monospaced", Font.PLAIN, 12), Color.black),		"",																							"Font used for the properties list." },
 			{ COLOUR_TYPE,		PROPERTIES_WARNING_COLOUR,				"Warning colour",						"2.1",			new Color(255,130,130),														"",																							"Colour used to indicate that a property is invalid." },
 			{ CHOICE_TYPE,		PROPERTIES_ADDITION_STRATEGY,			"Property addition strategy",			"2.1",			"Warn when invalid",														"Warn when invalid,Do not allow invalid",													"How to deal with properties that are invalid." },
-			{ BOOLEAN_TYPE,		PROPERTIES_CLEAR_LIST_ON_LOAD,			"Clear list when load model",			"2.1",			new Boolean(true),															"",																							"Clear the properties list whenever a new model is loaded." }
+			{ BOOLEAN_TYPE,		PROPERTIES_CLEAR_LIST_ON_LOAD,			"Clear list when load model",			"2.1",			Boolean.valueOf(true),															"",																							"Clear the properties list whenever a new model is loaded." }
 		},
 		{
 			{ FONT_COLOUR_TYPE,	LOG_FONT,								"Display font",							"2.1",			new FontColorPair(new Font("monospaced", Font.PLAIN, 12), Color.black),		"",																							"Font used for the log display." },
 			{ COLOUR_TYPE,		LOG_BG_COLOUR,							"Background colour",					"2.1",			new Color(255,255,255),														"",																							"Background colour for the log display." },
-			{ INTEGER_TYPE,		LOG_BUFFER_LENGTH,						"Buffer length",						"2.1",			new Integer(10000),															"1,",																						"Length of the buffer for the log display." },
-			{ BOOLEAN_TYPE,		LOG_MULTI_C_PARETO,			"Log Pareto sets for conjunctions",			"4.0.3",			new Boolean(false),															"",																							"Log the Pareto sets for conjunctions." },
-			{ BOOLEAN_TYPE,		LOG_MULTI_D_PARETO,			"Log Pareto sets for disjunctions",			"4.0.3",			new Boolean(false),															"",																							"Log the Pareto sets for disjunctions." },
-			{ BOOLEAN_TYPE,		LOG_MULTI_R_PARETO,			"Log Pareto sets for ratio objectives",			"4.0.3",			new Boolean(false),															"",																							"Log the Pareto sets for ratio objectives. Only applies when computing the Pareto sets, not for verification or strategy computation." },
-			{ BOOLEAN_TYPE,		LOG_MULTI_STRATEGY,			"Log Strategy Construction",    			"4.0.3",			new Boolean(false),															"",																							"Log details during strategy construction." }
+			{ INTEGER_TYPE,		LOG_BUFFER_LENGTH,						"Buffer length",						"2.1",			Integer.valueOf(10000),															"1,",																						"Length of the buffer for the log display." },
+			{ BOOLEAN_TYPE,		LOG_MULTI_C_PARETO,			"Log Pareto sets for conjunctions",			"4.0.3",			Boolean.valueOf(false),															"",																							"Log the Pareto sets for conjunctions." },
+			{ BOOLEAN_TYPE,		LOG_MULTI_D_PARETO,			"Log Pareto sets for disjunctions",			"4.0.3",			Boolean.valueOf(false),															"",																							"Log the Pareto sets for disjunctions." },
+			{ BOOLEAN_TYPE,		LOG_MULTI_R_PARETO,			"Log Pareto sets for ratio objectives",			"4.0.3",			Boolean.valueOf(false),															"",																							"Log the Pareto sets for ratio objectives. Only applies when computing the Pareto sets, not for verification or strategy computation." },
+			{ BOOLEAN_TYPE,		LOG_MULTI_STRATEGY,			"Log Strategy Construction",    			"4.0.3",			Boolean.valueOf(false),															"",																							"Log details during strategy construction." }
 
 		}
 	};
@@ -851,7 +859,7 @@ public class PrismSettings implements Observer
 			// Do we need to resave the file?
 			// (i.e. is the version of the saved settings (a) old? (b) unparseable?)
 			if (version == null) version = "0";
-			if (Prism.compareVersions(version, Prism.getVersion()) == -1) resaveNeeded = true;
+			if (PrismUtils.compareVersions(version, Prism.getVersion()) == -1) resaveNeeded = true;
 			
 			// Read whole file
 			reader = new BufferedReader(new FileReader(file));
@@ -879,7 +887,7 @@ public class PrismSettings implements Observer
 							{
 								// If the version of the settings file is not newer than the "version" of the setting,
 								// and we are re-saving the file, overwrite the setting with the default value 
-								if (resaveNeeded && Prism.compareVersions(version, set.getVersion()) <= 0) continue;
+								if (resaveNeeded && PrismUtils.compareVersions(version, set.getVersion()) <= 0) continue;
 								try
 								{
 									Object valObj = set.parseStringValue(value);
@@ -924,7 +932,7 @@ public class PrismSettings implements Observer
 						{
 							// If the version of the settings file is not newer than the "version" of the setting,
 							// and we are re-saving the file, overwrite the setting with the default value 
-							if (resaveNeeded && Prism.compareVersions(version, set.getVersion()) <= 0) continue;
+							if (resaveNeeded && PrismUtils.compareVersions(version, set.getVersion()) <= 0) continue;
 							try
 							{
 								Object valObj = set.parseStringValue(multiline.toString() + line);
@@ -1284,7 +1292,22 @@ public class PrismSettings implements Observer
 				throw new PrismException("No value specified for -" + sw + " switch");
 			}
 		}
-		
+		// export probabilities/rewards with up to n significant decimal places
+		else if (sw.equals("exportmodelprecision")) {
+			if (i < args.length - 1) {
+				try {
+					int precision = Integer.parseInt(args[++i]);
+					if (!RANGE_EXPORT_DOUBLE_PRECISION.contains(precision))
+						throw new NumberFormatException("");
+					set(PRISM_EXPORT_MODEL_PRECISION, precision);
+				} catch (NumberFormatException e) {
+					throw new PrismException("Invalid value for -" + sw + " switch");
+				}
+			} else {
+				throw new PrismException("No value specified for -" + sw + " switch");
+			}
+		}
+
 		// MODEL CHECKING OPTIONS:
 		
 		// Precomputation algs off
diff --git a/prism/src/prism/PrismUtils.java b/prism/src/prism/PrismUtils.java
index 3e4d5fca95df602e0e7520cf065ac79937953fab..c225a0e6a661ad0c2bfc192da690ff343d9eb748 100644
--- a/prism/src/prism/PrismUtils.java
+++ b/prism/src/prism/PrismUtils.java
@@ -548,41 +548,34 @@ public class PrismUtils
 	private static DecimalFormat formatterDouble2dp = new DecimalFormat("#0.00", DecimalFormatSymbols.getInstance(Locale.UK));
 
 	/**
-	 * Format a double, as would be done by printf's %.12g
+	 * Format a double, as would be done by printf's %.17g.
+	 * Preserving full double precision requires 17 = ceil(log(2^(52+1))) + 1 decimal places,
+	 * since the mantissa has 52+1 bits and one additional place is needed to tell close values apart.
 	 */
 	public static String formatDouble(double d)
 	{
-		// Use UK locale to avoid . being changed to , in some countries.
-		// To match C's printf, we have to tweak the Java version,
-		// strip trailing zeros after the .
-		String result = String.format(Locale.UK, "%.12g", d);
-		// if there are only zeros after the . (e.g., .000000), strip them including the . 
-		result = result.replaceFirst("\\.0+(e|$)", "$1");
-		// handle .xxxx0000
-		// we first match .xxx until there are only zeros before the end (or e)
-		// as we match reluctantly (using the *?), all trailing zeros are captured
-		// by the 0+ part
-		result = result.replaceFirst("(\\.[0-9]*?)0+(e|$)", "$1$2");
-		return result;
+		return formatDouble(17, d);
 	}
 
 	/**
 	 * Format a double, as would be done by printf's %.(prec)g
+	 * @param prec precision (significant digits) >= 1
 	 */
 	public static String formatDouble(int prec, double d)
 	{
-		// Use UK locale to avoid . being changed to , in some countries.
+		if (prec < 1)
+			throw new IllegalArgumentException("Precision has to be >= 1; got " + prec);
+		// Use no locale to avoid . being changed to , in some countries.
 		// To match C's printf, we have to tweak the Java version,
 		// strip trailing zeros after the .
-		String result = String.format(Locale.UK, "%." + prec + "g", d);
-		// if there are only zeros after the . (e.g., .000000), strip them including the . 
+		String result = String.format((Locale)null, "%." + prec + "g", d);
+		// if there are only zeros after the . (e.g., .000000), strip them including the .
 		result = result.replaceFirst("\\.0+(e|$)", "$1");
 		// handle .xxxx0000
 		// we first match .xxx until there are only zeros before the end (or e)
 		// as we match reluctantly (using the *?), all trailing zeros are captured
 		// by the 0+ part
-		result = result.replaceFirst("(\\.[0-9]*?)0+(e|$)", "$1$2");
-		return result;
+		return result.replaceFirst("(\\.[0-9]*?)0+(e|$)", "$1$2");
 	}
 
 	/**
@@ -781,6 +774,168 @@ public class PrismUtils
 			throw new PrismException("File \"" + filename + "\" could not opened for output");
 		}
 	}
+	
+	/**
+	 * Compare two version numbers of PRISM (strings).
+	 * Example ordering: { "1", "2.0", "2.1.alpha", "2.1.alpha.r5555", "2.1.alpha.r5557", "2.1.beta", "2.1.beta4", "2.1", "2.1.dev", "2.1.dev.r6666", "2.1.dev1", "2.1.dev2", "2.1.2", "2.9", "3", "3.4"};
+	 * Returns: 1 if v1&gt;v2, -1 if v1&lt;v2, 0 if v1=v2
+	 */
+	public static int compareVersions(String v1, String v2)
+	{
+		String ss1[], ss2[], tmp[];
+		int i, n, x;
+		double s1 = 0, s2 = 0;
+		boolean s1num, s2num;
+
+		// Exactly equal
+		if (v1.equals(v2))
+			return 0;
+		// Otherwise split into sections
+		ss1 = v1.split("\\.");
+		ss2 = v2.split("\\.");
+		// Pad if one is shorter
+		n = Math.max(ss1.length, ss2.length);
+		if (ss1.length < n) {
+			tmp = new String[n];
+			for (i = 0; i < ss1.length; i++)
+				tmp[i] = ss1[i];
+			for (i = ss1.length; i < n; i++)
+				tmp[i] = "";
+			ss1 = tmp;
+		}
+		if (ss2.length < n) {
+			tmp = new String[n];
+			for (i = 0; i < ss2.length; i++)
+				tmp[i] = ss2[i];
+			for (i = ss2.length; i < n; i++)
+				tmp[i] = "";
+			ss2 = tmp;
+		}
+		// Loop through sections of string
+		for (i = 0; i < n; i++) {
+			// 2.1.alpha < 2.1, etc.
+			// 2.1.alpha < 2.1.alpha2 < 2.1.alpha3, etc.
+			// so replace alphax with -10000+x
+			if (ss1[i].matches("alpha.*")) {
+				try {
+					if (ss1[i].length() == 5)
+						x = 0;
+					else
+						x = Integer.parseInt(ss1[i].substring(5));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss1[i] = "" + (-10000 + x);
+			}
+			if (ss2[i].matches("alpha.*")) {
+				try {
+					if (ss2[i].length() == 5)
+						x = 0;
+					else
+						x = Integer.parseInt(ss2[i].substring(5));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss2[i] = "" + (-10000 + x);
+			}
+			// 2.1.beta < 2.1, etc.
+			// 2.1.beta < 2.1.beta2 < 2.1.beta3, etc.
+			// so replace betax with -100+x
+			if (ss1[i].matches("beta.*")) {
+				try {
+					if (ss1[i].length() == 4)
+						x = 0;
+					else
+						x = Integer.parseInt(ss1[i].substring(4));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss1[i] = "" + (-100 + x);
+			}
+			if (ss2[i].matches("beta.*")) {
+				try {
+					if (ss2[i].length() == 4)
+						x = 0;
+					else
+						x = Integer.parseInt(ss2[i].substring(4));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss2[i] = "" + (-100 + x);
+			}
+			// 2 < 2.1, etc.
+			// so treat 2 as 2.0
+			if (ss1[i].equals(""))
+				ss1[i] = "0";
+			if (ss2[i].equals(""))
+				ss2[i] = "0";
+			// 2.1 < 2.1.dev, etc.
+			// 2.1.dev < 2.1.dev2 < 2.1.dev3, etc.
+			// so replace devx with 0.5+x/1000
+			if (ss1[i].matches("dev.*")) {
+				try {
+					if (ss1[i].length() == 3)
+						x = 0;
+					else
+						x = Integer.parseInt(ss1[i].substring(3));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss1[i] = "" + (0.5 + x / 1000.0);
+			}
+			if (ss2[i].matches("dev.*")) {
+				try {
+					if (ss2[i].length() == 3)
+						x = 0;
+					else
+						x = Integer.parseInt(ss2[i].substring(3));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss2[i] = "" + (0.5 + x / 1000.0);
+			}
+			// replace rx (e.g. as in 4.0.alpha.r5555) with x
+			if (ss1[i].matches("r.*")) {
+				try {
+					x = Integer.parseInt(ss1[i].substring(1));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss1[i] = "" + x;
+			}
+			if (ss2[i].matches("r.*")) {
+				try {
+					x = Integer.parseInt(ss2[i].substring(1));
+				} catch (NumberFormatException e) {
+					x = 0;
+				}
+				ss2[i] = "" + x;
+			}
+			// See if strings are integers
+			try {
+				s1num = true;
+				s1 = Double.parseDouble(ss1[i]);
+			} catch (NumberFormatException e) {
+				s1num = false;
+			}
+			try {
+				s2num = true;
+				s2 = Double.parseDouble(ss2[i]);
+			} catch (NumberFormatException e) {
+				s2num = false;
+			}
+			if (s1num && s2num) {
+				if (s1 < s2)
+					return -1;
+				if (s1 > s2)
+					return 1;
+				if (s1 == s2)
+					continue;
+			}
+		}
+
+		return 0;
+	}
 }
 
 //------------------------------------------------------------------------------
diff --git a/prism/src/prism/ProbModel.java b/prism/src/prism/ProbModel.java
index 0b5ddac5399cd99f994e3ea231cb4e9a9df209d5..a2942f59ac8b4e5b3e833031b2bc9ab0be540167 100644
--- a/prism/src/prism/ProbModel.java
+++ b/prism/src/prism/ProbModel.java
@@ -814,23 +814,24 @@ public class ProbModel implements Model
 
 	// export transition matrix to a file
 
-	public void exportToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	public void exportToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		if (!explicit) {
-			PrismMTBDD.ExportMatrix(trans, getTransSymbol(), allDDRowVars, allDDColVars, odd, exportType, (file != null) ? file.getPath() : null);
+			PrismMTBDD.ExportMatrix(trans, getTransSymbol(), allDDRowVars, allDDColVars, odd, exportType, (file != null) ? file.getPath() : null, precision);
 		} else {
-			PrismSparse.ExportMatrix(trans, getTransSymbol(), allDDRowVars, allDDColVars, odd, exportType, (file != null) ? file.getPath() : null);
+			PrismSparse.ExportMatrix(trans, getTransSymbol(), allDDRowVars, allDDColVars, odd, exportType, (file != null) ? file.getPath() : null, precision);
 		}
 	}
 
 	@Override
-	public void exportStateRewardsToFile(int r, int exportType, File file) throws FileNotFoundException, PrismException
+	public void exportStateRewardsToFile(int r, int exportType, File file, int precision) throws FileNotFoundException, PrismException
 	{
-		PrismMTBDD.ExportVector(stateRewards[r], "c" + (r + 1), allDDRowVars, odd, exportType, (file == null) ? null : file.getPath());
+		PrismMTBDD.ExportVector(stateRewards[r], "c" + (r + 1), allDDRowVars, odd, exportType, (file == null) ? null : file.getPath(), precision);
 	}
 
 	@Deprecated
-	public String exportStateRewardsToFile(int exportType, File file) throws FileNotFoundException, PrismException
+	@Override
+	public String exportStateRewardsToFile(int exportType, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		// export state rewards vector to a file
 		// returns string containing files used if there were more than 1, null otherwise
@@ -845,23 +846,23 @@ public class ProbModel implements Model
 				filename = PrismUtils.addCounterSuffixToFilename(filename, i + 1);
 				allFilenames += ((i > 0) ? ", " : "") + filename;
 			}
-			PrismMTBDD.ExportVector(stateRewards[i], "c" + (i + 1), allDDRowVars, odd, exportType, filename);
+			PrismMTBDD.ExportVector(stateRewards[i], "c" + (i + 1), allDDRowVars, odd, exportType, filename, precision);
 		}
 		return (allFilenames.length() > 0) ? allFilenames : null;
 	}
 
 	@Override
-	public void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file) throws FileNotFoundException, PrismException
+	public void exportTransRewardsToFile(int r, int exportType, boolean ordered, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		if (!ordered) {
-			PrismMTBDD.ExportMatrix(transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, odd, exportType, (file == null) ? null : file.getPath());
+			PrismMTBDD.ExportMatrix(transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, odd, exportType, (file == null) ? null : file.getPath(), precision);
 		} else {
-			PrismSparse.ExportMatrix(transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, odd, exportType, (file == null) ? null : file.getPath());
+			PrismSparse.ExportMatrix(transRewards[r], "C" + (r + 1), allDDRowVars, allDDColVars, odd, exportType, (file == null) ? null : file.getPath(), precision);
 		}
 	}
 
 	@Deprecated
-	public String exportTransRewardsToFile(int exportType, boolean explicit, File file) throws FileNotFoundException, PrismException
+	public String exportTransRewardsToFile(int exportType, boolean explicit, File file, int precision) throws FileNotFoundException, PrismException
 	{
 		// export transition rewards matrix to a file
 		// returns string containing files used if there were more than 1, null otherwise
@@ -877,9 +878,9 @@ public class ProbModel implements Model
 				allFilenames += ((i > 0) ? ", " : "") + filename;
 			}
 			if (!explicit) {
-				PrismMTBDD.ExportMatrix(transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, odd, exportType, filename);
+				PrismMTBDD.ExportMatrix(transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, odd, exportType, filename, precision);
 			} else {
-				PrismSparse.ExportMatrix(transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, odd, exportType, filename);
+				PrismSparse.ExportMatrix(transRewards[i], "C" + (i + 1), allDDRowVars, allDDColVars, odd, exportType, filename, precision);
 			}
 		}
 		return (allFilenames.length() > 0) ? allFilenames : null;
diff --git a/prism/src/prism/ProbModelChecker.java b/prism/src/prism/ProbModelChecker.java
index 49ad10f3c5af226664e9b9a0cab23f28f1027f71..91ffc043df87c8329aa67d9d5c21d2f2703b8620 100644
--- a/prism/src/prism/ProbModelChecker.java
+++ b/prism/src/prism/ProbModelChecker.java
@@ -593,8 +593,9 @@ public class ProbModelChecker extends NonProbModelChecker
 		// Output product, if required
 		if (prism.getExportProductTrans()) {
 			try {
+				int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 				mainLog.println("\nExporting product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"...");
-				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()));
+				modelProduct.exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()), precision);
 			} catch (FileNotFoundException e) {
 				mainLog.printWarning("Could not export product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"");
 			}
@@ -1019,8 +1020,9 @@ public class ProbModelChecker extends NonProbModelChecker
 		// Output product, if required
 		if (prism.getExportProductTrans()) {
 			try {
+				int precision = getSettings().getInteger(PrismSettings.PRISM_EXPORT_MODEL_PRECISION);
 				mainLog.println("\nExporting product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"...");
-				modelProduct.getProductModel().exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()));
+				modelProduct.getProductModel().exportToFile(Prism.EXPORT_PLAIN, true, new File(prism.getExportProductTransFilename()), precision);
 			} catch (FileNotFoundException e) {
 				mainLog.printWarning("Could not export product transition matrix to file \"" + prism.getExportProductTransFilename() + "\"");
 			}
diff --git a/prism/src/prism/RangingConstant.java b/prism/src/prism/RangingConstant.java
deleted file mode 100644
index ac0cac79b005568e9abcca7d39c62d52814ab08a..0000000000000000000000000000000000000000
--- a/prism/src/prism/RangingConstant.java
+++ /dev/null
@@ -1,88 +0,0 @@
-//==============================================================================
-//	
-//	Copyright (c) 2002-
-//	Authors:
-//	* Andrew Hinton <ug60axh@cs.bham.ac.uk> (University of Birmingham)
-//	* Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
-//	
-//------------------------------------------------------------------------------
-//	
-//	This file is part of PRISM.
-//	
-//	PRISM is free software; you can redistribute it and/or modify
-//	it under the terms of the GNU General Public License as published by
-//	the Free Software Foundation; either version 2 of the License, or
-//	(at your option) any later version.
-//	
-//	PRISM is distributed in the hope that it will be useful,
-//	but WITHOUT ANY WARRANTY; without even the implied warranty of
-//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//	GNU General Public License for more details.
-//	
-//	You should have received a copy of the GNU General Public License
-//	along with PRISM; if not, write to the Free Software Foundation,
-//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//	
-//==============================================================================
-
-package prism;
-
-public class RangingConstant
-{
-    private String name;
-    private int type;
-    private Object lo, hi, step;
-    
-    /** Creates a new instance of RangingConstant */
-    public RangingConstant(String name, int type, Object lo, Object hi, Object step)
-    {
-	this.name = name;
-	this.type = type;
-	this.lo = lo;
-	this.hi = hi;
-	this.step = step;
-    }
-    
-    //ACCESS METHODS
-    
-    public String getName()
-    {
-	return name;
-    }
-    
-    public int getType()
-    {
-	return type;
-    }
-    
-    public Object getLow()
-    {
-	return lo;
-    }
-    
-    public Object getHi()
-    {
-	return hi;
-    }
-    
-    public Object getStep()
-    {
-	return step;
-    }
-    
-    public int getNumSteps()
-    {
-	//dummy dummy dummy
-	return 2;
-    }
-    
-    /*	Gets the value for the constant at the index i,
-     *	eg. if we have 3..2..9, the values would be 
-     *  [3, 5, 7, 9] and so getValue(1) would return 5.
-     */
-    public Object getValue(int i)
-    {
-	return new Integer(1); //dummy dummy dummy
-    }
-    
-}
diff --git a/prism/src/prism/StateModelChecker.java b/prism/src/prism/StateModelChecker.java
index 52eb4f5f1ec7d4a0d899a9ace53c05ab545904a8..531615a8b3b1c4875edcc4b52d2ae43c1be5ecd8 100644
--- a/prism/src/prism/StateModelChecker.java
+++ b/prism/src/prism/StateModelChecker.java
@@ -1279,7 +1279,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			vals.filter(ddFilter);
 			d = vals.getNNZ();
 			// Store as object/vector
-			resObj = new Integer((int) d);
+			resObj = Integer.valueOf((int) d);
 			resVals = new StateValuesMTBDD(JDD.Constant(d), model);
 			// Create explanation of result and print some details to log
 			resultExpl = filterTrue ? "Count of satisfying states" : "Count of satisfying states also in filter";
@@ -1299,7 +1299,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			// Compute average
 			d = vals.sumOverBDD(ddFilter) / JDD.GetNumMinterms(ddFilter, allDDRowVars.n());
 			// Store as object/vector
-			resObj = new Double(d);
+			resObj = Double.valueOf(d);
 			resVals = new StateValuesMTBDD(JDD.Constant(d), model);
 			// Create explanation of result and print some details to log
 			resultExpl = "Average over " + filterStatesString;
@@ -1348,7 +1348,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			states = new StateListMTBDD(dd, model);
 			b = dd.equals(ddFilter);
 			// Store as object/vector
-			resObj = new Boolean(b);
+			resObj = Boolean.valueOf(b);
 			resVals = new StateValuesMTBDD(JDD.Constant(b ? 1.0 : 0.0), model);
 			// Set vals to null so that is not clear()-ed twice
 			vals = null;
@@ -1382,7 +1382,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			dd = JDD.And(dd, ddFilter);
 			b = !dd.equals(JDD.ZERO);
 			// Store as object/vector
-			resObj = new Boolean(b);
+			resObj = Boolean.valueOf(b);
 			resVals = new StateValuesMTBDD(JDD.Constant(b ? 1.0 : 0.0), model);
 			// Set vals to null so that is not clear()-ed twice
 			vals = null;
@@ -1566,7 +1566,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			stateRewards = model.getStateRewards(0);
 		} else if (rs instanceof Expression) {
 			int i = ((Expression) rs).evaluateInt(constantValues);
-			rs = new Integer(i); // for better error reporting below
+			rs = Integer.valueOf(i); // for better error reporting below
 			stateRewards = model.getStateRewards(i - 1);
 		} else if (rs instanceof String) {
 			stateRewards = model.getStateRewards((String) rs);
@@ -1589,7 +1589,7 @@ public class StateModelChecker extends PrismComponent implements ModelChecker
 			transRewards = model.getTransRewards(0);
 		} else if (rs instanceof Expression) {
 			int i = ((Expression) rs).evaluateInt(constantValues);
-			rs = new Integer(i); // for better error reporting below
+			rs = Integer.valueOf(i); // for better error reporting below
 			transRewards = model.getTransRewards(i - 1);
 		} else if (rs instanceof String) {
 			transRewards = model.getTransRewards((String) rs);
diff --git a/prism/src/pta/PTAModelChecker.java b/prism/src/pta/PTAModelChecker.java
index 93b8af5f8c9c0d3b9c64bd5fcd557a4949837357..1b05455eef409aed6d376aa161943f1f364e7843 100644
--- a/prism/src/pta/PTAModelChecker.java
+++ b/prism/src/pta/PTAModelChecker.java
@@ -228,7 +228,7 @@ public class PTAModelChecker extends PrismComponent
 		prob = computeProbabilisticReachability(targetLocs, min);
 
 		// Return result
-		return new Result(new Double(prob));
+		return new Result(Double.valueOf(prob));
 	}
 
 	/**
diff --git a/prism/src/settings/BooleanEditor.java b/prism/src/settings/BooleanEditor.java
index 4808ce510b563688d35619b08942f2b6342ad0b5..6637d5356c6992cb1447e4d2faaa04c014424632 100644
--- a/prism/src/settings/BooleanEditor.java
+++ b/prism/src/settings/BooleanEditor.java
@@ -59,7 +59,7 @@ public class BooleanEditor implements SettingEditor, ActionListener
 	{
 		if (modified) {
 			modified = false;
-			return new Boolean(renderer.isSelected());
+			return Boolean.valueOf(renderer.isSelected());
 		} else
 			return NOT_CHANGED_VALUE;
 	}
diff --git a/prism/src/settings/BooleanSetting.java b/prism/src/settings/BooleanSetting.java
index efb97a41f52d4257da57ff8963751525eb68d85e..ff64e5a85ee8b3250c9c15c7cb868c76fb9a6b49 100644
--- a/prism/src/settings/BooleanSetting.java
+++ b/prism/src/settings/BooleanSetting.java
@@ -70,8 +70,8 @@ public class BooleanSetting extends Setting
     
 	public Object parseStringValue(String string) throws SettingException
 	{
-		if(string.equals("true")) return new Boolean(true);
-		else if(string.equals("false")) return new Boolean(false);
+		if(string.equals("true")) return Boolean.valueOf(true);
+		else if(string.equals("false")) return Boolean.valueOf(false);
 		else throw new SettingException("Error when parsing: "+string+" as a Boolean value.");
 	}
 	
diff --git a/prism/src/settings/DoubleEditor.java b/prism/src/settings/DoubleEditor.java
index db215519ca22c33ee0aa8d66a83d042c8b65a8cf..4b3e735004757e5c7f3d09dde1503f0e57f10437 100644
--- a/prism/src/settings/DoubleEditor.java
+++ b/prism/src/settings/DoubleEditor.java
@@ -58,7 +58,7 @@ public class DoubleEditor implements SettingEditor, CaretListener, FocusListener
 			return NOT_CHANGED_VALUE;
 		valueGot = true;
 		try {
-			return new Double(field.getText());
+			return Double.valueOf(field.getText());
 		} catch (NumberFormatException e) {
 			throw new SettingException("The value entered is not a valid number.");
 		}
@@ -148,7 +148,7 @@ public class DoubleEditor implements SettingEditor, CaretListener, FocusListener
 				value = NOT_CHANGED_VALUE;
 			else {
 				try {
-					value = new Double(field.getText());
+					value = Double.valueOf(field.getText());
 				} catch (NumberFormatException ex) {
 					value = new SettingException("The value entered is not a valid number.");
 				}
diff --git a/prism/src/settings/DoubleSetting.java b/prism/src/settings/DoubleSetting.java
index 4f3ccc18f1a118a4f53d3c0e90de39b99ed7da23..4c304d2cbc9658f11d21d4efbe6f672742a1dbc7 100644
--- a/prism/src/settings/DoubleSetting.java
+++ b/prism/src/settings/DoubleSetting.java
@@ -72,7 +72,7 @@ public class DoubleSetting extends Setting
 	{
 		try
 		{
-			return new Double(string);
+			return Double.valueOf(string);
 		}
 		catch(NumberFormatException e)
 		{
diff --git a/prism/src/settings/IntegerEditor.java b/prism/src/settings/IntegerEditor.java
index 3a87cfe257dfbb7e465d05fd7724eacc0fba95f9..87e9c99991e526d52442aa350707529e90712c40 100644
--- a/prism/src/settings/IntegerEditor.java
+++ b/prism/src/settings/IntegerEditor.java
@@ -58,7 +58,7 @@ public class IntegerEditor implements SettingEditor, CaretListener, FocusListene
         valueGot = true;
         try
         {
-            return new Integer(field.getText());
+            return Integer.valueOf(field.getText());
         }
         catch(NumberFormatException e)
         {
@@ -171,7 +171,7 @@ public class IntegerEditor implements SettingEditor, CaretListener, FocusListene
             {
                 try
                 {
-                    value = new Integer(field.getText());
+                    value = Integer.valueOf(field.getText());
                 }
                 catch(NumberFormatException ex)
                 {
diff --git a/prism/src/settings/IntegerSetting.java b/prism/src/settings/IntegerSetting.java
index e28ed4ad019128f808cd58114650363b371f5e3c..615426ea0ab14ce258144c0b1053ed456c69de59 100644
--- a/prism/src/settings/IntegerSetting.java
+++ b/prism/src/settings/IntegerSetting.java
@@ -72,7 +72,7 @@ public class IntegerSetting extends Setting
 	{
 		try
 		{
-			return new Integer(string);
+			return Integer.valueOf(string);
 		}
 		catch(NumberFormatException e)
 		{
diff --git a/prism/src/settings/LongEditor.java b/prism/src/settings/LongEditor.java
index a19eabca710d75521f702d3fbe6f1ceb68cb4f8b..9f8f1673319e65adf7c8beff8001b67cb3effcb6 100644
--- a/prism/src/settings/LongEditor.java
+++ b/prism/src/settings/LongEditor.java
@@ -58,7 +58,7 @@ public class LongEditor implements SettingEditor, CaretListener, FocusListener
         valueGot = true;
         try
         {
-            return new Long(field.getText());
+            return Long.valueOf(field.getText());
         }
         catch(NumberFormatException e)
         {
@@ -171,7 +171,7 @@ public class LongEditor implements SettingEditor, CaretListener, FocusListener
             {
                 try
                 {
-                    value = new Long(field.getText());
+                    value = Long.valueOf(field.getText());
                 }
                 catch(NumberFormatException ex)
                 {
diff --git a/prism/src/settings/LongSetting.java b/prism/src/settings/LongSetting.java
index d3eec188f132eb5502f80a577c68c0201ca8aa98..9d956e6e01573a8178ff379c1a0d701660bcd030 100644
--- a/prism/src/settings/LongSetting.java
+++ b/prism/src/settings/LongSetting.java
@@ -72,7 +72,7 @@ public class LongSetting extends Setting
 	{
 		try
 		{
-			return new Long(string);
+			return Long.valueOf(string);
 		}
 		catch(NumberFormatException e)
 		{
diff --git a/prism/src/settings/SettingTable.java b/prism/src/settings/SettingTable.java
index e80b0e68fe39e896d1f3afde16f31ac601cc31d9..37a73e593a3cb40f8f17aec033be17f6015cad74 100644
--- a/prism/src/settings/SettingTable.java
+++ b/prism/src/settings/SettingTable.java
@@ -633,7 +633,7 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 					currGroupCount++;
 					if(!po.getSettingOwnerName().equals(""))ownerList += "\'"+po.getSettingOwnerName()+"\'";
 					tempName = po.getSettingOwnerClassName();
-					groupStarts.add(new Integer(0));
+					groupStarts.add(Integer.valueOf(0));
 				}
 				else if(po.getSettingOwnerID() == last.getSettingOwnerID())
 				{
@@ -650,12 +650,12 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 					tempName+=" "+ownerList+"";
 					ownerList = "";
 					groupNames.add(tempName);
-					groupSizes.add(new Integer(currGroupCount));
+					groupSizes.add(Integer.valueOf(currGroupCount));
 					currGroupCount = 0;
 					currGroupCount++;
 					ownerList += "\'"+po.getSettingOwnerName()+"\'";
 					if(!po.getSettingOwnerName().equals(""))tempName = po.getSettingOwnerClassName()+" \'"+po.getSettingOwnerName()+"\'";
-					groupStarts.add(new Integer(index));
+					groupStarts.add(Integer.valueOf(index));
 				}
 				last = po;
 				index++;
@@ -664,7 +664,7 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 			{
 				tempName += " "+ownerList+"";
 				groupNames.add(tempName);
-				groupSizes.add(new Integer(currGroupCount));
+				groupSizes.add(Integer.valueOf(currGroupCount));
 			}
 			if(currentGroup > owners.size()-1) currentGroup = 0;
 			comboModel = new DefaultComboBoxModel(groupNames.toArray());
@@ -693,7 +693,7 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 					currGroupCount++;
 					if(!po.getSettingOwnerName().equals(""))ownerList += "\'"+po.getSettingOwnerName()+"\'";
 					tempName = po.getSettingOwnerClassName();
-					//groupStarts.add(new Integer(0));
+					//groupStarts.add(Integer.valueOf(0));
 				}
 				else if(po.getSettingOwnerID() == last.getSettingOwnerID())
 				{
@@ -711,12 +711,12 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 					ownerList = "";
 					groupNames.add(tempName);
 					//System.out.println("adding: "+tempName);
-					//groupSizes.add(new Integer(currGroupCount));
+					//groupSizes.add(Integer.valueOf(currGroupCount));
 					currGroupCount = 0;
 					currGroupCount++;
 					ownerList += "\'"+po.getSettingOwnerName()+"\'";
 					if(!po.getSettingOwnerName().equals(""))tempName = po.getSettingOwnerClassName()+" \'"+po.getSettingOwnerName()+"\'";
-					//groupStarts.add(new Integer(index));
+					//groupStarts.add(Integer.valueOf(index));
 				}
 				last = po;
 				index++;
@@ -726,7 +726,7 @@ public class SettingTable extends JPanel implements ListSelectionListener, Table
 				tempName += " "+ownerList+"";
 				groupNames.add(tempName);
 				//System.out.println("adding "+tempName);
-				//groupSizes.add(new Integer(currGroupCount));
+				//groupSizes.add(Integer.valueOf(currGroupCount));
 			}
 			//if(currentGroup > owners.size()-1) currentGroup = 0;
 			comboModel = new DefaultComboBoxModel(groupNames.toArray());
diff --git a/prism/src/simulator/method/APMCMethod.java b/prism/src/simulator/method/APMCMethod.java
index ce0125ac7d1f519112d60b2319ea7185c0e14380..73d1e201f56807da00fed538969c37ce61206377 100644
--- a/prism/src/simulator/method/APMCMethod.java
+++ b/prism/src/simulator/method/APMCMethod.java
@@ -151,19 +151,19 @@ public abstract class APMCMethod extends SimulationMethod
 		double estimate = sampler.getMeanValue();
 		switch (prOp) {
 		case 0: // 0=quantitative
-			return new Double(estimate);
+			return Double.valueOf(estimate);
 		case -1: // -1=lower bound
 			if (estimate >= theta + approximation)
-				return new Boolean(true);
+				return Boolean.valueOf(true);
 			else if (estimate <= theta - approximation)
-				return new Boolean(false);
+				return Boolean.valueOf(false);
 			else
 				throw new PrismException("Approximation is not precise enough to get a result");
 		case 1: // 1=upper bound
 			if (estimate >= theta + approximation)
-				return new Boolean(false);
+				return Boolean.valueOf(false);
 			else if (estimate <= theta - approximation)
-				return new Boolean(true);
+				return Boolean.valueOf(true);
 			else
 				throw new PrismException("Approximation is not precise enough to get a result");
 		default:
diff --git a/prism/src/simulator/method/CIMethod.java b/prism/src/simulator/method/CIMethod.java
index 50e3b0578dad7ad496726eef8e05e92df8d271f1..f4729d81724cf71f7ddecaf58cebb14df0145a59 100644
--- a/prism/src/simulator/method/CIMethod.java
+++ b/prism/src/simulator/method/CIMethod.java
@@ -140,21 +140,21 @@ public abstract class CIMethod extends SimulationMethod
 		//double stddev = Math.sqrt(variance);
 		switch (prOp) {
 		case 0: // 0=quantitative
-			return new Double(mean);
+			return Double.valueOf(mean);
 			//return new prism.Interval(mean - stddev, mean + stddev);
-			//return new Double(stddev / mean); // noise
+			//return Double.valueOf(stddev / mean); // noise
 		case -1: // -1=lower bound
 			if (mean >= theta + width)
-				return new Boolean(true);
+				return Boolean.valueOf(true);
 			else if (mean <= theta - width)
-				return new Boolean(false);
+				return Boolean.valueOf(false);
 			else
 				throw new PrismException("Approximation is not precise enough to get a result");
 		case 1: // 1=upper bound
 			if (mean >= theta + width)
-				return new Boolean(false);
+				return Boolean.valueOf(false);
 			else if (mean <= theta - width)
-				return new Boolean(true);
+				return Boolean.valueOf(true);
 			else
 				throw new PrismException("Approximation is not precise enough to get a result");
 		default:
diff --git a/prism/src/simulator/method/SPRTMethod.java b/prism/src/simulator/method/SPRTMethod.java
index 2594643c166ec7a42cc219c90d05526fc93d5c1b..60053b007907af382b6bfeb600da66dbb9a3104a 100644
--- a/prism/src/simulator/method/SPRTMethod.java
+++ b/prism/src/simulator/method/SPRTMethod.java
@@ -229,7 +229,7 @@ public final class SPRTMethod extends SimulationMethod
 	public Object getResult(Sampler sampler) throws PrismException
 	{
 		// Is hypothesis H0 true?
-		return new Boolean(h0true);
+		return Boolean.valueOf(h0true);
 	}
 
 	@Override
diff --git a/prism/src/simulator/sampler/SamplerBoolean.java b/prism/src/simulator/sampler/SamplerBoolean.java
index f8d0b48f1dc25aebe1312a9688240cea75b4cc31..ab6b18b2f7a5e262387cd1a557dd4e4107b901ae 100644
--- a/prism/src/simulator/sampler/SamplerBoolean.java
+++ b/prism/src/simulator/sampler/SamplerBoolean.java
@@ -73,7 +73,7 @@ public abstract class SamplerBoolean extends Sampler
 	public Object getCurrentValue()
 	{
 		// XOR: value && !negated || !value && negated 
-		return new Boolean(value != negated);
+		return Boolean.valueOf(value != negated);
 	}
 
 	@Override
diff --git a/prism/src/simulator/sampler/SamplerDouble.java b/prism/src/simulator/sampler/SamplerDouble.java
index 25a90dfb2535d107cf6c0073af93fcefc5cf22ec..04cd740047d356784344d84b98553f09888cc90b 100644
--- a/prism/src/simulator/sampler/SamplerDouble.java
+++ b/prism/src/simulator/sampler/SamplerDouble.java
@@ -93,7 +93,7 @@ public abstract class SamplerDouble extends Sampler
 	@Override
 	public Object getCurrentValue()
 	{
-		return new Double(value);
+		return Double.valueOf(value);
 	}
 
 	@Override
diff --git a/prism/src/sparse/PS_ExportMDP.cc b/prism/src/sparse/PS_ExportMDP.cc
index 99f370bee14891cb2e462878b55ad31d40777ec0..bba7bd651c223bce2db6482d177c2c369f5163e8 100644
--- a/prism/src/sparse/PS_ExportMDP.cc
+++ b/prism/src/sparse/PS_ExportMDP.cc
@@ -131,13 +131,13 @@ jstring fn		// filename
 			for (k = l2; k < h2; k++) {
 				switch (export_type) {
 				case EXPORT_PLAIN:
-					export_string("%d %d %d %.12g", i, j-l1, cols[k], non_zeros[k]);
-					if (actions != NULL) export_string(" %s", (actions[j]>0?action_names[actions[j]-1]:""));
+					export_string("%d %d %d %.*g", i, j-l1, cols[k], export_model_precision, non_zeros[k]);
+					if (actions != NULL && actions[j]>0) export_string(" %s", action_names[actions[j]-1]);
 					export_string("\n");
 					break;
-				case EXPORT_MATLAB: export_string("%s%d(%d,%d)=%.12g;\n", export_name, j-l1+1, i+1, cols[k]+1, non_zeros[k]); break;
-				case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("n%d_%d -> %d [ label=\"%.12g\" ];\n", i, j-l1, cols[k], non_zeros[k]); break;
-				case EXPORT_ROWS: export_string(" %.12g:%d", non_zeros[k], cols[k]); break;
+				case EXPORT_MATLAB: export_string("%s%d(%d,%d)=%.*g;\n", export_name, j-l1+1, i+1, cols[k]+1, export_model_precision, non_zeros[k]); break;
+				case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("n%d_%d -> %d [ label=\"%.*g\" ];\n", i, j-l1, cols[k], export_model_precision, non_zeros[k]); break;
+				case EXPORT_ROWS: export_string(" %.*g:%d", export_model_precision, non_zeros[k], cols[k]); break;
 				}
 			}
 			if (export_type == EXPORT_ROWS && actions != NULL) export_string(" %s", (actions[j]>0?action_names[actions[j]-1]:""));
diff --git a/prism/src/sparse/PS_ExportMatrix.cc b/prism/src/sparse/PS_ExportMatrix.cc
index b392350f7cc271c5fa39859fc1b8c4010169725c..e93ffce41938ef99d43e2a0a60a543288831ce9c 100644
--- a/prism/src/sparse/PS_ExportMatrix.cc
+++ b/prism/src/sparse/PS_ExportMatrix.cc
@@ -144,11 +144,11 @@ jstring fn		// filename
 				d = dist[(int)(cols[j] & dist_mask)];
 			}
 			switch (export_type) {
-			case EXPORT_PLAIN: export_string("%d %d %.12g\n", r, c, d); break;
-			case EXPORT_MATLAB: export_string("%s(%d,%d)=%.12g;\n", export_name, r+1, c+1, d); break;
-			case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("%d -> %d [ label=\"%.12g\" ];\n", r, c, d); break;
-			case EXPORT_MRMC: export_string("%d %d %.12g\n", r+1, c+1, d); break;
-			case EXPORT_ROWS: export_string(" %.12g:%d", d, c); break;
+			case EXPORT_PLAIN: export_string("%d %d %.*g\n", r, c, export_model_precision, d); break;
+			case EXPORT_MATLAB: export_string("%s(%d,%d)=%.*g;\n", export_name, r+1, c+1, export_model_precision, d); break;
+			case EXPORT_DOT: case EXPORT_DOT_STATES: export_string("%d -> %d [ label=\"%.*g\" ];\n", r, c, export_model_precision, d); break;
+			case EXPORT_MRMC: export_string("%d %d %.*g\n", r+1, c+1, export_model_precision, d); break;
+			case EXPORT_ROWS: export_string(" %.*g:%d", export_model_precision, d, c); break;
 			}
 		}
 		if (export_type == EXPORT_ROWS) export_string("\n");
diff --git a/prism/src/sparse/PS_ExportSubMDP.cc b/prism/src/sparse/PS_ExportSubMDP.cc
index 0c37d8402a01413d2984b556a66224569f33eef0..e10393f5355fc1908f8eed2d73d8b7073ae0ec6a 100644
--- a/prism/src/sparse/PS_ExportSubMDP.cc
+++ b/prism/src/sparse/PS_ExportSubMDP.cc
@@ -109,9 +109,9 @@ jstring fn		// filename
 			if (export_type == EXPORT_ROWS) export_string("%d", i);
 			for (k = l2; k < h2; k++) {
 				switch (export_type) {
-				case EXPORT_PLAIN: export_string("%d %d %d %.12g\n", i, j-l1, cols[k], non_zeros[k]); break;
-				case EXPORT_MATLAB: export_string("%s%d(%d,%d)=%.12g;\n", export_name, j-l1+1, i+1, cols[k]+1, non_zeros[k]); break;
-				case EXPORT_ROWS: export_string(" %.12g:%d", non_zeros[k], cols[k]); break;
+				case EXPORT_PLAIN: export_string("%d %d %d %.*g\n", i, j-l1, cols[k], export_model_precision, non_zeros[k]); break;
+				case EXPORT_MATLAB: export_string("%s%d(%d,%d)=%.*g;\n", export_name, j-l1+1, i+1, cols[k]+1, export_model_precision, non_zeros[k]); break;
+				case EXPORT_ROWS: export_string(" %.*g:%d", export_model_precision, non_zeros[k], cols[k]); break;
 				}
 			}
 			if (export_type == EXPORT_ROWS) export_string("\n");
diff --git a/prism/src/sparse/PrismSparse.java b/prism/src/sparse/PrismSparse.java
index c98d44e86990bcc1d90f57c2e8d44efd65b73432..be880374c7eaa36895f53ff268d1e35174c8cd24 100644
--- a/prism/src/sparse/PrismSparse.java
+++ b/prism/src/sparse/PrismSparse.java
@@ -589,8 +589,9 @@ public class PrismSparse
 
 	// export matrix
 	private static native int PS_ExportMatrix(long matrix, String name, long rv, int nrv, long cv, int ncv, long odd, int exportType, String filename);
-	public static void ExportMatrix(JDDNode matrix, String name, JDDVars rows, JDDVars cols, ODDNode odd, int exportType, String filename) throws FileNotFoundException, PrismException
+	public static void ExportMatrix(JDDNode matrix, String name, JDDVars rows, JDDVars cols, ODDNode odd, int exportType, String filename, int precision) throws FileNotFoundException, PrismException
 	{
+		PrismNative.setExportModelPrecision(precision);
 		checkNumStates(odd);
 		int res = PS_ExportMatrix(matrix.ptr(), name, rows.array(), rows.n(), cols.array(), cols.n(), odd.ptr(), exportType, filename);
 		if (res == -1) {
@@ -603,8 +604,9 @@ public class PrismSparse
 	
 	// export mdp
 	private static native int PS_ExportMDP(long mdp, long trans_actions, List<String> synchs, String name, long rv, int nrv, long cv, int ncv, long ndv, int nndv, long odd, int exportType, String filename);
-	public static void ExportMDP(JDDNode mdp, JDDNode transActions, List<String> synchs, String name, JDDVars rows, JDDVars cols, JDDVars nondet, ODDNode odd, int exportType, String filename) throws FileNotFoundException, PrismException
+	public static void ExportMDP(JDDNode mdp, JDDNode transActions, List<String> synchs, String name, JDDVars rows, JDDVars cols, JDDVars nondet, ODDNode odd, int exportType, String filename, int precision) throws FileNotFoundException, PrismException
 	{
+		PrismNative.setExportModelPrecision(precision);
 		checkNumStates(odd);
 		int res = PS_ExportMDP(mdp.ptr(), (transActions == null) ? 0 : transActions.ptr(), synchs, name, rows.array(), rows.n(), cols.array(), cols.n(), nondet.array(), nondet.n(), odd.ptr(), exportType, filename);
 		if (res == -1) {
@@ -617,8 +619,9 @@ public class PrismSparse
 	
 	// export sub-mdp, i.e. mdp transition rewards
 	private static native int PS_ExportSubMDP(long mdp, long submdp, String name, long rv, int nrv, long cv, int ncv, long ndv, int nndv, long odd, int exportType, String filename);
-	public static void ExportSubMDP(JDDNode mdp, JDDNode submdp, String name, JDDVars rows, JDDVars cols, JDDVars nondet, ODDNode odd, int exportType, String filename) throws FileNotFoundException, PrismException
+	public static void ExportSubMDP(JDDNode mdp, JDDNode submdp, String name, JDDVars rows, JDDVars cols, JDDVars nondet, ODDNode odd, int exportType, String filename, int precision) throws FileNotFoundException, PrismException
 	{
+		PrismNative.setExportModelPrecision(precision);
 		checkNumStates(odd);
 		int res = PS_ExportSubMDP(mdp.ptr(), submdp.ptr(), name, rows.array(), rows.n(), cols.array(), cols.n(), nondet.array(), nondet.n(), odd.ptr(), exportType, filename);
 		if (res == -1) {
diff --git a/prism/src/strat/CSGStrategy.java b/prism/src/strat/CSGStrategy.java
index fbcdab275d33248e1ecffb541b1b9a4c17338aed..2217cfd6da00be954690f8cd00b1478894eff72f 100644
--- a/prism/src/strat/CSGStrategy.java
+++ b/prism/src/strat/CSGStrategy.java
@@ -219,12 +219,12 @@ public class CSGStrategy extends PrismComponent implements Strategy {
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out) {
+	public void exportInducedModel(PrismLog out, int precision) {
 		// TODO Auto-generated method stub
 	}
 	
 	@Override
-	public void exportDotFile(PrismLog out) {
+	public void exportDotFile(PrismLog out, int precision) {
 		// TODO Auto-generated method stub
 	}
 	
diff --git a/prism/src/strat/ExactValueStrategy.java b/prism/src/strat/ExactValueStrategy.java
index 857a57829e6ab00084291ca2363cb99d223e234b..2ed05fbfc5b8b4d7d10d396d3130f34cbea3e32d 100644
--- a/prism/src/strat/ExactValueStrategy.java
+++ b/prism/src/strat/ExactValueStrategy.java
@@ -730,14 +730,14 @@ public class ExactValueStrategy implements Strategy
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
diff --git a/prism/src/strat/MDStrategy.java b/prism/src/strat/MDStrategy.java
index 1d07df06ab31c881234bfa36074c7ba94d7c31b3..d71b00bd92c7464ebd42dd439572e04b44aa005f 100644
--- a/prism/src/strat/MDStrategy.java
+++ b/prism/src/strat/MDStrategy.java
@@ -194,10 +194,10 @@ public abstract class MDStrategy implements Strategy
 	}
 	
 	@Override
-	public abstract void exportInducedModel(PrismLog out);
+	public abstract void exportInducedModel(PrismLog out, int precision);
 	
 	@Override
-	public abstract void exportDotFile(PrismLog out);
+	public abstract void exportDotFile(PrismLog out, int precision);
 	
 	@Override
 	public abstract void exportStratToFile(File file, StrategyExportType exportType);
diff --git a/prism/src/strat/MDStrategyArray.java b/prism/src/strat/MDStrategyArray.java
index 4014c1165221bdf6c71c4c2231b6aa4394168365..8b532df5458ae15cc442a5b2e7868a54a7bcfea3 100644
--- a/prism/src/strat/MDStrategyArray.java
+++ b/prism/src/strat/MDStrategyArray.java
@@ -321,16 +321,16 @@ public class MDStrategyArray extends MDStrategy
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		Model dtmcInd = model.constructInducedModel(this);
-		dtmcInd.exportToPrismExplicitTra(out);
+		dtmcInd.exportToPrismExplicitTra(out, precision);
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
-		model.exportToDotFileWithStrat(out, null, choices);
+		model.exportToDotFileWithStrat(out, null, choices, precision);
 	}
 
 	@Override
diff --git a/prism/src/strat/MDStrategyIV.java b/prism/src/strat/MDStrategyIV.java
index 271ebeabb486ce1a60a4630faaec92e7dbf85184..09d22c29e99c81125d7cdb292e0fe268eefbba97 100644
--- a/prism/src/strat/MDStrategyIV.java
+++ b/prism/src/strat/MDStrategyIV.java
@@ -151,8 +151,7 @@ public class MDStrategyIV extends MDStrategy
 		}*/
 	}
 	
-	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		/*try {
 			model.exportStrategyToFile(iv, Prism.EXPORT_STRAT_INDUCED, true, null);
@@ -164,10 +163,10 @@ public class MDStrategyIV extends MDStrategy
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
 		/*try {
-			model.exportStrategyToFile(iv, Prism.EXPORT_STRAT_DOT, true, null);
+			model.exportToFile(Prism.EXPORT_DOT, true, new java.io.File("a.dot"), precision);
 		} catch (FileNotFoundException e) {
 			e.printStackTrace();
 		} catch (PrismException e) {
diff --git a/prism/src/strat/MemorylessDeterministicStrategy.java b/prism/src/strat/MemorylessDeterministicStrategy.java
index 912d7dec8e5e6f1ef3903337c4aac75a4bd3afdf..98b3ea665793c8d2be591a4420add1e8eab5b3be 100644
--- a/prism/src/strat/MemorylessDeterministicStrategy.java
+++ b/prism/src/strat/MemorylessDeterministicStrategy.java
@@ -373,14 +373,14 @@ public class MemorylessDeterministicStrategy implements Strategy
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
diff --git a/prism/src/strat/StepBoundedDeterministicStrategy.java b/prism/src/strat/StepBoundedDeterministicStrategy.java
index d8a034c5a97a3442cab906ba855d073e164ac23a..43475c0508d1824118fcb9583fcdb49802993a68 100644
--- a/prism/src/strat/StepBoundedDeterministicStrategy.java
+++ b/prism/src/strat/StepBoundedDeterministicStrategy.java
@@ -624,14 +624,14 @@ public class StepBoundedDeterministicStrategy implements Strategy
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 		
diff --git a/prism/src/strat/StochasticUpdateStrategy.java b/prism/src/strat/StochasticUpdateStrategy.java
index cef73319576d3c677d98c2cb73892d331fd00736..217f8a84a040ec2575e9b4908ed5b0ff56521379 100644
--- a/prism/src/strat/StochasticUpdateStrategy.java
+++ b/prism/src/strat/StochasticUpdateStrategy.java
@@ -1747,7 +1747,7 @@ public class StochasticUpdateStrategy implements Strategy
 	}
 
 	@Override
-	public void exportInducedModel(PrismLog out)
+	public void exportInducedModel(PrismLog out, int precision)
 	{
 		// TODO
 	}
@@ -1760,7 +1760,7 @@ public class StochasticUpdateStrategy implements Strategy
 	}
 
 	@Override
-	public void exportDotFile(PrismLog out)
+	public void exportDotFile(PrismLog out, int precision)
 	{
 		// TODO Auto-generated method stub
 
diff --git a/prism/src/strat/Strategy.java b/prism/src/strat/Strategy.java
index 95ff988af52ae6d355624276c08df7306d435371..624a74328889525ea7b877393f702805b872a685 100644
--- a/prism/src/strat/Strategy.java
+++ b/prism/src/strat/Strategy.java
@@ -36,6 +36,8 @@ import prism.Prism.StrategyExportType;
 import prism.PrismException;
 import prism.PrismLog;
 
+import static prism.PrismSettings.DEFAULT_EXPORT_MODEL_PRECISION;
+
 /**
  * Interface for classes to store strategies (for MDPs, games, etc.)
  * 
@@ -181,13 +183,31 @@ public interface Strategy
 	/**
 	 * Export the model induced by this strategy to a PrismLog.
 	 */
-	public void exportInducedModel(PrismLog out);
+	default void exportInducedModel(PrismLog out)
+	{
+		exportInducedModel(out, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
+
+	/**
+	 * Export the model induced by this strategy to a PrismLog.
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportInducedModel(PrismLog out, int precision);
 
 	/**
 	 * Export the strategy to a dot file (of the model showing the strategy).
 	 */
-	public void exportDotFile(PrismLog out);
+	default void exportDotFile(PrismLog out)
+	{
+		exportDotFile(out, DEFAULT_EXPORT_MODEL_PRECISION);
+	}
 
+	/**
+	 * Export the strategy to a dot file (of the model showing the strategy).
+	 * @param precision number of significant digits >= 1
+	 */
+	public void exportDotFile(PrismLog out, int precision);
+	
 	// Other new methods
 	
 	/**
diff --git a/prism/src/userinterface/GUIClipboard.java b/prism/src/userinterface/GUIClipboard.java
index 45b2a0ae498893fb65cb56f9b48f92c362d84a59..bc25024ff417b551db9bbbb20f19230b942a48cd 100644
--- a/prism/src/userinterface/GUIClipboard.java
+++ b/prism/src/userinterface/GUIClipboard.java
@@ -262,7 +262,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionUndo.putValue(Action.LONG_DESCRIPTION, "Undo the last edit.");
-        actionUndo.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_U));
+        actionUndo.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_U));
         actionUndo.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionUndo.putValue(Action.NAME, "Undo");
         actionUndo.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallUndo.png"));
@@ -275,7 +275,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionRedo.putValue(Action.LONG_DESCRIPTION, "Redo the last edit.");
-        actionRedo.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+        actionRedo.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
         actionRedo.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionRedo.putValue(Action.NAME, "Redo");
         actionRedo.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallRedo.png"));
@@ -289,7 +289,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionCut.putValue(Action.LONG_DESCRIPTION, "Copys the currently selected item/text to the clipboard and then removes it.");
-        actionCut.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
+        actionCut.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_C));
         actionCut.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionCut.putValue(Action.NAME, "Cut");
         actionCut.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallCut.png"));
@@ -302,7 +302,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionCopy.putValue(Action.LONG_DESCRIPTION, "Copys the currently selected item/text to the clipboard.");
-        actionCopy.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+        actionCopy.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
         actionCopy.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionCopy.putValue(Action.NAME, "Copy");
         actionCopy.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallCopy.png"));
@@ -315,7 +315,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionPaste.putValue(Action.LONG_DESCRIPTION, "Pastes the contents of the clipboard.");
-        actionPaste.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+        actionPaste.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
         actionPaste.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionPaste.putValue(Action.NAME, "Paste");
         actionPaste.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPaste.png"));
@@ -329,7 +329,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionDelete.putValue(Action.LONG_DESCRIPTION, "Removes the currently selected item");        
-        actionDelete.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
+        actionDelete.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
         actionDelete.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionDelete.putValue(Action.NAME, "Delete");
         actionDelete.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallDelete.png"));
@@ -342,7 +342,7 @@ public class GUIClipboard extends GUIPlugin
             }
         };
         actionSelectAll.putValue(Action.LONG_DESCRIPTION, "Selects all items of the focussed component.");
-        actionSelectAll.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+        actionSelectAll.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
         actionSelectAll.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
         actionSelectAll.putValue(Action.NAME, "Select all");
         actionSelectAll.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallSelectAll.png"));     
diff --git a/prism/src/userinterface/GUIFileMenu.java b/prism/src/userinterface/GUIFileMenu.java
index 0655379aa463e89cf57a546a40af76293cea63a2..5ed70d4d6a7420c64bf060eebdfa31890878bd99 100644
--- a/prism/src/userinterface/GUIFileMenu.java
+++ b/prism/src/userinterface/GUIFileMenu.java
@@ -104,7 +104,7 @@ public class GUIFileMenu extends GUIPlugin
         };
         exitAction.putValue(Action.LONG_DESCRIPTION, "Exits the application");
         //exitAction.putValue(Action.SHORT_DESCRIPTION, "Exit");
-        exitAction.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X));
+        exitAction.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_X));
         exitAction.putValue(Action.NAME, "Exit");
         exitAction.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallExit.png"));
         exitAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
diff --git a/prism/src/userinterface/GUIPrism.java b/prism/src/userinterface/GUIPrism.java
index fded618d7dbb8e4f1d80c1b4d705df7fa5db5337..6158ca05818fb28828864cf222d44815becf8926 100644
--- a/prism/src/userinterface/GUIPrism.java
+++ b/prism/src/userinterface/GUIPrism.java
@@ -340,7 +340,7 @@ public class GUIPrism extends JFrame
 			}
 		};
 		prismOptions.putValue(Action.LONG_DESCRIPTION, "Brings up an option dialog for setting PRISM and user interface parameters.");
-		prismOptions.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+		prismOptions.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
 		prismOptions.putValue(Action.NAME, "Options");
 		prismOptions.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallOptions.png"));
 
@@ -355,7 +355,7 @@ public class GUIPrism extends JFrame
 			}
 		};
 		fontIncrease.putValue(Action.LONG_DESCRIPTION, "Increase the application font size.");
-		fontIncrease.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_I));
+		fontIncrease.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_I));
 		fontIncrease.putValue(Action.NAME, "Increase font size");
 		fontIncrease.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFontIncrease.png"));
 		fontIncrease.putValue(Action.ACCELERATOR_KEY,
@@ -372,7 +372,7 @@ public class GUIPrism extends JFrame
 			}
 		};
 		fontDecrease.putValue(Action.LONG_DESCRIPTION, "Decrease the application font size.");
-		fontDecrease.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
+		fontDecrease.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
 		fontDecrease.putValue(Action.NAME, "Decrease font size");
 		fontDecrease.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFontDecrease.png"));
 		fontDecrease.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
diff --git a/prism/src/userinterface/GUISimulationPicker.java b/prism/src/userinterface/GUISimulationPicker.java
index e261a415f315c7faac7d13c89505aba9aad17c36..4e0ee2dde9c899088a3a0271e8bb4449ffef9873 100644
--- a/prism/src/userinterface/GUISimulationPicker.java
+++ b/prism/src/userinterface/GUISimulationPicker.java
@@ -953,9 +953,9 @@ public class GUISimulationPicker extends javax.swing.JDialog implements KeyListe
 						String bool = initValuesModel.getValue(i).value.toString();
 						if (!(bool.equals("true") || bool.equals("false")))
 							throw new NumberFormatException();
-						parameterValue = new Boolean(bool);
+						parameterValue = Boolean.valueOf(bool);
 					} else if (initValuesModel.getValue(i).type instanceof TypeInt) {
-						parameterValue = new Integer(initValuesModel.getValue(i).value.toString());
+						parameterValue = Integer.valueOf(initValuesModel.getValue(i).value.toString());
 					} else { 
 						throw new NumberFormatException();
 					}
diff --git a/prism/src/userinterface/graph/AxisSettings.java b/prism/src/userinterface/graph/AxisSettings.java
index 98f6696b83fb265575e71fca4f91e8355b47704f..cd02ef7e4e1b3e1782acea30770b2666da54a9fd 100644
--- a/prism/src/userinterface/graph/AxisSettings.java
+++ b/prism/src/userinterface/graph/AxisSettings.java
@@ -1,36 +1,36 @@
-//==============================================================================
-//	
-//	Copyright (c) 2002-
-//	Authors:
-//	* Andrew Hinton <ug60axh@cs.bham.ac.uk> (University of Birmingham)
-//	* Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
-//	* Mark Kattenbelt <mark.kattenbelt@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
-//	* Alistair John Strachan <alistair@devzero.co.uk> (University of Edinburgh)
-//	* Mike Arthur <mike@mikearthur.co.uk> (University of Edinburgh)
-//	* Zak Cohen <zakcohen@gmail.com> (University of Edinburgh)
-//	
-//------------------------------------------------------------------------------
-//	
-//	This file is part of PRISM.
-//	
-//	PRISM is free software; you can redistribute it and/or modify
-//	it under the terms of the GNU General Public License as published by
-//	the Free Software Foundation; either version 2 of the License, or
-//	(at your option) any later version.
-//	
-//	PRISM is distributed in the hope that it will be useful,
-//	but WITHOUT ANY WARRANTY; without even the implied warranty of
-//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//	GNU General Public License for more details.
-//	
-//	You should have received a copy of the GNU General Public License
-//	along with PRISM; if not, write to the Free Software Foundation,
-//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//	
-//==============================================================================
-
-package userinterface.graph;
-
+//==============================================================================
+//	
+//	Copyright (c) 2002-
+//	Authors:
+//	* Andrew Hinton <ug60axh@cs.bham.ac.uk> (University of Birmingham)
+//	* Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
+//	* Mark Kattenbelt <mark.kattenbelt@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
+//	* Alistair John Strachan <alistair@devzero.co.uk> (University of Edinburgh)
+//	* Mike Arthur <mike@mikearthur.co.uk> (University of Edinburgh)
+//	* Zak Cohen <zakcohen@gmail.com> (University of Edinburgh)
+//	
+//------------------------------------------------------------------------------
+//	
+//	This file is part of PRISM.
+//	
+//	PRISM is free software; you can redistribute it and/or modify
+//	it under the terms of the GNU General Public License as published by
+//	the Free Software Foundation; either version 2 of the License, or
+//	(at your option) any later version.
+//	
+//	PRISM is distributed in the hope that it will be useful,
+//	but WITHOUT ANY WARRANTY; without even the implied warranty of
+//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//	GNU General Public License for more details.
+//	
+//	You should have received a copy of the GNU General Public License
+//	along with PRISM; if not, write to the Free Software Foundation,
+//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//	
+//==============================================================================
+
+package userinterface.graph;
+
 import settings.*;
 
 import java.awt.*;
@@ -45,7 +45,7 @@ import org.jfree.chart.axis.*;
 import org.jfree.data.*;
 import org.jfree.data.xy.*;
 
-import org.w3c.dom.Element;
+import org.w3c.dom.Element;
 
 /**
  * Representation of an axis of a Graph.
@@ -122,19 +122,19 @@ public class AxisSettings extends Observable implements SettingOwner
 		headingFont = new FontColorSetting("heading font", new FontColorPair(new Font("SansSerif", Font.PLAIN, 12), Color.black), "The font for this axis' heading.", this, true);
 		numberFont = new FontColorSetting("numbering font", new FontColorPair(new Font("SansSerif", Font.PLAIN, 12), Color.black), "The font used to number the axis.", this, true);
 		
-		showGrid = new BooleanSetting("show gridlines", new Boolean(true), "Should the gridlines be visible", this, true);
+		showGrid = new BooleanSetting("show gridlines", Boolean.valueOf(true), "Should the gridlines be visible", this, true);
 		gridColour = new ColorSetting("gridline colour", new Color(204,204,204), "The colour of the gridlines", this, true);
 		
 		String[] logarithmicChoices = {"Normal", "Logarithmic"};
 		scaleType = new ChoiceSetting("scale type", logarithmicChoices, logarithmicChoices[0], "Should the scale be normal, or logarithmic", this, true);
-		autoScale = new BooleanSetting("auto-scale", new Boolean(true), "When set to true, all minimum values, maximum values, grid intervals, maximum logarithmic powers and minimum logarithmic powers are automatically set and maintained when the data changes.", this, true);
-		minValue = new DoubleSetting("minimum value", new Double(0.0), "The minimum value for the axis", this, true);
-		maxValue = new DoubleSetting("maximum value", new Double(1.0), "The maximum value for the axis", this, true);
-		gridInterval = new DoubleSetting("gridline interval", new Double(0.2), "The interval between gridlines", this, false, new RangeConstraint(0, Double.POSITIVE_INFINITY, false, true));
-		logBase = new DoubleSetting("log base", new Double(10), "The base for the logarithmic scale", this, false, new RangeConstraint("1,"));
+		autoScale = new BooleanSetting("auto-scale", Boolean.valueOf(true), "When set to true, all minimum values, maximum values, grid intervals, maximum logarithmic powers and minimum logarithmic powers are automatically set and maintained when the data changes.", this, true);
+		minValue = new DoubleSetting("minimum value", Double.valueOf(0.0), "The minimum value for the axis", this, true);
+		maxValue = new DoubleSetting("maximum value", Double.valueOf(1.0), "The maximum value for the axis", this, true);
+		gridInterval = new DoubleSetting("gridline interval", Double.valueOf(0.2), "The interval between gridlines", this, false, new RangeConstraint(0, Double.POSITIVE_INFINITY, false, true));
+		logBase = new DoubleSetting("log base", Double.valueOf(10), "The base for the logarithmic scale", this, false, new RangeConstraint("1,"));
 		
-		minimumPower = new DoubleSetting("minimum power", new Double("0.0"), "The minimum logarithmic power that should be displayed on the scale", this, true);
-		maximumPower = new DoubleSetting("maximum power", new Double("1.0"), "The maximum logarithmic power that should be displayed on the scale", this, true);
+		minimumPower = new DoubleSetting("minimum power", Double.valueOf("0.0"), "The minimum logarithmic power that should be displayed on the scale", this, true);
+		maximumPower = new DoubleSetting("maximum power", Double.valueOf("1.0"), "The maximum logarithmic power that should be displayed on the scale", this, true);
 		
 		String[] logStyleChoices = {"Values", "Base and exponent"};
 		logStyle = new ChoiceSetting("logarithmic number style", logStyleChoices, logStyleChoices[1], "Should the style of the logarithmic scale show the actual values, or the base with the exponent.", this, false);
@@ -146,16 +146,16 @@ public class AxisSettings extends Observable implements SettingOwner
 			{
 				if(activated && d >= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be < Maximum value");
 			}
-			
-			public void checkValueInteger(int i) throws SettingException
-			{
-				if(activated && i >= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be < Maximum value");
-			}
-			
-			public void checkValueLong(long i) throws SettingException
-			{
-				if(activated && i >= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be < Maximum value");
-			}
+			
+			public void checkValueInteger(int i) throws SettingException
+			{
+				if(activated && i >= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be < Maximum value");
+			}
+			
+			public void checkValueLong(long i) throws SettingException
+			{
+				if(activated && i >= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be < Maximum value");
+			}
 		});
 		maxValue.addConstraint(new NumericConstraint()
 		{
@@ -168,11 +168,11 @@ public class AxisSettings extends Observable implements SettingOwner
 			{
 				if(activated && i <= minValue.getDoubleValue()) throw new SettingException("Maximum value should be > Minimum value");
 			}
-			
-			public void checkValueLong(long i) throws SettingException
-			{
-				if(activated && i <= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be > Maximum value");
-			}
+			
+			public void checkValueLong(long i) throws SettingException
+			{
+				if(activated && i <= maxValue.getDoubleValue()) throw new SettingException("Minimum value should be > Maximum value");
+			}
 		});
 		minimumPower.addConstraint(new NumericConstraint()
 		{
@@ -180,16 +180,16 @@ public class AxisSettings extends Observable implements SettingOwner
 			{
 				if(activated && d >= maximumPower.getDoubleValue()) throw new SettingException("Minimum power should be < Maximum power");
 			}
-			
-			public void checkValueInteger(int i) throws SettingException
-			{
-				if(activated && i >= maximumPower.getDoubleValue()) throw new SettingException("Minimum power should be < Maximum power");
-			}
-			
-			public void checkValueLong(long i) throws SettingException
-			{
-				if(activated && i >= maximumPower.getDoubleValue()) throw new SettingException("Minimum power should be < Maximum power");
-			}
+			
+			public void checkValueInteger(int i) throws SettingException
+			{
+				if(activated && i >= maximumPower.getDoubleValue()) throw new SettingException("Minimum power should be < Maximum power");
+			}
+			
+			public void checkValueLong(long i) throws SettingException
+			{
+				if(activated && i >= maximumPower.getDoubleValue()) throw new SettingException("Minimum power should be < Maximum power");
+			}
 		});
 		maximumPower.addConstraint(new NumericConstraint()
 		{
@@ -197,11 +197,11 @@ public class AxisSettings extends Observable implements SettingOwner
 			{
 				if(activated && d <= minimumPower.getDoubleValue()) throw new SettingException("Maximum power should be > Minimum power");
 			}
-			
-			public void checkValueInteger(int i) throws SettingException
-			{
-				if(activated && i <= minimumPower.getDoubleValue()) throw new SettingException("Maximum power should be > Minimum power");
-			}
+			
+			public void checkValueInteger(int i) throws SettingException
+			{
+				if(activated && i <= minimumPower.getDoubleValue()) throw new SettingException("Maximum power should be > Minimum power");
+			}
 			
 			public void checkValueLong(long i) throws SettingException
 			{
@@ -405,7 +405,7 @@ public class AxisSettings extends Observable implements SettingOwner
 	{
 		try
 		{
-			autoScale.setValue(new Boolean(value));
+			autoScale.setValue(Boolean.valueOf(value));
 			doEnables();
 			updateAxis();
 			setChanged();
@@ -436,7 +436,7 @@ public class AxisSettings extends Observable implements SettingOwner
 	{
 		try
 		{
-			showGrid.setValue(new Boolean(value));
+			showGrid.setValue(Boolean.valueOf(value));
 			doEnables();
 			updateAxis();
 			setChanged();
@@ -640,7 +640,7 @@ public class AxisSettings extends Observable implements SettingOwner
 	 */
 	public void setMinimumPower(double value) throws SettingException
 	{		
-		minimumPower.setValue(new Double(value));
+		minimumPower.setValue(Double.valueOf(value));
 		doEnables();
 		updateAxis();
 		setChanged();
@@ -662,7 +662,7 @@ public class AxisSettings extends Observable implements SettingOwner
 	 */
 	public void setMaximumPower(double value) throws SettingException
 	{		
-		maximumPower.setValue(new Double(value));
+		maximumPower.setValue(Double.valueOf(value));
 		doEnables();
 		updateAxis();
 		setChanged();
@@ -927,12 +927,12 @@ public class AxisSettings extends Observable implements SettingOwner
 				if (gridInterval.getDoubleValue() != numAxis.getTickUnit().getSize())
 				{
 					// FIXME: With i.e. interval 0.01 it rounds "0.10" to "0.1"
-					numAxis.setTickUnit(new NumberTickUnit(gridInterval.getDoubleValue()));
+					numAxis.setTickUnit(new NumberTickUnit(gridInterval.getDoubleValue()));
 					// Some experimental code to make axis display only odd numbers:
-					/*if (axisShouldOnlyShowOdd) numAxis.setNumberFormatOverride(new DecimalFormat()
-					{ public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
-							return ((int)number % 2 == 0) ? new StringBuffer("") : super.format(number, toAppendTo, pos);
-					} });*/
+					/*if (axisShouldOnlyShowOdd) numAxis.setNumberFormatOverride(new DecimalFormat()
+					{ public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
+							return ((int)number % 2 == 0) ? new StringBuffer("") : super.format(number, toAppendTo, pos);
+					} });*/
 				}
 			}
 		}
diff --git a/prism/src/userinterface/graph/DisplaySettings.java b/prism/src/userinterface/graph/DisplaySettings.java
index b78d2f6b65115f51f407a52b0d1f4989783bbd32..21a6258b1b4c6674e210e38e72541d59df19838b 100644
--- a/prism/src/userinterface/graph/DisplaySettings.java
+++ b/prism/src/userinterface/graph/DisplaySettings.java
@@ -1,29 +1,29 @@
-//==============================================================================
-//	
-//	Copyright (c) 2002-
-//	Authors:
-//	* Mark Kattenbelt <mark.kattenbelt@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
-//	
-//------------------------------------------------------------------------------
-//	
-//	This file is part of PRISM.
-//	
-//	PRISM is free software; you can redistribute it and/or modify
-//	it under the terms of the GNU General Public License as published by
-//	the Free Software Foundation; either version 2 of the License, or
-//	(at your option) any later version.
-//	
-//	PRISM is distributed in the hope that it will be useful,
-//	but WITHOUT ANY WARRANTY; without even the implied warranty of
-//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//	GNU General Public License for more details.
-//	
-//	You should have received a copy of the GNU General Public License
-//	along with PRISM; if not, write to the Free Software Foundation,
-//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//	
-//==============================================================================
-
+//==============================================================================
+//	
+//	Copyright (c) 2002-
+//	Authors:
+//	* Mark Kattenbelt <mark.kattenbelt@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
+//	
+//------------------------------------------------------------------------------
+//	
+//	This file is part of PRISM.
+//	
+//	PRISM is free software; you can redistribute it and/or modify
+//	it under the terms of the GNU General Public License as published by
+//	the Free Software Foundation; either version 2 of the License, or
+//	(at your option) any later version.
+//	
+//	PRISM is distributed in the hope that it will be useful,
+//	but WITHOUT ANY WARRANTY; without even the implied warranty of
+//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//	GNU General Public License for more details.
+//	
+//	You should have received a copy of the GNU General Public License
+//	along with PRISM; if not, write to the Free Software Foundation,
+//	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//	
+//==============================================================================
+
 package userinterface.graph;
 
 import java.util.Observable;
@@ -39,9 +39,9 @@ import settings.SettingException;
 import settings.SettingOwner;
 
 import java.awt.*;
-
-import javax.swing.UIManager;
-
+
+import javax.swing.UIManager;
+
 /**
  * Representation of the display settings of a Graph.
  * The settings are propagated to the JFreeChart library.
@@ -69,11 +69,11 @@ public class DisplaySettings extends Observable implements SettingOwner
 		this.chart = graph.getChart();
 		this.plot = chart.getXYPlot();
 		
-		antiAlias = new BooleanSetting("anti-aliasing", new Boolean(true), "Should the graph be rendered using anti-aliasing?", this, false);
-		Color defaultColor = Color.white; 
+		antiAlias = new BooleanSetting("anti-aliasing", Boolean.valueOf(true), "Should the graph be rendered using anti-aliasing?", this, false);
+		Color defaultColor = Color.white; 
 		
 		//Color defaultColor =  UIManager.getColor("Panel.background");
-		
+		
 		//if (chart.getBackgroundPaint() instanceof Color)
 		//	defaultColor = ((Color)chart.getBackgroundPaint());
 		
@@ -159,7 +159,7 @@ public class DisplaySettings extends Observable implements SettingOwner
 	{
 		try
 		{
-			antiAlias.setValue(new Boolean(value));
+			antiAlias.setValue(Boolean.valueOf(value));
 			updateDisplay();
 			setChanged();
 			notifyObservers(this);
diff --git a/prism/src/userinterface/graph/Graph.java b/prism/src/userinterface/graph/Graph.java
index 97c31179c4f66ccd20e44fae3ee9969111709095..4249b393407d9c263e6f2ca10ca6c10babcabd6b 100644
--- a/prism/src/userinterface/graph/Graph.java
+++ b/prism/src/userinterface/graph/Graph.java
@@ -643,11 +643,16 @@ public class Graph extends ChartPanel implements SettingOwner, EntityResolver, O
 		polyCache = new HashMap<SeriesKey, LinkedList<double[]>>();
 		keyToSeries = new HashMap<SeriesKey, XYSeries>();
 		keyToGraphSeries = new HashMap<SeriesKey, SeriesSettings>();
-		graphTitle = new MultipleLineStringSetting("title", title, "The main title heading for the chart.", this, false);
-		titleFont = new FontColorSetting("title font", new FontColorPair(new Font("SansSerif", Font.PLAIN, 14), Color.black), "The font for the chart's title",
+		graphTitle = new MultipleLineStringSetting("title", title,
+				"The main title heading for the chart.", this, false);
+		titleFont = new FontColorSetting("title font", new FontColorPair(
+				new Font("SansSerif", Font.PLAIN, 14), Color.black),
+				"The font for the chart's title", this, false);
+		legendVisible = new BooleanSetting(
+				"legend visible?",
+				Boolean.valueOf(true),
+				"Should the legend, which displays all of the series headings, be displayed?",
 				this, false);
-		legendVisible = new BooleanSetting("legend visible?", new Boolean(true),
-				"Should the legend, which displays all of the series headings, be displayed?", this, false);
 
 		String[] choices = { "Left", "Right", "Bottom", "Top" };
 		legendPosition = new ChoiceSetting("legend position", choices, choices[RIGHT], "The position of the legend", this, false);
diff --git a/prism/src/userinterface/graph/PrismLogarithmicAxis.java b/prism/src/userinterface/graph/PrismLogarithmicAxis.java
index 189918f68bf12debe497a279f3f2667135dd1b9c..9fc50a9720e09bd2efdc280ef6261d4cde97c2f8 100644
--- a/prism/src/userinterface/graph/PrismLogarithmicAxis.java
+++ b/prism/src/userinterface/graph/PrismLogarithmicAxis.java
@@ -609,7 +609,7 @@ public class PrismLogarithmicAxis extends ValueAxis {
 
         	double v = calculateValue(current);
             if (range.contains(v)) {
-                ticks.add(new NumberTick(new Double(v), createTickLabel(v), 
+                ticks.add(new NumberTick(Double.valueOf(v), createTickLabel(v), 
                         TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0));
             }
             // add minor ticks (for gridlines)
@@ -618,7 +618,7 @@ public class PrismLogarithmicAxis extends ValueAxis {
             for (int i = 1; i < this.minorTickCount; i++) {
                 double minorV = v + i * ((next - v) / this.minorTickCount);
                 if (range.contains(minorV)) {
-                    ticks.add(new NumberTick(new Double(minorV), 
+                    ticks.add(new NumberTick(Double.valueOf(minorV), 
                         "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0));
                 }
             }
@@ -649,7 +649,7 @@ public class PrismLogarithmicAxis extends ValueAxis {
         	
             double v = calculateValue(current);
             if (range.contains(v)) {
-                ticks.add(new NumberTick(new Double(v), createTickLabel(v), 
+                ticks.add(new NumberTick(Double.valueOf(v), createTickLabel(v), 
                         TextAnchor.CENTER_RIGHT, TextAnchor.CENTER, 0.0));
             }
             // add minor ticks (for gridlines)
@@ -658,7 +658,7 @@ public class PrismLogarithmicAxis extends ValueAxis {
             for (int i = 1; i < this.minorTickCount; i++) {
                 double minorV = v + i * ((next - v) / this.minorTickCount);
                 if (range.contains(minorV)) {
-                    ticks.add(new NumberTick(new Double(minorV), "", 
+                    ticks.add(new NumberTick(Double.valueOf(minorV), "", 
                             TextAnchor.CENTER_RIGHT, TextAnchor.CENTER, 0.0));
                 }
             }
diff --git a/prism/src/userinterface/graph/PrismXYSeries.java b/prism/src/userinterface/graph/PrismXYSeries.java
index 57715085f3afe289ac666cbce8ad7bc6edaf1191..25f814549075fd6f7a936af6f05d5de3115ca934 100644
--- a/prism/src/userinterface/graph/PrismXYSeries.java
+++ b/prism/src/userinterface/graph/PrismXYSeries.java
@@ -116,22 +116,22 @@ public class PrismXYSeries extends XYSeries
 	
 	@Override
 	public void add(double x, double y) {
-		add(new Double(x), new Double(y), true);
+		add(Double.valueOf(x), Double.valueOf(y), true);
 	}
 
 	@Override
 	public void add(double x, double y, boolean notify) {
-		add(new Double(x), new Double(y), notify);
+		add(Double.valueOf(x), Double.valueOf(y), notify);
 	}
 	
 	@Override
 	public void add(double x, Number y) {
-		add(new Double(x), y);
+		add(Double.valueOf(x), y);
 	}
 
 	@Override
 	public void add(double x, Number y, boolean notify) {
-		add(new Double(x), y, notify);
+		add(Double.valueOf(x), y, notify);
 	}
 
 	@Override
diff --git a/prism/src/userinterface/graph/SeriesEditorDialog.java b/prism/src/userinterface/graph/SeriesEditorDialog.java
index 1ea6bd8b0e81ecd1209d9b2d91c4e3902bf6b3c5..4b7d9d92f6840e417a9b16b81300562d0fa03fe5 100644
--- a/prism/src/userinterface/graph/SeriesEditorDialog.java
+++ b/prism/src/userinterface/graph/SeriesEditorDialog.java
@@ -389,9 +389,9 @@ public class SeriesEditorDialog extends JDialog
 								clearBufferRow(bufferIndex);
 								
 								if (columnIndex == 0)
-									SeriesEditor.this.xySeries.addOrUpdate(new Double(value), otherBufferValue);
+									SeriesEditor.this.xySeries.addOrUpdate(Double.valueOf(value), otherBufferValue);
 								else
-									SeriesEditor.this.xySeries.addOrUpdate(otherBufferValue, new Double(value));
+									SeriesEditor.this.xySeries.addOrUpdate(otherBufferValue, Double.valueOf(value));
 							}
 						}
 					}
@@ -418,7 +418,7 @@ public class SeriesEditorDialog extends JDialog
 							
 							Double yValue = SeriesEditor.this.xySeries.getY(rowIndex).doubleValue();
 							SeriesEditor.this.xySeries.remove(rowIndex);
-							SeriesEditor.this.xySeries.addOrUpdate(new Double(value), yValue);							
+							SeriesEditor.this.xySeries.addOrUpdate(Double.valueOf(value), yValue);							
 						}
 											
 						else
diff --git a/prism/src/userinterface/graph/SeriesSettings.java b/prism/src/userinterface/graph/SeriesSettings.java
index b2a770f6b050fd1890e8e6d281e3a1a4166ededb..130fe39f23db25c09a1e67bfe2cf2c56d2410bda 100644
--- a/prism/src/userinterface/graph/SeriesSettings.java
+++ b/prism/src/userinterface/graph/SeriesSettings.java
@@ -152,11 +152,11 @@ public class SeriesSettings extends Observable implements SettingOwner
 		
 		seriesHeading = new SingleLineStringSetting("heading", "heading", "The heading for this series, as displayed in the legend.", this, true);
         seriesColour = new ColorSetting("colour", Color.black, "The colour for all lines and points in this series.", this, true);
-        showPoints = new BooleanSetting("show points", new Boolean(true), "Should points be displayed for this series?", this, true);
+        showPoints = new BooleanSetting("show points", Boolean.valueOf(true), "Should points be displayed for this series?", this, true);
         String[] choices = { "Circle", "Square", "Triangle", "Horizontal Rectangle", "Vertical Rectangle", "None" };
         seriesShape = new ChoiceSetting("point shape", choices, choices[0], "The shape of points for this series.", this, true);
-        showLines = new BooleanSetting("show lines", new Boolean(true), "Should lines be displayed for this series?", this, true);
-        lineWidth = new DoubleSetting("line width",  new Double(1.0), "The line width for this series.", this, true, new RangeConstraint("0.0,"));
+        showLines = new BooleanSetting("show lines", Boolean.valueOf(true), "Should lines be displayed for this series?", this, true);
+        lineWidth = new DoubleSetting("line width",  Double.valueOf(1.0), "The line width for this series.", this, true, new RangeConstraint("0.0,"));
         String [] styles = { "---------", "- - - - -", "- -- - --" };
         lineStyle = new ChoiceSetting("line style", styles, styles[0], "The line style for this series.", this, true);
         
@@ -192,7 +192,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 				{ 
 					// just do it.
 					Boolean pointsVisibleFlag = true;
-					showPoints.setValue(new Boolean(pointsVisibleFlag == null || pointsVisibleFlag.booleanValue())); 
+					showPoints.setValue(Boolean.valueOf(pointsVisibleFlag == null || pointsVisibleFlag.booleanValue())); 
 				}
 				catch (SettingException e)
 				{					
@@ -227,7 +227,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 				try
 				{ 
 					Boolean linesVisibleFlag = true;
-					showLines.setValue(new Boolean(linesVisibleFlag == null || linesVisibleFlag.booleanValue())); 
+					showLines.setValue(Boolean.valueOf(linesVisibleFlag == null || linesVisibleFlag.booleanValue())); 
 				}
 				catch (SettingException e)
 				{					
@@ -371,7 +371,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 	{
 		try
 		{
-			showPoints.setValue(new Boolean(value));
+			showPoints.setValue(Boolean.valueOf(value));
 			updateSeries();
 			setChanged();
 			notifyObservers(this);
@@ -397,7 +397,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 	 */
 	public void setSeriesShape(int value) throws SettingException
 	{
-		seriesShape.setSelectedIndex(new Integer(value));
+		seriesShape.setSelectedIndex(Integer.valueOf(value));
 		updateSeries();
 		setChanged();
 		notifyObservers(this);		
@@ -420,7 +420,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 	{
 		try
 		{
-			showLines.setValue(new Boolean(value));
+			showLines.setValue(Boolean.valueOf(value));
 			updateSeries();
 			setChanged();
 			notifyObservers(this);
@@ -446,7 +446,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 	 */
 	public void setLineWidth(double value) throws SettingException
 	{
-		lineWidth.setValue(new Double(value));
+		lineWidth.setValue(Double.valueOf(value));
 		updateSeries();
 		setChanged();
 		notifyObservers();					
@@ -467,7 +467,7 @@ public class SeriesSettings extends Observable implements SettingOwner
 	 */
 	public void setLineStyle(int value) throws SettingException
 	{
-		lineStyle.setSelectedIndex(new Integer(value));
+		lineStyle.setSelectedIndex(Integer.valueOf(value));
 		updateSeries();
 		setChanged();
 		notifyObservers(this);		
diff --git a/prism/src/userinterface/log/GUILog.java b/prism/src/userinterface/log/GUILog.java
index f9d298076f423a330d3d1affdfe6ccc64118e646..78cf1db40ca1f78662123356d12fbce90c768c71 100644
--- a/prism/src/userinterface/log/GUILog.java
+++ b/prism/src/userinterface/log/GUILog.java
@@ -212,7 +212,7 @@ public class GUILog extends GUIPlugin implements MouseListener, PrismSettingsLis
 			}
 		};
 		clearAction.putValue(Action.SHORT_DESCRIPTION, "Clear log");
-		clearAction.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
+		clearAction.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_C));
 		clearAction.putValue(Action.NAME, "Clear log");
 		clearAction.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallDelete.png"));
 		
@@ -239,7 +239,7 @@ public class GUILog extends GUIPlugin implements MouseListener, PrismSettingsLis
 			}
 		};
 		saveAction.putValue(Action.SHORT_DESCRIPTION, "Save log as...");
-		saveAction.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		saveAction.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		saveAction.putValue(Action.NAME, "Save log as...");
 		saveAction.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallSave.png"));
 		
diff --git a/prism/src/userinterface/model/GUIMultiModel.java b/prism/src/userinterface/model/GUIMultiModel.java
index eff33e789fd8618abebe7aecb832d3ca59ffedbb..2efe40077d44ea37d45e2397d3bdec5c1c828f66 100644
--- a/prism/src/userinterface/model/GUIMultiModel.java
+++ b/prism/src/userinterface/model/GUIMultiModel.java
@@ -504,7 +504,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		newPRISMModel.putValue(Action.LONG_DESCRIPTION, "Removes the current build, and loads a new model editor in PRISM Text Model mode.");
 		//newPRISMModel.putValue(Action.SHORT_DESCRIPTION, "New PRISM Text Model");
-		newPRISMModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		newPRISMModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		newPRISMModel.putValue(Action.NAME, "PRISM model");
 		newPRISMModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFilePrism.png"));
 		newPRISMModel.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
@@ -519,7 +519,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		newPEPAModel.putValue(Action.LONG_DESCRIPTION, "Removes the current build, and loads a new model editor in PEPA Text Model mode.");
 		//newPEPAModel.putValue(Action.SHORT_DESCRIPTION, "New PEPA Text Model");
-		newPEPAModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_E));
+		newPEPAModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_E));
 		newPEPAModel.putValue(Action.NAME, "PEPA model");
 		newPEPAModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFilePepa.png"));
 
@@ -538,7 +538,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 						Action.LONG_DESCRIPTION,
 						"Brings up a file loading dialogue and loads the file into the editor.  The editor will change mode according to the format of the file.  The loaded file is active for saving.");
 		//loadModel.putValue(Action.SHORT_DESCRIPTION, "Open Model");
-		loadModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+		loadModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
 		loadModel.putValue(Action.NAME, "Open model...");
 		//loadModel.putValue(Action.ACTION_COMMAND_KEY,
 		loadModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallOpen.png"));
@@ -553,7 +553,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		reloadModel.putValue(Action.LONG_DESCRIPTION, "Reloads the current active file.");
 		//reloadModel.putValue(Action.SHORT_DESCRIPTION, "Reload Model");
-		reloadModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		reloadModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		reloadModel.putValue(Action.NAME, "Reload model");
 		//loadModel.putValue(Action.ACTION_COMMAND_KEY,
 		reloadModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallReload.png"));
@@ -569,7 +569,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		saveModel.putValue(Action.LONG_DESCRIPTION,
 				"Brings up a file saving dialogue and saves the current text editor to the active file or to a new file.  The saved file becomes active");
 		//saveModel.putValue(Action.SHORT_DESCRIPTION, "Save Model");
-		saveModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		saveModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		saveModel.putValue(Action.NAME, "Save model");
 		saveModel.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
 		//saveModel.putValue(Action.ACTION_COMMAND_KEY,
@@ -585,7 +585,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		saveAsModel.putValue(Action.LONG_DESCRIPTION,
 				"Brings up a file saving dialogue and saves the current text editor to a new file.  The saved file becomes active");
 		//saveAsModel.putValue(Action.SHORT_DESCRIPTION, "Save Model As...");
-		saveAsModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_A));
+		saveAsModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_A));
 		saveAsModel.putValue(Action.NAME, "Save model as...");
 		//saveAsModel.putValue(Action.ACTION_COMMAND_KEY,
 		saveAsModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallSaveAs.png"));
@@ -599,7 +599,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		parseModel.putValue(Action.LONG_DESCRIPTION, "Forces a parse of the model in the editor.  The parsed description is shown in the model tree.");
 		//parseModel.putValue(Action.SHORT_DESCRIPTION, "Parse Model");
-		parseModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		parseModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		parseModel.putValue(Action.NAME, "Parse model");
 		parseModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallParse.png"));
 		parseModel.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
@@ -612,7 +612,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		buildModel.putValue(Action.LONG_DESCRIPTION, "Builds the model that has been parsed.");
 		//buildModel.putValue(Action.SHORT_DESCRIPTION, "Build Model");
-		buildModel.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_B));
+		buildModel.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_B));
 		buildModel.putValue(Action.NAME, "Build model");
 		buildModel.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallBuild.png"));
 		buildModel.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
@@ -625,7 +625,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportStatesPlain.putValue(Action.LONG_DESCRIPTION, "Exports the reachable states to a plain text file");
-		exportStatesPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportStatesPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportStatesPlain.putValue(Action.NAME, "Plain text file");
 		exportStatesPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -637,7 +637,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportStatesMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the reachable states to a Matlab file");
-		exportStatesMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportStatesMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportStatesMatlab.putValue(Action.NAME, "Matlab file");
 		exportStatesMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -649,7 +649,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransPlain.putValue(Action.LONG_DESCRIPTION, "Exports the transition matrix to a plain text file");
-		exportTransPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportTransPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportTransPlain.putValue(Action.NAME, "Plain text file");
 		exportTransPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -661,7 +661,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the transition matrix to a Matlab file");
-		exportTransMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportTransMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportTransMatlab.putValue(Action.NAME, "Matlab file");
 		exportTransMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -673,7 +673,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransDot.putValue(Action.LONG_DESCRIPTION, "Exports the transition matrix graph to a Dot file");
-		exportTransDot.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
+		exportTransDot.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
 		exportTransDot.putValue(Action.NAME, "Dot file");
 		exportTransDot.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileDot.png"));
 
@@ -685,7 +685,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransDotStates.putValue(Action.LONG_DESCRIPTION, "Exports the transition matrix graph to a Dot file (with states)");
-		exportTransDotStates.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		exportTransDotStates.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		exportTransDotStates.putValue(Action.NAME, "Dot file (with states)");
 		exportTransDotStates.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileDot.png"));
 
@@ -697,7 +697,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransMRMC.putValue(Action.LONG_DESCRIPTION, "Exports the transition matrix to a MRMC file");
-		exportTransMRMC.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		exportTransMRMC.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		exportTransMRMC.putValue(Action.NAME, "MRMC file");
 		exportTransMRMC.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -709,7 +709,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportStateRewardsPlain.putValue(Action.LONG_DESCRIPTION, "Exports the state rewards vector to a plain text file");
-		exportStateRewardsPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportStateRewardsPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportStateRewardsPlain.putValue(Action.NAME, "Plain text file");
 		exportStateRewardsPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -721,7 +721,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportStateRewardsMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the state rewards vector to a Matlab file");
-		exportStateRewardsMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportStateRewardsMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportStateRewardsMatlab.putValue(Action.NAME, "Matlab file");
 		exportStateRewardsMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -733,7 +733,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportStateRewardsMRMC.putValue(Action.LONG_DESCRIPTION, "Exports the state rewards vector graph to a MRMC file");
-		exportStateRewardsMRMC.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		exportStateRewardsMRMC.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		exportStateRewardsMRMC.putValue(Action.NAME, "MRMC file");
 		exportStateRewardsMRMC.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -745,7 +745,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransRewardsPlain.putValue(Action.LONG_DESCRIPTION, "Exports the transition rewards matrix to a plain text file");
-		exportTransRewardsPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportTransRewardsPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportTransRewardsPlain.putValue(Action.NAME, "Plain text file");
 		exportTransRewardsPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -757,7 +757,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransRewardsMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the transition rewards matrix to a Matlab file");
-		exportTransRewardsMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportTransRewardsMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportTransRewardsMatlab.putValue(Action.NAME, "Matlab file");
 		exportTransRewardsMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -769,7 +769,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTransRewardsMRMC.putValue(Action.LONG_DESCRIPTION, "Exports the transition rewards matrix to a MRMC file");
-		exportTransRewardsMRMC.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		exportTransRewardsMRMC.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		exportTransRewardsMRMC.putValue(Action.NAME, "MRMC file");
 		exportTransRewardsMRMC.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -781,7 +781,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportLabelsPlain.putValue(Action.LONG_DESCRIPTION, "Exports the model's labels and their satisfying states to a plain text file");
-		exportLabelsPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportLabelsPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportLabelsPlain.putValue(Action.NAME, "Plain text file");
 		exportLabelsPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -793,7 +793,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportLabelsMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the model's labels and their satisfying states to a Matlab file");
-		exportLabelsMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportLabelsMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportLabelsMatlab.putValue(Action.NAME, "Matlab file");
 		exportLabelsMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -806,7 +806,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		computeSS.putValue(Action.LONG_DESCRIPTION, "Computes steady-state probabilities for the model");
 		//computeSS.putValue(Action.SHORT_DESCRIPTION, "Compute steady-state probabilities");
-		computeSS.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		computeSS.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		computeSS.putValue(Action.NAME, "Steady-state probabilities");
 		computeSS.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallSteadyState.png"));
 		computeSS.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0));
@@ -820,7 +820,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 		};
 		computeTr.putValue(Action.LONG_DESCRIPTION, "Computes transient probabilities for the model");
 		//computeTr.putValue(Action.SHORT_DESCRIPTION, "Compute transient probabilities");
-		computeTr.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_T));
+		computeTr.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_T));
 		computeTr.putValue(Action.NAME, "Transient probabilities");
 		computeTr.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallClockAnim1.png"));
 		computeTr.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.CTRL_DOWN_MASK));
@@ -833,7 +833,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportSSPlain.putValue(Action.LONG_DESCRIPTION, "Exports the steady-state probabilities to a plain text file");
-		exportSSPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportSSPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportSSPlain.putValue(Action.NAME, "Plain text file");
 		exportSSPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -845,7 +845,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportSSMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the steady-state probabilities to a Matlab file");
-		exportSSMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportSSMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportSSMatlab.putValue(Action.NAME, "Matlab file");
 		exportSSMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -857,7 +857,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTrPlain.putValue(Action.LONG_DESCRIPTION, "Exports the transient probabilities to a plain text file");
-		exportTrPlain.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportTrPlain.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportTrPlain.putValue(Action.NAME, "Plain text file");
 		exportTrPlain.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileText.png"));
 
@@ -869,7 +869,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		exportTrMatlab.putValue(Action.LONG_DESCRIPTION, "Exports the transient probabilities to a Matlab file");
-		exportTrMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportTrMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportTrMatlab.putValue(Action.NAME, "Matlab file");
 		exportTrMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 
@@ -881,7 +881,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewStates.putValue(Action.LONG_DESCRIPTION, "Print the reachable states to the log");
-		viewStates.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		viewStates.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		viewStates.putValue(Action.NAME, "States");
 		viewStates.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallStates.png"));
 
@@ -893,7 +893,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewTrans.putValue(Action.LONG_DESCRIPTION, "Print the transition matrix to the log");
-		viewTrans.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_T));
+		viewTrans.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_T));
 		viewTrans.putValue(Action.NAME, "Transition matrix");
 		viewTrans.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallMatrix.png"));
 
@@ -905,7 +905,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewStateRewards.putValue(Action.LONG_DESCRIPTION, "Print the state rewards to the log");
-		viewStateRewards.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		viewStateRewards.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		viewStateRewards.putValue(Action.NAME, "State rewards");
 		viewStateRewards.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallStates.png"));
 
@@ -917,7 +917,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewTransRewards.putValue(Action.LONG_DESCRIPTION, "Print the transition rewards to the log");
-		viewTransRewards.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_E));
+		viewTransRewards.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_E));
 		viewTransRewards.putValue(Action.NAME, "Transition rewards");
 		viewTransRewards.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallMatrix.png"));
 
@@ -929,7 +929,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewLabels.putValue(Action.LONG_DESCRIPTION, "Print the labels and satisfying states to the log");
-		viewLabels.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_L));
+		viewLabels.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_L));
 		viewLabels.putValue(Action.NAME, "Labels");
 		viewLabels.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallStates.png"));
 
@@ -941,7 +941,7 @@ public class GUIMultiModel extends GUIPlugin implements PrismSettingsListener
 			}
 		};
 		viewPrismCode.putValue(Action.LONG_DESCRIPTION, "This shows the parsed model in a text editor.");
-		viewPrismCode.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_V));
+		viewPrismCode.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_V));
 		viewPrismCode.putValue(Action.NAME, "Parsed PRISM model");
 		viewPrismCode.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFilePrism.png"));
 	}
diff --git a/prism/src/userinterface/model/GUIMultiModelTree.java b/prism/src/userinterface/model/GUIMultiModelTree.java
index e3133b2dc3ea1fa2c8f08a35266298862cbe775c..aa61021933feb26ff3f4ae941ba8f49800c33d82 100644
--- a/prism/src/userinterface/model/GUIMultiModelTree.java
+++ b/prism/src/userinterface/model/GUIMultiModelTree.java
@@ -1289,7 +1289,7 @@ public class GUIMultiModelTree extends JPanel implements MouseListener
 			}
 		};
 		addModule.putValue(Action.LONG_DESCRIPTION, "Adds an editable module to the tree");
-		addModule.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_A));
+		addModule.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_A));
 		addModule.putValue(Action.NAME, "Add Module");
 		moduleCollectionPopup.add(addModule);
 
diff --git a/prism/src/userinterface/model/GUITextModelEditor.java b/prism/src/userinterface/model/GUITextModelEditor.java
index c34085e51aba8f3624ffe1e4b186a857aa480b5d..118d66f9245742acc11b2e8aba059c83dfbec222 100644
--- a/prism/src/userinterface/model/GUITextModelEditor.java
+++ b/prism/src/userinterface/model/GUITextModelEditor.java
@@ -201,7 +201,7 @@ public class GUITextModelEditor extends GUIModelEditor implements DocumentListen
 				GUITextModelEditor.this.handler.getGUIPlugin().getSelectionChangeHandler().notifyListeners(new GUIEvent(1));				
 			}
 		}); 
-		editor.getDocument().putProperty( PlainDocument.tabSizeAttribute, new Integer(4) );
+		editor.getDocument().putProperty( PlainDocument.tabSizeAttribute, Integer.valueOf(4) );
 		
 		editor.addMouseListener(this);
 		errorHighlightPainter = new DefaultHighlighter.DefaultHighlightPainter(new Color(255,192,192));
diff --git a/prism/src/userinterface/model/GUITextModelEditorGutter.java b/prism/src/userinterface/model/GUITextModelEditorGutter.java
index a45c5daf16d1559d13270915af13657e9ba896e1..d5ed2f5840cc55e37ba1ae87665329b974cf81f3 100644
--- a/prism/src/userinterface/model/GUITextModelEditorGutter.java
+++ b/prism/src/userinterface/model/GUITextModelEditorGutter.java
@@ -184,7 +184,7 @@ public class GUITextModelEditorGutter extends JPanel implements PropertyChangeLi
 			
 			if (errorMessages != null)
 			{
-				if (errorMessages.containsKey(new Integer(i+1)))
+				if (errorMessages.containsKey(Integer.valueOf(i+1)))
 					isErrorLine = true;
 			}
 			
diff --git a/prism/src/userinterface/properties/GUIGraphHandler.java b/prism/src/userinterface/properties/GUIGraphHandler.java
index f0de1e18766c8d85962503f94465857748bd6f90..ffd050afbfd025ca76d34739c03997e56bcbc74e 100644
--- a/prism/src/userinterface/properties/GUIGraphHandler.java
+++ b/prism/src/userinterface/properties/GUIGraphHandler.java
@@ -120,7 +120,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 		};
 
 		graphOptions.putValue(Action.NAME, "Graph options");
-		graphOptions.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_G));
+		graphOptions.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_G));
 		graphOptions.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallOptions.png"));
 		graphOptions.putValue(Action.LONG_DESCRIPTION, "Displays the options dialog for the graph.");
 
@@ -134,7 +134,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 		};
 
 		zoomIn.putValue(Action.NAME, "In");
-		zoomIn.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_I));
+		zoomIn.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_I));
 		zoomIn.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerFwd.png"));
 		zoomIn.putValue(Action.LONG_DESCRIPTION, "Zoom in on the graph.");
 
@@ -148,7 +148,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 		};
 
 		zoomOut.putValue(Action.NAME, "Out");
-		zoomOut.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+		zoomOut.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
 		zoomOut.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerRew.png"));
 		zoomOut.putValue(Action.LONG_DESCRIPTION, "Zoom out of the graph.");
 
@@ -162,7 +162,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 		};
 
 		zoomDefault.putValue(Action.NAME, "Default");
-		zoomDefault.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
+		zoomDefault.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
 		zoomDefault.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerStart.png"));
 		zoomDefault.putValue(Action.LONG_DESCRIPTION, "Set the default zoom for the graph.");
 
@@ -181,7 +181,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		importXML.putValue(Action.NAME, "PRISM graph (*.gra)");
-		importXML.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_I));
+		importXML.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_I));
 		importXML.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileGraph.png"));
 		importXML.putValue(Action.LONG_DESCRIPTION, "Imports a saved PRISM graph from a file.");
 
@@ -200,7 +200,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		exportXML.putValue(Action.NAME, "PRISM graph (*.gra)");
-		exportXML.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X));
+		exportXML.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_X));
 		exportXML.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileGraph.png"));
 		exportXML.putValue(Action.LONG_DESCRIPTION, "Export graph as a PRISM graph file.");
 
@@ -214,7 +214,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		exportImageJPG.putValue(Action.NAME, "JPEG Interchange Format (*.jpg, *.jpeg)");
-		exportImageJPG.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_J));
+		exportImageJPG.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_J));
 		exportImageJPG.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileImage.png"));
 		exportImageJPG.putValue(Action.LONG_DESCRIPTION, "Export graph as a JPEG file.");
 
@@ -228,7 +228,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		exportImagePNG.putValue(Action.NAME, "Portable Network Graphics (*.png)");
-		exportImagePNG.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		exportImagePNG.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		exportImagePNG.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileImage.png"));
 		exportImagePNG.putValue(Action.LONG_DESCRIPTION, "Export graph as a Portable Network Graphics file.");
 
@@ -242,7 +242,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		exportImageEPS.putValue(Action.NAME, "Encapsulated PostScript (*.eps)");
-		exportImageEPS.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_E));
+		exportImageEPS.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_E));
 		exportImageEPS.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFilePdf.png"));
 		exportImageEPS.putValue(Action.LONG_DESCRIPTION, "Export graph as an Encapsulated PostScript file.");
 
@@ -262,7 +262,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		exportMatlab.putValue(Action.NAME, "Matlab file (*.m)");
-		exportMatlab.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_M));
+		exportMatlab.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_M));
 		exportMatlab.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileMatlab.png"));
 		exportMatlab.putValue(Action.LONG_DESCRIPTION, "Export graph as a Matlab file.");
 
@@ -283,7 +283,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		printGraph.putValue(Action.NAME, "Print graph");
-		printGraph.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		printGraph.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		printGraph.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPrint.png"));
 		printGraph.putValue(Action.LONG_DESCRIPTION, "Print the graph to a printer or file");
 
@@ -303,7 +303,7 @@ public class GUIGraphHandler extends JPanel implements MouseListener
 			}
 		};
 		deleteGraph.putValue(Action.NAME, "Delete graph");
-		deleteGraph.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_D));
+		deleteGraph.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_D));
 		deleteGraph.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallDelete.png"));
 		deleteGraph.putValue(Action.LONG_DESCRIPTION, "Deletes the graph.");
 
diff --git a/prism/src/userinterface/simulator/GUIInitialStatePicker.java b/prism/src/userinterface/simulator/GUIInitialStatePicker.java
index 9cf5e18c95cb7a5e385027fbff645e941436edb1..f538494c93e8f1146eb03b56a4668b19639124e7 100644
--- a/prism/src/userinterface/simulator/GUIInitialStatePicker.java
+++ b/prism/src/userinterface/simulator/GUIInitialStatePicker.java
@@ -288,9 +288,9 @@ public class GUIInitialStatePicker extends javax.swing.JDialog implements KeyLis
 					String bool = initValuesModel.getValue(i).value.toString();
 					if (!(bool.equals("true") || bool.equals("false")))
 						throw new NumberFormatException();
-					parameterValue = new Boolean(bool);
+					parameterValue = Boolean.valueOf(bool);
 				} else if (initValuesModel.getValue(i).type instanceof TypeInt) {
-					parameterValue = new Integer(initValuesModel.getValue(i).value.toString());
+					parameterValue = Integer.valueOf(initValuesModel.getValue(i).value.toString());
 				} else {
 					throw new NumberFormatException();
 				}
diff --git a/prism/src/userinterface/simulator/GUISimulator.java b/prism/src/userinterface/simulator/GUISimulator.java
index 48026f28402998ee52de4ec298f1a7855cda58ba..025546d6f4e0a8c60b3433fedf5d8da09f892ae1 100644
--- a/prism/src/userinterface/simulator/GUISimulator.java
+++ b/prism/src/userinterface/simulator/GUISimulator.java
@@ -1768,7 +1768,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 		};
 
 		newPath.putValue(Action.LONG_DESCRIPTION, "Creates a new path.");
-		newPath.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
+		newPath.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_N));
 		newPath.putValue(Action.NAME, "New path");
 		newPath.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallStates.png"));
 		newPath.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0));
@@ -1783,7 +1783,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 		};
 
 		newPathFromState.putValue(Action.LONG_DESCRIPTION, "Creates a new path from a chosen state.");
-		//newPathFromState.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
+		//newPathFromState.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_N));
 		newPathFromState.putValue(Action.NAME, "New path from state");
 		newPathFromState.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallStates.png"));
 
@@ -1795,7 +1795,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		newPathPlot.putValue(Action.LONG_DESCRIPTION, "Creates and plots a new path.");
-		//newPathPlot.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
+		//newPathPlot.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_N));
 		newPathPlot.putValue(Action.NAME, "Plot new path");
 		newPathPlot.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileGraph.png"));
 		newPathPlot.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F8, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
@@ -1808,7 +1808,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		newPathPlotFromState.putValue(Action.LONG_DESCRIPTION, "Creates and plots a new path from a chosen state.");
-		//newPathPlotFromState.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
+		//newPathPlotFromState.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_N));
 		newPathPlotFromState.putValue(Action.NAME, "Plot new path from state");
 		newPathPlotFromState.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileGraph.png"));
 
@@ -1821,7 +1821,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 		};
 
 		resetPath.putValue(Action.LONG_DESCRIPTION, "Resets the path.");
-		resetPath.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		resetPath.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		resetPath.putValue(Action.NAME, "Reset path");
 		resetPath.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerStart.png"));
 		resetPath.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F8, InputEvent.SHIFT_DOWN_MASK));
@@ -1835,7 +1835,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 		};
 
 		exportPath.putValue(Action.LONG_DESCRIPTION, "Exports the path.");
-		exportPath.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_X));
+		exportPath.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_X));
 		exportPath.putValue(Action.NAME, "Export path");
 		exportPath.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallExport.png"));
 
@@ -1847,7 +1847,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		plotPath.putValue(Action.LONG_DESCRIPTION, "Plots the path on a graph.");
-		plotPath.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
+		plotPath.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_P));
 		plotPath.putValue(Action.NAME, "Plot path");
 		plotPath.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallFileGraph.png"));
 
@@ -1859,7 +1859,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		randomExploration.putValue(Action.LONG_DESCRIPTION, "Extends the path by simulating.");
-		randomExploration.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
+		randomExploration.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_S));
 		randomExploration.putValue(Action.NAME, "Simulate");
 		randomExploration.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerFwd.png"));
 		randomExploration.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0));
@@ -1872,7 +1872,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		backtrack.putValue(Action.LONG_DESCRIPTION, "Backtracks the path.");
-		backtrack.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_K));
+		backtrack.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_K));
 		backtrack.putValue(Action.NAME, "Backtrack");
 		backtrack.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerRew.png"));
 		backtrack.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.SHIFT_DOWN_MASK));
@@ -1885,7 +1885,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		backtrackToHere.putValue(Action.LONG_DESCRIPTION, "Backtracks the path to the selected state.");
-		backtrackToHere.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_B));
+		backtrackToHere.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_B));
 		backtrackToHere.putValue(Action.NAME, "Backtrack to here");
 		backtrackToHere.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallPlayerRew.png"));
 
@@ -1902,7 +1902,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		removeToHere.putValue(Action.LONG_DESCRIPTION, "Removes states preceding the selected state from the path.");
-		removeToHere.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+		removeToHere.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R));
 		removeToHere.putValue(Action.NAME, "Remove preceding steps");
 		removeToHere.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallRemove.png"));
 
@@ -1914,7 +1914,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 			}
 		};
 		configureView.putValue(Action.LONG_DESCRIPTION, "Configures the view.");
-		configureView.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
+		configureView.putValue(Action.MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_C));
 		configureView.putValue(Action.NAME, "Configure view");
 		configureView.putValue(Action.SMALL_ICON, GUIPrism.getIconFromImage("smallOptions.png"));
 
@@ -2214,7 +2214,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 	public void setDisplayStyle(boolean displayStyle) throws PrismException
 	{
 
-		getPrism().getSettings().set(PrismSettings.SIMULATOR_RENDER_ALL_VALUES, new Integer(displayStyle ? 0 : 1));
+		getPrism().getSettings().set(PrismSettings.SIMULATOR_RENDER_ALL_VALUES, Integer.valueOf(displayStyle ? 0 : 1));
 
 	}
 
@@ -2234,7 +2234,7 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 	public void setDisplayPathLoops(boolean b) throws PrismException
 	{
 		displayPathLoops = b;
-		//getPrism().getSettings().set(PrismSettings.SIMULATOR_????, new Boolean(b));
+		//getPrism().getSettings().set(PrismSettings.SIMULATOR_????, Boolean.valueOf(b));
 	}
 
 	public ModulesFile getModulesFile()
@@ -2557,11 +2557,10 @@ public class GUISimulator extends GUIPlugin implements MouseListener, ListSelect
 	}
 
 	/**
-	 * Utility function to format floating point numbers.
+	 * Utility function to format floating point numbers with a precision of 6 decimal places.
 	 */
 	public String formatDouble(double d)
 	{
-		// getPrism().getSettings().
 		return PrismUtils.formatDouble(6, d);
 	}
 
diff --git a/prism/src/userinterface/util/BooleanProperty.java b/prism/src/userinterface/util/BooleanProperty.java
index dc7c2f2ed08e77345518de4302e50dae566d66a7..839336ec77b586c5b16e8a45f9d9974d504ce82a 100644
--- a/prism/src/userinterface/util/BooleanProperty.java
+++ b/prism/src/userinterface/util/BooleanProperty.java
@@ -45,7 +45,7 @@ public class BooleanProperty extends SingleProperty
     
     public BooleanProperty(PropertyOwner owner,String name, boolean property, String comment)
     {
-        super(owner, name, new Boolean(property), "", false, comment);
+        super(owner, name, Boolean.valueOf(property), "", false, comment);
         //FlowLayout fl = new FlowLayout(FlowLayout.CENTER, 0, 0);
         pan.setLayout(new GridBagLayout());
         renderer.setBorder(null);
@@ -56,7 +56,7 @@ public class BooleanProperty extends SingleProperty
     {
         try
         {
-            setProperty(new Boolean(property));
+            setProperty(Boolean.valueOf(property));
         }
         catch(PropertyException e)
         {
@@ -77,7 +77,7 @@ public class BooleanProperty extends SingleProperty
         else if(property instanceof String)
         {
             if(property.toString().equals("true") || property.toString().equals("false"))
-                super.setProperty(new Boolean(Boolean.getBoolean((String)property)));
+                super.setProperty(Boolean.valueOf(Boolean.getBoolean((String)property)));
             return;
             
         }
@@ -229,7 +229,7 @@ public class BooleanProperty extends SingleProperty
     public Boolean getEditorValue()
     {
         
-        return new Boolean(renderer.isSelected()); 
+        return Boolean.valueOf(renderer.isSelected()); 
     }
     
     public void addListenerToEditor(ActionListener e)
diff --git a/prism/src/userinterface/util/DoubleProperty.java b/prism/src/userinterface/util/DoubleProperty.java
index 6af64529534040612a9948dc07c0287fa009afe2..82d07f55f9aca93b5b11056c279d6b885895810e 100644
--- a/prism/src/userinterface/util/DoubleProperty.java
+++ b/prism/src/userinterface/util/DoubleProperty.java
@@ -37,7 +37,7 @@ public class DoubleProperty extends SingleProperty
     
     public DoubleProperty(PropertyOwner owner,String name, double property, String comment)
     {
-        super(owner,name, new Double(property), "", false, comment);
+        super(owner,name, Double.valueOf(property), "", false, comment);
     }
     
     public void setValue(double property)
@@ -49,7 +49,7 @@ public class DoubleProperty extends SingleProperty
     {
         try
         {
-            setProperty(new Double(property), notifyObservers);
+            setProperty(Double.valueOf(property), notifyObservers);
         }
         catch(PropertyException e)
         {
@@ -76,7 +76,7 @@ public class DoubleProperty extends SingleProperty
             try
             {
             
-                super.setProperty(new Double(Double.parseDouble((String)property)), notifyObservers);
+                super.setProperty(Double.valueOf(Double.parseDouble((String)property)), notifyObservers);
                 return;
             }
             catch(NumberFormatException e)
diff --git a/prism/src/userinterface/util/GUIGroupedTableColumnModel.java b/prism/src/userinterface/util/GUIGroupedTableColumnModel.java
index 4542f2697724b7f800a51088bb913612c4546fde..08c92f4847f39f5bdb890d5e469f634b40397091 100644
--- a/prism/src/userinterface/util/GUIGroupedTableColumnModel.java
+++ b/prism/src/userinterface/util/GUIGroupedTableColumnModel.java
@@ -76,7 +76,7 @@ public class GUIGroupedTableColumnModel extends DefaultTableColumnModel implemen
 			this.addColumn(elementColumns.get(i));
 		}
 
-		lastColumn.add(new Integer(this.getColumnCount() - 1));
+		lastColumn.add(Integer.valueOf(this.getColumnCount() - 1));
 		updateGroups();
 	}
 
diff --git a/prism/src/userinterface/util/GUILogEvent.java b/prism/src/userinterface/util/GUILogEvent.java
index 622243cd417d1532d6c2df5c522a3327a5a92f7e..2c3d6b8ba387bfc07dd1e032fbd27ed6bc607701 100644
--- a/prism/src/userinterface/util/GUILogEvent.java
+++ b/prism/src/userinterface/util/GUILogEvent.java
@@ -45,37 +45,37 @@ public class GUILogEvent extends GUIEvent
     
     public GUILogEvent(int type, int message)
     {
-        super(type, new Integer(message));
+        super(type, Integer.valueOf(message));
     }
     
     public GUILogEvent(int type, double message)
     {
-        super(type, new Double(message));
+        super(type, Double.valueOf(message));
     }
     
     public GUILogEvent(int type, float message)
     {
-        super(type, new Float(message));
+        super(type, Float.valueOf(message));
     }
     
     public GUILogEvent(int type, long message)
     {
-        super(type, new Long(message));
+        super(type, Long.valueOf(message));
     }
     
     public GUILogEvent(int type, short message)
     {
-        super(type, new Short(message));
+        super(type, Short.valueOf(message));
     }
     
     public GUILogEvent(int type, byte message)
     {
-        super(type, new Byte(message));
+        super(type, Byte.valueOf(message));
     }
     
     public GUILogEvent(int type, boolean message)
     {
-        super(type, new Boolean(message));
+        super(type, Boolean.valueOf(message));
     }
     
     public GUILogEvent(Object message)
@@ -85,36 +85,36 @@ public class GUILogEvent extends GUIEvent
     
     public GUILogEvent(int message)
     {
-        this(PRINTLN, new Integer(message));
+        this(PRINTLN, Integer.valueOf(message));
     }
     
     public GUILogEvent(double message)
     {
-        this(PRINTLN, new Double(message));
+        this(PRINTLN, Double.valueOf(message));
     }
     
     public GUILogEvent(float message)
     {
-        this(PRINTLN, new Float(message));
+        this(PRINTLN, Float.valueOf(message));
     }
     
     public GUILogEvent(long message)
     {
-        this(PRINTLN, new Long(message));
+        this(PRINTLN, Long.valueOf(message));
     }
     
     public GUILogEvent(short message)
     {
-        this(PRINTLN, new Short(message));
+        this(PRINTLN, Short.valueOf(message));
     }
     
     public GUILogEvent(byte message)
     {
-        this(PRINTLN, new Byte(message));
+        this(PRINTLN, Byte.valueOf(message));
     }
     
     public GUILogEvent(boolean message)
     {
-        this(PRINTLN, new Boolean(message));
+        this(PRINTLN, Boolean.valueOf(message));
     }
 }
diff --git a/prism/src/userinterface/util/PropertyTableModel.java b/prism/src/userinterface/util/PropertyTableModel.java
index 71d399c9a08610e118191809a6d036e190a01348..364ef9a6e413fb44725ce5dae7ae704b8d741d10 100644
--- a/prism/src/userinterface/util/PropertyTableModel.java
+++ b/prism/src/userinterface/util/PropertyTableModel.java
@@ -91,7 +91,7 @@ public class PropertyTableModel extends AbstractTableModel implements Observer
 				if (!po.getDescriptor().equals(""))
 					ownerList += "\'" + po.getDescriptor() + "\'";
 				tempName = po.getClassDescriptor();//+" \'"+po.getDescriptor()+"\'";
-				groupStarts.add(new Integer(0));
+				groupStarts.add(Integer.valueOf(0));
 			} else if (po.getUniquePropertyID() == last.getUniquePropertyID()) {
 				//this is for the second or after in the sequence
 				currGroupCount++;
@@ -105,13 +105,13 @@ public class PropertyTableModel extends AbstractTableModel implements Observer
 				tempName += " " + ownerList + "";
 				ownerList = "";
 				groupNames.add(tempName);
-				groupSizes.add(new Integer(currGroupCount));
+				groupSizes.add(Integer.valueOf(currGroupCount));
 				currGroupCount = 0;
 				currGroupCount++;
 				ownerList += po.getDescriptor();
 				if (!po.getDescriptor().equals(""))
 					tempName = po.getClassDescriptor() + " \'" + po.getDescriptor() + "\'";
-				groupStarts.add(new Integer(index));
+				groupStarts.add(Integer.valueOf(index));
 			}
 			last = po;
 			index++;
@@ -119,7 +119,7 @@ public class PropertyTableModel extends AbstractTableModel implements Observer
 		if (owners.size() != 0) {
 			tempName += " " + ownerList + "";
 			groupNames.add(tempName);
-			groupSizes.add(new Integer(currGroupCount));
+			groupSizes.add(Integer.valueOf(currGroupCount));
 		}
 		if (currentGroup > owners.size() - 1)
 			currentGroup = 0;
diff --git a/prism/unit-tests/Makefile b/prism/unit-tests/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a227d1f1fad170ea3e0aec25f64280c9c5edb2b4
--- /dev/null
+++ b/prism/unit-tests/Makefile
@@ -0,0 +1,46 @@
+################################################
+#  NB: This Makefile is designed to be called  #
+#      from the main PRISM Makefile. It won't  #
+#      work on its own because it needs        #
+#      various options to be passed in         #
+################################################
+
+.SUFFIXES: .o .c .cc
+
+# Reminder: $@ = target, $* = target without extension, $< = dependency
+
+PRISM_DIR_REL = ..
+
+JAVA_FILES_ALL := $(shell find . -name '*.java')
+JAVA_FILES = $(subst package-info.java,,$(JAVA_FILES_ALL))
+CLASS_FILES = $(JAVA_FILES:%.java=$(PRISM_DIR_REL)/$(PRISM_CLASSES_DIR)/%.class)
+
+PRISM_CLASSPATH = "$(PRISM_DIR_REL)/$(PRISM_CLASSES_DIR)$(CLASSPATHSEP)$(PRISM_DIR_REL)/$(PRISM_LIB_DIR)/*"
+
+default: all
+
+all: checks class_files
+
+# inhibit building in parallel (-j option)
+.NOTPARALLEL:
+
+# Try and prevent accidental makes (i.e. called manually, not from top-level Makefile)
+checks:
+	@if [ "$(PRISM_SRC_DIR)" = "" ]; then \
+	  (echo "Error: This Makefile is designed to be called from the main PRISM Makefile"; exit 1) \
+	fi;
+
+class_files:
+	@echo ${JAVA_FILES} > java_files.txt
+	$(JAVAC) $(JFLAGS) -sourcepath $(PRISM_DIR_REL)/$(PRISM_SRC_DIR)$(CLASSPATHSEP)$(PRISM_DIR_REL)/unit-tests\
+	                   -classpath $(PRISM_CLASSPATH)\
+	                   -d $(PRISM_DIR_REL)/$(PRISM_CLASSES_DIR)\
+	                   @java_files.txt
+	@rm -f java_files.txt
+
+clean: checks
+	@rm -f $(CLASS_FILES)
+
+celan: clean
+
+#################################################
diff --git a/prism/unit-tests/common/iterable/ArrayIteratorTest.java b/prism/unit-tests/common/iterable/ArrayIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..958f49b1b3ff6aecb4259382fca6b3759939a391
--- /dev/null
+++ b/prism/unit-tests/common/iterable/ArrayIteratorTest.java
@@ -0,0 +1,253 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.function.Supplier;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+interface ArrayIteratorTest
+{
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testNextIndex(Supplier<? extends ArrayIterator<?>> supplier)
+	{
+		ArrayIterator<?> iterator = supplier.get();
+		for (int i=0; iterator.hasNext(); i++) {
+			assertEquals(i, iterator.nextIndex());
+			iterator.next();
+		}
+		assertThrows(NoSuchElementException.class, iterator::nextIndex);
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements ArrayIteratorTest, FunctionalIteratorTest.Of<Object,ArrayIterator.Of<Object>>
+	{
+		@Override
+		public ArrayIterator.Of<Object> getReducible(Object[] objects)
+		{
+			return new ArrayIterator.Of<>(objects);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		public void testOfArray(Object[] array)
+		{
+			ArrayIterator.Of<Object> iterator = new ArrayIterator.Of<>(array);
+			Object[] actual = iterator.collect(new Object[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		public void testOfArrayIntInt_All(Object[] array)
+		{
+			ArrayIterator.Of<Object> expected = new ArrayIterator.Of<>(array);
+			ArrayIterator.Of<Object> actual = new ArrayIterator.Of<>(array, 0, array.length);
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysAsArguments"})
+		public void testOfArrayIntInt_Range(Object[] array)
+		{
+			FunctionalIterable<Object> expected = new Range(1, array.length - 1).map((int i) -> array[i]);
+			ArrayIterator.Of<Object> actual = new ArrayIterator.Of<>(array, 1, array.length - 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOf_Errors()
+		{
+			Optional<Object[]> any = getMultitonArraysOfObject().findAny();
+			assert any.isPresent();
+			Object[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.Of<>((Object[]) null));
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.Of<>(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.Of<>(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.Of<>(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.Of<>(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.Of<>(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.Of<>(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements ArrayIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<ArrayIterator.OfDouble>
+	{
+		@Override
+		public ArrayIterator.OfDouble getReducible(double[] numbers)
+		{
+			return new ArrayIterator.OfDouble(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfDouble", "getSingletonArraysOfDouble", "getMultitonArraysOfDouble"})
+		public void testOfDouble(double[] array)
+		{
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(array);
+			double[] actual = iterator.collect(new double[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfDouble", "getSingletonArraysOfDouble", "getMultitonArraysOfDouble"})
+		public void testOfDoubleArrayIntInt_All(double[] array)
+		{
+			ArrayIterator.OfDouble expected = new ArrayIterator.OfDouble(array);
+			ArrayIterator.OfDouble actual = new ArrayIterator.OfDouble(array, 0, array.length);
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfDouble"})
+		public void testOfDoubleArrayIntInt_Range(double[] array)
+		{
+			FunctionalPrimitiveIterable.OfDouble expected = new Range(1, array.length - 1).mapToDouble((int i) -> array[i]);
+			ArrayIterator.OfDouble actual = new ArrayIterator.OfDouble(array, 1, array.length - 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfDouble_Errors()
+		{
+			Optional<double[]> any = getMultitonArraysOfDouble().findAny();
+			assert any.isPresent();
+			double[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfDouble((double[]) null));
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfDouble(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfDouble(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfDouble(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfDouble(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfDouble(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfDouble(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements ArrayIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<ArrayIterator.OfInt>
+	{
+		@Override
+		public ArrayIterator.OfInt getReducible(int[] numbers)
+		{
+			return new ArrayIterator.OfInt(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfInt", "getSingletonArraysOfInt", "getMultitonArraysOfInt"})
+		public void testOfInt(int[] array)
+		{
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(array);
+			int[] actual = iterator.collect(new int[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfInt", "getSingletonArraysOfInt", "getMultitonArraysOfInt"})
+		public void testOfIntArrayIntInt_All(int[] array)
+		{
+			ArrayIterator.OfInt expected = new ArrayIterator.OfInt(array);
+			ArrayIterator.OfInt actual = new ArrayIterator.OfInt(array, 0, array.length);
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfInt"})
+		public void testOfIntArrayIntInt_Range(int[] array)
+		{
+			FunctionalPrimitiveIterable.OfInt expected = new Range(1, array.length - 1).mapToInt((int i) -> array[i]);
+			ArrayIterator.OfInt actual = new ArrayIterator.OfInt(array, 1, array.length - 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfInt_Errors()
+		{
+			Optional<int[]> any = getMultitonArraysOfInt().findAny();
+			assert any.isPresent();
+			int[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfInt((int[]) null));
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfInt(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfInt(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfInt(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfInt(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfInt(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfInt(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements ArrayIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<ArrayIterator.OfLong>
+	{
+		@Override
+		public ArrayIterator.OfLong getReducible(long[] numbers)
+		{
+			return new ArrayIterator.OfLong(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfLong", "getSingletonArraysOfLong", "getMultitonArraysOfLong"})
+		public void testOfLong(long[] array)
+		{
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(array);
+			long[] actual = iterator.collect(new long[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfLong", "getSingletonArraysOfLong", "getMultitonArraysOfLong"})
+		public void testOfLongArrayIntInt_All(long[] array)
+		{
+			ArrayIterator.OfLong expected = new ArrayIterator.OfLong(array);
+			ArrayIterator.OfLong actual = new ArrayIterator.OfLong(array, 0, array.length);
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfLong"})
+		public void testOfLongArrayIntInt_Range(long[] array)
+		{
+			FunctionalPrimitiveIterable.OfLong expected = new Range(1, array.length - 1).mapToLong((int i) -> array[i]);
+			ArrayIterator.OfLong actual = new ArrayIterator.OfLong(array, 1, array.length - 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfLong_Errors()
+		{
+			Optional<long[]> any = getMultitonArraysOfLong().findAny();
+			assert any.isPresent();
+			long[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfLong((long[]) null));
+			assertThrows(NullPointerException.class, () -> new ArrayIterator.OfLong(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfLong(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfLong(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfLong(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfLong(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new ArrayIterator.OfLong(array, length+1, length+1));
+		}
+	}
+}
\ No newline at end of file
diff --git a/prism/unit-tests/common/iterable/Assertions.java b/prism/unit-tests/common/iterable/Assertions.java
new file mode 100644
index 0000000000000000000000000000000000000000..113676bf6ac0292e9e4fb10cd4538f8ed50d8ba9
--- /dev/null
+++ b/prism/unit-tests/common/iterable/Assertions.java
@@ -0,0 +1,80 @@
+package common.iterable;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.PrimitiveIterator;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class Assertions
+{
+	/**
+	 * Assert two double values are equals while assuming -0.0 == +0.0 and NaN == NaN.
+	 */
+	protected static void assertDoubleEquals(double expected, double actual)
+	{
+		assertTrue(expected == actual || Double.isNaN(expected) && Double.isNaN(actual));
+	}
+
+	public static void assertIterableEquals(PrimitiveIterable.OfDouble expected, PrimitiveIterable.OfDouble actual)
+	{
+		assertIteratorEquals(expected.iterator(), actual.iterator());
+	}
+
+	public static void assertIterableEquals(PrimitiveIterable.OfInt expected, PrimitiveIterable.OfInt actual)
+	{
+		assertIteratorEquals(expected.iterator(), actual.iterator());
+	}
+
+	public static void assertIterableEquals(PrimitiveIterable.OfLong expected, PrimitiveIterable.OfLong actual)
+	{
+		assertIteratorEquals(expected.iterator(), actual.iterator());
+	}
+
+	public static void assertIteratorEquals(Iterator<?> expected, Iterator<?> actual)
+	{
+		while(expected.hasNext() && actual.hasNext()) {
+			assertEquals(expected.next(), actual.next());
+		}
+		assertFalse(expected.hasNext());
+		assertFalse(actual.hasNext());
+		assertThrows(NoSuchElementException.class, expected::next);
+		assertThrows(NoSuchElementException.class, actual::next);
+	}
+
+	public static void assertIteratorEquals(PrimitiveIterator.OfDouble expected, PrimitiveIterator.OfDouble actual)
+	{
+		while(expected.hasNext() && actual.hasNext()) {
+			double exp = expected.nextDouble();
+			double act = actual.nextDouble();
+			assertDoubleEquals(exp, act);
+		}
+		assertFalse(expected.hasNext());
+		assertFalse(actual.hasNext());
+		assertThrows(NoSuchElementException.class, expected::next);
+		assertThrows(NoSuchElementException.class, actual::next);
+	}
+
+	public static void assertIteratorEquals(PrimitiveIterator.OfInt expected, PrimitiveIterator.OfInt actual)
+	{
+		while(expected.hasNext() && actual.hasNext()) {
+			assertEquals(expected.nextInt(), actual.nextInt());
+		}
+		assertFalse(expected.hasNext());
+		assertFalse(actual.hasNext());
+		assertThrows(NoSuchElementException.class, expected::next);
+		assertThrows(NoSuchElementException.class, actual::next);
+	}
+
+	public static void assertIteratorEquals(PrimitiveIterator.OfLong expected, PrimitiveIterator.OfLong actual)
+	{
+		while(expected.hasNext() && actual.hasNext()) {
+			assertEquals(expected.nextLong(), actual.nextLong());
+		}
+		assertFalse(expected.hasNext());
+		assertFalse(actual.hasNext());
+		assertThrows(NoSuchElementException.class, expected::next);
+		assertThrows(NoSuchElementException.class, actual::next);
+	}
+
+}
diff --git a/prism/unit-tests/common/iterable/ChainedIterableTest.java b/prism/unit-tests/common/iterable/ChainedIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0952b95582dcbb78c647e8a5e96b14b0e78cf0a
--- /dev/null
+++ b/prism/unit-tests/common/iterable/ChainedIterableTest.java
@@ -0,0 +1,345 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.Reducible.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface ChainedIterableTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements ChainedIterableTest, FunctionalIterableTest.Of<Object,ChainedIterable.Of<Object>>
+	{
+		@Override
+		public ChainedIterable.Of<Object> getReducible(Object[] objects)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<Iterable<Object>>> split(Object[] objects)
+		{
+			// 1. whole sequence
+			FunctionalIterable<Iterable<Object>> complete = FunctionalIterable.of(Arrays.asList(objects));
+			// 2. split & pad with empty
+			List<Object> empty = Collections.emptyList();
+			int l1 = objects.length / 2;
+			Object[] first = new Object[l1];
+			System.arraycopy(objects, 0, first, 0, l1);
+			int l2 = objects.length - l1;
+			Object[] second = new Object[l2];
+			System.arraycopy(objects, l1, second, 0, l2);
+			FunctionalIterable<Iterable<Object>>  chunks = FunctionalIterable.of(empty, Arrays.asList(first), empty, Arrays.asList(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<Iterable<Object>>> getChains()
+		{
+			return getArraysOfObject().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.Of<Object>>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getDuplicatesArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.Of<>(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.Of<Object>>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getEmptyArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.Of<>(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.Of<Object>>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getSingletonArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.Of<>(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.Of<Object>>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getMultitonArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.Of<>(args));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(Iterable<FunctionalIterable<Object>> chain)
+		{
+			List<Object> expected = new ArrayList<>();
+			for (Iterable<?> iterable : chain) {
+				for (Object each : iterable) {
+					expected.add(each);
+				}
+			}
+			ChainedIterable.Of<Object> actual = new ChainedIterable.Of<>(chain);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterable.Of<>(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements ChainedIterableTest, FunctionalPrimitiveIterableTest.OfDouble<ChainedIterable.OfDouble>
+	{
+		@Override
+		public ChainedIterable.OfDouble getReducible(double[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> split(double[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfDouble> complete = FunctionalIterable.of(new IterableArray.OfDouble(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfDouble empty = EmptyIterable.ofDouble();
+			int l1 = numbers.length / 2;
+			double[] first = new double[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			double[] second = new double[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfDouble>  chunks = FunctionalIterable.of(empty, new IterableArray.OfDouble(first), empty, new IterableArray.OfDouble(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> getChains()
+		{
+			return getArraysOfDouble().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfDouble>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getDuplicatesArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfDouble(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfDouble>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getEmptyArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfDouble(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfDouble>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getSingletonArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfDouble(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfDouble>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getMultitonArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfDouble(args));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfDouble> chain)
+		{
+			List<Double> expected = new ArrayList<>();
+			for (Iterable<Double> iterable : chain) {
+				for (Double each : iterable) {
+					expected.add(each);
+				}
+			}
+			ChainedIterable.OfDouble actual = new ChainedIterable.OfDouble(chain);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterable.OfDouble(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements ChainedIterableTest, FunctionalPrimitiveIterableTest.OfInt<ChainedIterable.OfInt>
+	{
+		@Override
+		public ChainedIterable.OfInt getReducible(int[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfInt>> split(int[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfInt> complete = FunctionalIterable.of(new IterableArray.OfInt(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfInt empty = EmptyIterable.ofInt();
+			int l1 = numbers.length / 2;
+			int[] first = new int[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			int[] second = new int[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfInt>  chunks = FunctionalIterable.of(empty, new IterableArray.OfInt(first), empty, new IterableArray.OfInt(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<PrimitiveIterable.OfInt>> getChains()
+		{
+			return getArraysOfInt().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfInt>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getDuplicatesArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfInt(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfInt>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getEmptyArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfInt(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfInt>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getSingletonArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfInt(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfInt>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getMultitonArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfInt(args));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfInt> chain)
+		{
+			List<Integer> expected = new ArrayList<>();
+			for (Iterable<Integer> iterable : chain) {
+				for (Integer each : iterable) {
+					expected.add(each);
+				}
+			}
+			ChainedIterable.OfInt actual = new ChainedIterable.OfInt(chain);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterable.OfInt(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements ChainedIterableTest, FunctionalPrimitiveIterableTest.OfLong<ChainedIterable.OfLong>
+	{
+		@Override
+		public ChainedIterable.OfLong getReducible(long[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfLong>> split(long[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfLong> complete = FunctionalIterable.of(new IterableArray.OfLong(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfLong empty = EmptyIterable.ofLong();
+			int l1 = numbers.length / 2;
+			long[] first = new long[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			long[] second = new long[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfLong>  chunks = FunctionalIterable.of(empty, new IterableArray.OfLong(first), empty, new IterableArray.OfLong(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<PrimitiveIterable.OfLong>> getChains()
+		{
+			return getArraysOfLong().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfLong>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getDuplicatesArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfLong(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfLong>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getEmptyArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfLong(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfLong>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getSingletonArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfLong(args));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterable.OfLong>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getMultitonArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterable.OfLong(args));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfLong> chain)
+		{
+			List<Long> expected = new ArrayList<>();
+			for (Iterable<Long> iterable : chain) {
+				for (Long each : iterable) {
+					expected.add(each);
+				}
+			}
+			ChainedIterable.OfLong actual = new ChainedIterable.OfLong(chain);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterable.OfLong(null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/ChainedIteratorTest.java b/prism/unit-tests/common/iterable/ChainedIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1e05c761419786562209b9024c5b999987f66dc
--- /dev/null
+++ b/prism/unit-tests/common/iterable/ChainedIteratorTest.java
@@ -0,0 +1,347 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface ChainedIteratorTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements ChainedIteratorTest, FunctionalIteratorTest.Of<Object,ChainedIterator.Of<Object>>
+	{
+		@Override
+		public ChainedIterator.Of<Object> getReducible(Object[] objects)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<Iterable<Object>>> split(Object[] objects)
+		{
+			// 1. whole sequence
+			FunctionalIterable<Iterable<Object>> complete = FunctionalIterable.of(Arrays.asList(objects));
+			// 2. split & pad with empty
+			List<Object> empty = Collections.emptyList();
+			int l1 = objects.length / 2;
+			Object[] first = new Object[l1];
+			System.arraycopy(objects, 0, first, 0, l1);
+			int l2 = objects.length - l1;
+			Object[] second = new Object[l2];
+			System.arraycopy(objects, l1, second, 0, l2);
+			FunctionalIterable<Iterable<Object>>  chunks = FunctionalIterable.of(empty, Arrays.asList(first), empty, Arrays.asList(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<Iterable<Object>>> getChains()
+		{
+			return getArraysOfObject().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.Of<Object>>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getDuplicatesArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.Of<>(args.map(Iterable::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.Of<Object>>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getEmptyArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.Of<>(args.map(Iterable::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.Of<Object>>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getSingletonArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.Of<>(args.map(Iterable::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.Of<Object>>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<Iterable<Object>>> splits = getMultitonArraysOfObject().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.Of<>(args.map(Iterable::iterator).iterator()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<Iterable<Object>> chain)
+		{
+			List<Object> expected = new ArrayList<>();
+			for (Iterable<?> iterable : chain) {
+				for (Object each : iterable) {
+					expected.add(each);
+				}
+			}
+			FunctionalIterator<Iterator<Object>> iterators = Reducible.extend(chain.iterator()).map(Iterable::iterator);
+			ChainedIterator.Of<Object> actual = new ChainedIterator.Of<>(iterators);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterator.Of<>(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements ChainedIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<ChainedIterator.OfDouble>
+	{
+		@Override
+		public ChainedIterator.OfDouble getReducible(double[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> split(double[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfDouble> complete = FunctionalIterable.of(new IterableArray.OfDouble(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfDouble empty = EmptyIterable.ofDouble();
+			int l1 = numbers.length / 2;
+			double[] first = new double[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			double[] second = new double[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfDouble>  chunks = FunctionalIterable.of(empty, new IterableArray.OfDouble(first), empty, new IterableArray.OfDouble(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> getChains()
+		{
+			return getArraysOfDouble().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfDouble>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getDuplicatesArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfDouble(args.map(PrimitiveIterable.OfDouble::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfDouble>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getEmptyArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfDouble(args.map(PrimitiveIterable.OfDouble::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfDouble>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getSingletonArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfDouble(args.map(PrimitiveIterable.OfDouble::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfDouble>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfDouble>> splits = getMultitonArraysOfDouble().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfDouble(args.map(PrimitiveIterable.OfDouble::iterator).iterator()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfDouble> chain)
+		{
+			List<Double> expected = new ArrayList<>();
+			for (Iterable<Double> iterable : chain) {
+				for (Double each : iterable) {
+					expected.add(each);
+				}
+			}
+			FunctionalIterator<PrimitiveIterator.OfDouble> iterators = Reducible.extend(chain.iterator()).map(PrimitiveIterable.OfDouble::iterator);
+			ChainedIterator.OfDouble actual = new ChainedIterator.OfDouble(iterators);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterator.OfDouble(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements ChainedIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<ChainedIterator.OfInt>
+	{
+		@Override
+		public ChainedIterator.OfInt getReducible(int[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfInt>> split(int[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfInt> complete = FunctionalIterable.of(new IterableArray.OfInt(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfInt empty = EmptyIterable.ofInt();
+			int l1 = numbers.length / 2;
+			int[] first = new int[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			int[] second = new int[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfInt>  chunks = FunctionalIterable.of(empty, new IterableArray.OfInt(first), empty, new IterableArray.OfInt(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<FunctionalIterable<PrimitiveIterable.OfInt>> getChains()
+		{
+			return getArraysOfInt().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfInt>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getDuplicatesArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfInt(args.map(PrimitiveIterable.OfInt::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfInt>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getEmptyArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfInt(args.map(PrimitiveIterable.OfInt::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfInt>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getSingletonArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfInt(args.map(PrimitiveIterable.OfInt::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfInt>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfInt>> splits = getMultitonArraysOfInt().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfInt(args.map(PrimitiveIterable.OfInt::iterator).iterator()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfInt> chain)
+		{
+			List<Integer> expected = new ArrayList<>();
+			for (Iterable<Integer> iterable : chain) {
+				for (Integer each : iterable) {
+					expected.add(each);
+				}
+			}
+			FunctionalIterator<PrimitiveIterator.OfInt> iterators = Reducible.extend(chain.iterator()).map(PrimitiveIterable.OfInt::iterator);
+			ChainedIterator.OfInt actual = new ChainedIterator.OfInt(iterators);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterator.OfInt(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements ChainedIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<ChainedIterator.OfLong>
+	{
+		@Override
+		public ChainedIterator.OfLong getReducible(long[] numbers)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		protected Stream<FunctionalIterable<PrimitiveIterable.OfLong>> split(long[] numbers)
+		{
+			// 1. whole sequence
+			FunctionalIterable<PrimitiveIterable.OfLong> complete = FunctionalIterable.of(new IterableArray.OfLong(numbers));
+			// 2. split & pad with empty
+			EmptyIterable.OfLong empty = EmptyIterable.ofLong();
+			int l1 = numbers.length / 2;
+			long[] first = new long[l1];
+			System.arraycopy(numbers, 0, first, 0, l1);
+			int l2 = numbers.length - l1;
+			long[] second = new long[l2];
+			System.arraycopy(numbers, l1, second, 0, l2);
+			FunctionalIterable<PrimitiveIterable.OfLong>  chunks = FunctionalIterable.of(empty, new IterableArray.OfLong(first), empty, new IterableArray.OfLong(second), empty);
+			return Stream.of(complete, chunks);
+		}
+
+		public Stream<Iterable<PrimitiveIterable.OfLong>> getChains()
+		{
+			return getArraysOfLong().flatMap(this::split);
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfLong>> getDuplicatesReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getDuplicatesArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfLong(args.map(PrimitiveIterable.OfLong::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfLong>> getEmptyReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getEmptyArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfLong(args.map(PrimitiveIterable.OfLong::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfLong>> getSingletonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getSingletonArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfLong(args.map(PrimitiveIterable.OfLong::iterator).iterator()));
+		}
+
+		@Override
+		public Stream<Supplier<ChainedIterator.OfLong>> getMultitonReducibles()
+		{
+			Stream<FunctionalIterable<PrimitiveIterable.OfLong>> splits = getMultitonArraysOfLong().flatMap(this::split);
+			return splits.map(args -> () -> new ChainedIterator.OfLong(args.map(PrimitiveIterable.OfLong::iterator).iterator()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getChains")
+		public void testOf(FunctionalIterable<PrimitiveIterable.OfLong> chain)
+		{
+			List<Long> expected = new ArrayList<>();
+			for (Iterable<Long> iterable : chain) {
+				for (Long each : iterable) {
+					expected.add(each);
+				}
+			}
+			FunctionalIterator<PrimitiveIterator.OfLong> iterators = Reducible.extend(chain.iterator()).map(PrimitiveIterable.OfLong::iterator);
+			ChainedIterator.OfLong actual = new ChainedIterator.OfLong(iterators);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new ChainedIterator.OfLong(null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/DistinctTest.java b/prism/unit-tests/common/iterable/DistinctTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b11636438a9fe245ea8e3d282e63406df71d1421
--- /dev/null
+++ b/prism/unit-tests/common/iterable/DistinctTest.java
@@ -0,0 +1,160 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface DistinctTest<E, T extends Distinct<E>>
+{
+	T getDistinct();
+
+	boolean test(T distinct, E element);
+
+	Stream<FunctionalIterable<E>> getElements();
+
+	@ParameterizedTest
+	@MethodSource("getElements")
+	default void testGetSeen(FunctionalIterable<E> elements)
+	{
+		T distinct = getDistinct();
+		assertTrue(distinct.getSeen().isEmpty());
+		for (E e : elements) {
+			test(distinct, e);
+			test(distinct, e);
+		}
+		FunctionalIterable<E> seen = distinct.getSeen();
+		for (E e : elements) {
+			assertTrue(seen.contains(e));
+		}
+		for (E e : seen) {
+			assertTrue(elements.contains(e));
+		}
+	}
+
+	@ParameterizedTest
+	@MethodSource("getElements")
+	default void testTest(FunctionalIterable<E> elements)
+	{
+		T distinct = getDistinct();
+		for (E e : elements) {
+			assertTrue(test(distinct, e));
+			assertFalse(test(distinct, e));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements DistinctTest<String, Distinct.Of<String>>
+	{
+		@Override
+		public Distinct.Of<String> getDistinct()
+		{
+			return new Distinct.Of<>();
+		}
+
+		@Override
+		public boolean test(Distinct.Of<String> distinct, String element)
+		{
+			return distinct.test(element);
+		}
+
+		@Override
+		public Stream<FunctionalIterable<String>> getElements()
+		{
+			return Stream.of(new IterableArray.Of<>("first", "second", "third"));
+		}
+	}
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements DistinctTest<Double, Distinct.OfDouble>
+	{
+		@Override
+		public Distinct.OfDouble getDistinct()
+		{
+			return new Distinct.OfDouble();
+		}
+
+		@Override
+		public boolean test(Distinct.OfDouble distinct, Double element)
+		{
+			return distinct.test(element);
+		}
+
+		@Override
+		public Stream<FunctionalIterable<Double>> getElements()
+		{
+			return Stream.of(new IterableArray.Of<>(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, 0.0, 1.0, 2.0, 3.0));
+		}
+
+		@Test
+		public void testTest_PositiveZero()
+		{
+			Distinct.OfDouble distinct = getDistinct();
+			assertTrue(distinct.test(+0.0));
+			assertFalse(distinct.test(+0.0));
+			assertFalse(distinct.test(-0.0));
+		}
+
+		@Test
+		public void testTest_NegativeZero()
+		{
+			Distinct.OfDouble distinct = getDistinct();
+			assertTrue(distinct.test(+0.0));
+			assertFalse(distinct.test(-0.0));
+			assertFalse(distinct.test(+0.0));
+		}
+	}
+
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements DistinctTest<Integer, Distinct.OfInt>
+	{
+		@Override
+		public Distinct.OfInt getDistinct()
+		{
+			return new Distinct.OfInt();
+		}
+
+		@Override
+		public boolean test(Distinct.OfInt distinct, Integer element)
+		{
+			return distinct.test(element);
+		}
+
+		@Override
+		public Stream<FunctionalIterable<Integer>> getElements()
+		{
+			return Stream.of(new IterableArray.Of<>(1, 2, 3));
+		}
+	}
+
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements DistinctTest<Long, Distinct.OfLong>
+	{
+		@Override
+		public Distinct.OfLong getDistinct()
+		{
+			return new Distinct.OfLong();
+		}
+
+		@Override
+		public boolean test(Distinct.OfLong distinct, Long element)
+		{
+			return distinct.test(element);
+		}
+
+		@Override
+		public Stream<FunctionalIterable<Long>> getElements()
+		{
+			return Stream.of(new IterableArray.Of<>(1L, 2L, 3L));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/EmptyIterableTest.java b/prism/unit-tests/common/iterable/EmptyIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..af3489556d0a36e855d87a0ebff4dd7a45dcc325
--- /dev/null
+++ b/prism/unit-tests/common/iterable/EmptyIterableTest.java
@@ -0,0 +1,342 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.TestInstance;
+
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+interface EmptyIterableTest<E, T extends EmptyIterable<E>> extends FunctionalIterableTest<E,T>
+{
+	@Override
+	default void testAllMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testAnyMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testDetect_Multiton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testDetect_Singleton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testFlatMapToNull(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testNoneMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testIsEmpty_NonEmpty(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Singleton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Multiton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinary_ResultNull(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements EmptyIterableTest<Object,EmptyIterable.Of<Object>>, FunctionalIterableTest.Of<Object,EmptyIterable.Of<Object>>
+	{
+		@Override
+		public EmptyIterable.Of<Object> getReducible(Object[] objects)
+		{
+			assert objects.length == 0 : "empty array expected";
+			return EmptyIterable.of();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.Of<Object>>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.Of<Object>>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.Of<Object>>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements EmptyIterableTest<Double,EmptyIterable.OfDouble>, FunctionalPrimitiveIterableTest.OfDouble<EmptyIterable.OfDouble>
+	{
+		@Override
+		public EmptyIterable.OfDouble getReducible(double[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterable.ofDouble();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfDouble>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfDouble>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfDouble>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchDoublePredicate(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchDoublePredicate(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Multiton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Singleton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapDoubleToNull(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchDoublePredicate(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Singleton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<EmptyIterable.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements EmptyIterableTest<Integer,EmptyIterable.OfInt>, FunctionalPrimitiveIterableTest.OfInt<EmptyIterable.OfInt>
+	{
+		@Override
+		public EmptyIterable.OfInt getReducible(int[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterable.ofInt();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfInt>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfInt>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfInt>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchIntPredicate(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchIntPredicate(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectIntPredicate_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectIntPredicate_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapIntToNull(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchIntPredicate(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Singleton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<EmptyIterable.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements EmptyIterableTest<Long,EmptyIterable.OfLong>, FunctionalPrimitiveIterableTest.OfLong<EmptyIterable.OfLong>
+	{
+		@Override
+		public EmptyIterable.OfLong getReducible(long[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterable.ofLong();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfLong>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfLong>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterable.OfLong>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchLongPredicate(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatch(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchLongPredicate(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectLongPredicate_Multiton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectLongPredicate_Singleton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapLongToNull(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchLongPredicate(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Singleton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<EmptyIterable.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+}
\ No newline at end of file
diff --git a/prism/unit-tests/common/iterable/EmptyIteratorTest.java b/prism/unit-tests/common/iterable/EmptyIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0816bdfe1d699cbf2475fd75d262287d9deaacac
--- /dev/null
+++ b/prism/unit-tests/common/iterable/EmptyIteratorTest.java
@@ -0,0 +1,342 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.TestInstance;
+
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+interface EmptyIteratorTest<E, T extends EmptyIterator<E>> extends FunctionalIteratorTest<E,T>
+{
+	@Override
+	default void testAllMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testAnyMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testDetect_Multiton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testDetect_Singleton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testFlatMapToNull(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testNoneMatch(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testIsEmpty_NonEmpty(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Singleton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Multiton(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+	@Override
+	default void testReduceBinary_ResultNull(Supplier<T> supplier)
+	{ /* empty reducibles hold no value */ }
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements EmptyIteratorTest<Object,EmptyIterator.Of<Object>>, FunctionalIteratorTest.Of<Object,EmptyIterator.Of<Object>>
+	{
+		@Override
+		public EmptyIterator.Of<Object> getReducible(Object[] objects)
+		{
+			assert objects.length == 0 : "empty array expected";
+			return EmptyIterator.of();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.Of<Object>>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.Of<Object>>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.Of<Object>>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements EmptyIteratorTest<Double,EmptyIterator.OfDouble>, FunctionalPrimitiveIteratorTest.OfDouble<EmptyIterator.OfDouble>
+	{
+		@Override
+		public EmptyIterator.OfDouble getReducible(double[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterator.ofDouble();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfDouble>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfDouble>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfDouble>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchDoublePredicate(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchDoublePredicate(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Multiton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Singleton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapDoubleToNull(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchDoublePredicate(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Singleton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<EmptyIterator.OfDouble> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements EmptyIteratorTest<Integer,EmptyIterator.OfInt>, FunctionalPrimitiveIteratorTest.OfInt<EmptyIterator.OfInt>
+	{
+		@Override
+		public EmptyIterator.OfInt getReducible(int[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterator.ofInt();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfInt>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfInt>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfInt>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchIntPredicate(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchIntPredicate(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectIntPredicate_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectIntPredicate_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapIntToNull(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchIntPredicate(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Singleton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<EmptyIterator.OfInt> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements EmptyIteratorTest<Long,EmptyIterator.OfLong>, FunctionalPrimitiveIteratorTest.OfLong<EmptyIterator.OfLong>
+	{
+		@Override
+		public EmptyIterator.OfLong getReducible(long[] numbers)
+		{
+			assert numbers.length == 0 : "empty array expected";
+			return EmptyIterator.ofLong();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfLong>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfLong>> getSingletonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<EmptyIterator.OfLong>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchLongPredicate(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatch(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testAnyMatchLongPredicate(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectLongPredicate_Multiton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testDetectLongPredicate_Singleton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testFlatMapLongToNull(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Singleton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Singleton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testNoneMatchLongPredicate(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Singleton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<EmptyIterator.OfLong> supplier)
+		{ /* empty reducibles hold no value */ }
+	}
+}
\ No newline at end of file
diff --git a/prism/unit-tests/common/iterable/FilteringIterableTest.java b/prism/unit-tests/common/iterable/FilteringIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca3711c58f941bb1bb0065f1e80cd0468f7f50d8
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FilteringIterableTest.java
@@ -0,0 +1,165 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface FilteringIterableTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements FilteringIterableTest, FunctionalIterableTest.Of<Object,FilteringIterable.Of<Object>>
+	{
+		@Override
+		public FilteringIterable.Of<Object> getReducible(Object[] objects)
+		{
+			return new FilteringIterable.Of<>(FunctionalIterable.of(objects), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		void testOf(Object[] objects)
+		{
+			ArrayList<Object> expected = new ArrayList<>();
+			int c = 0;
+			for (Object each : objects) {
+				if (c++ % 2 == 0) {
+					expected.add(each);
+				}
+			}
+			Iterable<Object> iterable = Arrays.asList(objects);
+			FunctionalIterable<Object> actual = new FilteringIterable.Of<>(iterable, expected::contains);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			EmptyIterable.Of<Object> iterable = EmptyIterable.of();
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.Of<>(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.Of<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements FilteringIterableTest, FunctionalPrimitiveIterableTest.OfDouble<FilteringIterable.OfDouble>
+	{
+		@Override
+		public FilteringIterable.OfDouble getReducible(double[] numbers)
+		{
+			return new FilteringIterable.OfDouble(FunctionalIterable.ofDouble(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testOfDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			int c = 0;
+			for (double d : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(d);
+				}
+			}
+			IterableArray.OfDouble iterable = new IterableArray.OfDouble(numbers);
+			FunctionalPrimitiveIterable.OfDouble actual = new FilteringIterable.OfDouble(iterable, expected::contains);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			EmptyIterable.OfDouble iterable = EmptyIterable.ofDouble();
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfDouble(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfDouble(iterable, null));
+		}
+	}
+
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements FilteringIterableTest, FunctionalPrimitiveIterableTest.OfInt<FilteringIterable.OfInt>
+	{
+		@Override
+		public FilteringIterable.OfInt getReducible(int[] numbers)
+		{
+			return new FilteringIterable.OfInt(FunctionalIterable.ofInt(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testOfInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			int c = 0;
+			for (int i : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(i);
+				}
+			}
+			IterableArray.OfInt iterable = new IterableArray.OfInt(numbers);
+			FunctionalPrimitiveIterable.OfInt actual = new FilteringIterable.OfInt(iterable, expected::contains);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testOfInt_Null()
+		{
+			EmptyIterable.OfInt iterable = EmptyIterable.ofInt();
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfInt(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfInt(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements FilteringIterableTest, FunctionalPrimitiveIterableTest.OfLong<FilteringIterable.OfLong>
+	{
+		@Override
+		public FilteringIterable.OfLong getReducible(long[] numbers)
+		{
+			return new FilteringIterable.OfLong(FunctionalIterable.ofLong(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testOfLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			int c = 0;
+			for (Long i : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(i);
+				}
+			}
+			IterableArray.OfLong iterable = new IterableArray.OfLong(numbers);
+			FunctionalPrimitiveIterable.OfLong actual = new FilteringIterable.OfLong(iterable, expected::contains);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testOfLong_Null()
+		{
+			EmptyIterable.OfLong iterable = EmptyIterable.ofLong();
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfLong(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterable.OfLong(iterable, null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FilteringIteratorTest.java b/prism/unit-tests/common/iterable/FilteringIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..004e9e32d0b43b89d639b36e263bb8722e4f184a
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FilteringIteratorTest.java
@@ -0,0 +1,165 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface FilteringIteratorTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements FilteringIteratorTest, FunctionalIteratorTest.Of<Object,FilteringIterator.Of<Object>>
+	{
+		@Override
+		public FilteringIterator.Of<Object> getReducible(Object[] objects)
+		{
+			return new FilteringIterator.Of<>(FunctionalIterator.of(objects), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		void testOf(Object[] objects)
+		{
+			ArrayList<Object> expected = new ArrayList<>();
+			int c = 0;
+			for (Object each : objects) {
+				if (c++ % 2 == 0) {
+					expected.add(each);
+				}
+			}
+			Iterator<Object> iterator = Arrays.asList(objects).iterator();
+			FunctionalIterator<Object> actual = new FilteringIterator.Of<>(iterator, expected::contains);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			EmptyIterator.Of<Object> iterator = EmptyIterator.of();
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.Of<>(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.Of<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements FilteringIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<FilteringIterator.OfDouble>
+	{
+		@Override
+		public FilteringIterator.OfDouble getReducible(double[] numbers)
+		{
+			return new FilteringIterator.OfDouble(FunctionalIterator.ofDouble(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testOfDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			int c = 0;
+			for (double d : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(d);
+				}
+			}
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(numbers);
+			FunctionalPrimitiveIterator.OfDouble actual = new FilteringIterator.OfDouble(iterator, expected::contains);
+			assertIteratorEquals(unboxDouble(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testOfDouble_Null()
+		{
+			EmptyIterator.OfDouble iterator = EmptyIterator.ofDouble();
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfDouble(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfDouble(iterator, null));
+		}
+	}
+
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements FilteringIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<FilteringIterator.OfInt>
+	{
+		@Override
+		public FilteringIterator.OfInt getReducible(int[] numbers)
+		{
+			return new FilteringIterator.OfInt(FunctionalIterator.ofInt(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testOfInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			int c = 0;
+			for (int i : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(i);
+				}
+			}
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(numbers);
+			FunctionalPrimitiveIterator.OfInt actual = new FilteringIterator.OfInt(iterator, expected::contains);
+			assertIteratorEquals(unboxInt(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testOfInt_Null()
+		{
+			EmptyIterator.OfInt iterator = EmptyIterator.ofInt();
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfInt(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfInt(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements FilteringIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<FilteringIterator.OfLong>
+	{
+		@Override
+		public FilteringIterator.OfLong getReducible(long[] numbers)
+		{
+			return new FilteringIterator.OfLong(FunctionalIterator.ofLong(numbers), e -> true);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testOfLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			int c = 0;
+			for (Long i : numbers) {
+				if (c++ % 2 == 0) {
+					expected.add(i);
+				}
+			}
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(numbers);
+			FunctionalPrimitiveIterator.OfLong actual = new FilteringIterator.OfLong(iterator, expected::contains);
+			assertIteratorEquals(unboxLong(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testOfLong_Null()
+		{
+			EmptyIterator.OfLong iterator = EmptyIterator.ofLong();
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfLong(null, e -> true));
+			assertThrows(NullPointerException.class, () -> new FilteringIterator.OfLong(iterator, null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalIterableStaticTest.java b/prism/unit-tests/common/iterable/FunctionalIterableStaticTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7bbe8fb2b2ff84772426b8270362e06f67168db9
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalIterableStaticTest.java
@@ -0,0 +1,140 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+class FunctionalIterableStaticTest
+{
+	static Stream<Iterable<Object>> getIterables()
+	{
+		return Stream.of(Collections.singleton(null),
+				Collections.emptyList(),
+				Collections.singleton("one"),
+				Arrays.asList("one", "two", "three"));
+	}
+
+	static Stream<Iterable<Double>> getIterablesDouble()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1.0),
+				Arrays.asList(1.0, 2.0, 3.0));
+	}
+
+	static Stream<Iterable<Integer>> getIterablesInt()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1),
+				Arrays.asList(1, 2, 3));
+	}
+
+	static Stream<Iterable<Long>> getIterablesLong()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1L),
+				Arrays.asList(1L, 2L, 3L));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	void testOf(Iterable<Object> iterable)
+	{
+		FunctionalIterable<Object> expected = Reducible.extend(iterable);
+		Object[] arguments = new Object[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalIterable<Object> actual = FunctionalIterable.of(arguments);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	void testOfTypes()
+	{
+		assertTrue(FunctionalIterable.of() instanceof EmptyIterable.Of);
+		assertTrue(FunctionalIterable.of("first") instanceof SingletonIterable.Of);
+	}
+
+	@Test
+	void testOf_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterable.of((Object[]) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	void testOfDouble(Iterable<Double> iterable)
+	{
+		FunctionalPrimitiveIterable.OfDouble expected = Reducible.unboxDouble(iterable);
+		double[] arguments = new double[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterable.OfDouble actual = FunctionalIterable.ofDouble(arguments);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	static void testOfDoubleTypes()
+	{
+		assertTrue(FunctionalIterable.ofDouble() instanceof EmptyIterable.OfDouble);
+		assertTrue(FunctionalIterable.ofDouble(1.0) instanceof SingletonIterable.Of);
+	}
+
+	@Test
+	void testOfDouble_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterable.ofDouble((double[]) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	void testOfInt(Iterable<Integer> iterable)
+	{
+		FunctionalPrimitiveIterable.OfInt expected = Reducible.unboxInt(iterable);
+		int[] arguments = new int[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterable.OfInt actual = FunctionalIterable.ofInt(arguments);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	void testOfIntTypes()
+	{
+		assertTrue(FunctionalIterable.ofInt() instanceof EmptyIterable.OfInt);
+		assertTrue(FunctionalIterable.ofInt(1) instanceof SingletonIterable.OfInt);
+	}
+
+	@Test
+	void testOfInt_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterable.ofInt((int[])null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	void testOfLong(Iterable<Long> iterable)
+	{
+		FunctionalPrimitiveIterable.OfLong expected = Reducible.unboxLong(iterable);
+		long[] arguments = new long[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterable.OfLong actual = FunctionalIterable.ofLong(arguments);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	void testOfLongTypes()
+	{
+		assertTrue(FunctionalIterable.ofLong() instanceof EmptyIterable.OfLong);
+		assertTrue(FunctionalIterable.ofLong(1L) instanceof SingletonIterable.OfLong);
+	}
+
+	@Test
+	void testOfLong_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterable.ofLong((long[]) null));
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalIterableTest.java b/prism/unit-tests/common/iterable/FunctionalIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d6caeb7751f2997c0f81c6a70aaa69b5e46ea99
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalIterableTest.java
@@ -0,0 +1,112 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface FunctionalIterableTest<E, T extends FunctionalIterable<E>> extends ReducibleTest<E, T>
+{
+	@ParameterizedTest
+	@MethodSource({"getReducibles"})
+	@Override
+	default void testConcat(Supplier<T> supplier)
+	{
+		ArrayList<Object> expected = supplier.get().collect(new ArrayList<>());
+		supplier.get().collect(expected);
+		FunctionalIterable<E> actual = supplier.get().concat(supplier.get());
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMap(Supplier<T> supplier)
+	{
+		FunctionalIterable<String> expected = supplier.get().map(String::valueOf);
+		FunctionalIterable<String> actual = supplier.get().flatMap(e -> new SingletonIterable.Of<>(String.valueOf(e)));
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToDouble(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		PrimitiveIterable.OfDouble expected = unboxDouble(range.map((int i) -> (double) i));
+		Range.RangeIterator index = range.iterator();
+		PrimitiveIterable.OfDouble actual = supplier.get().flatMapToDouble(e -> new SingletonIterable.OfDouble(index.next()));
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToInt(Supplier<T> supplier)
+	{
+		Range expected = new Range((int) supplier.get().count());
+		Range.RangeIterator index = expected.iterator();
+		PrimitiveIterable.OfInt actual = supplier.get().flatMapToInt(e -> new SingletonIterable.OfInt(index.next()));
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToLong(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		PrimitiveIterable.OfLong expected = unboxLong(range.map((int i) -> (long) i));
+		Range.RangeIterator index = range.iterator();
+		PrimitiveIterable.OfLong actual = supplier.get().flatMapToLong(e -> new SingletonIterable.OfLong(index.next()));
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getSingletonReducibles")
+	@Override
+	default void testFlatMapToNull(Supplier<T> supplier)
+	{
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMap(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong(e -> null).consume());
+	}
+
+	@Test
+	@Override
+	default void testFlatMap_Null()
+	{
+		FunctionalIterable<E> iterator = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> iterator.flatMap(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToInt(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToLong(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@DisplayName("forEach yields same sequence as the underlying iterator.")
+	@Override
+	default void testForEach(Supplier<T> supplier)
+	{
+		T expected = supplier.get();
+		List<Object> actual = new ArrayList<>();
+		supplier.get().forEach(actual::add);
+		assertIterableEquals(expected, actual);
+	}
+
+
+
+	interface Of<E, T extends FunctionalIterable<E>> extends FunctionalIterableTest<E, T>, ReducibleTest.Of<E, T>
+	{
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalIteratorStaticTest.java b/prism/unit-tests/common/iterable/FunctionalIteratorStaticTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..78f9a33da8491f648fb6e938e4065425488392f7
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalIteratorStaticTest.java
@@ -0,0 +1,139 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+class FunctionalIteratorStaticTest
+{
+	static Stream<Iterable<Object>> getIterables()
+	{
+		return Stream.of(Collections.singleton(null),
+				Collections.emptyList(),
+				Collections.singleton("one"),
+				Arrays.asList("one", "two", "three"));
+	}
+
+	static Stream<Iterable<Double>> getIterablesDouble()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1.0),
+				Arrays.asList(1.0, 2.0, 3.0));
+	}
+
+	static Stream<Iterable<Integer>> getIterablesInt()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1),
+				Arrays.asList(1, 2, 3));
+	}
+
+	static Stream<Iterable<Long>> getIterablesLong()
+	{
+		return Stream.of(Collections.emptyList(),
+				Collections.singleton(1L),
+				Arrays.asList(1L, 2L, 3L));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	void testOf(Iterable<Object> iterable)
+	{
+		FunctionalIterable<Object> expected = Reducible.extend(iterable);
+		Object[] arguments = new Object[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalIterator<Object> actual = FunctionalIterator.of(arguments);
+		assertIteratorEquals(expected.iterator(), actual);
+	}
+
+	@Test
+	void testOfTypes()
+	{
+		assertTrue(FunctionalIterator.of() instanceof EmptyIterator.Of);
+		assertTrue(FunctionalIterator.of("first") instanceof SingletonIterator.Of);
+	}
+
+	@Test
+	void testOf_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterator.of((Object[]) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	void testOfDouble(Iterable<Double> iterable)
+	{
+		FunctionalPrimitiveIterable.OfDouble expected = Reducible.unboxDouble(iterable);
+		double[] arguments = new double[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterator.OfDouble actual = FunctionalIterator.ofDouble(arguments);
+		assertIteratorEquals(expected.iterator(), actual);
+	}
+
+	@Test
+	static void testOfDoubleTypes()
+	{
+		assertTrue(FunctionalIterator.ofDouble() instanceof EmptyIterator.OfDouble);
+		assertTrue(FunctionalIterator.ofDouble(1.0) instanceof SingletonIterator.Of);
+	}
+
+	@Test
+	void testOfDouble_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterator.ofDouble((double[]) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	void testOfInt(Iterable<Integer> iterable)
+	{
+		FunctionalPrimitiveIterable.OfInt expected = Reducible.unboxInt(iterable);
+		int[] arguments = new int[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterator.OfInt actual = FunctionalIterator.ofInt(arguments);
+		assertIteratorEquals(expected.iterator(), actual);
+	}
+
+	@Test
+	void testOfIntTypes()
+	{
+		assertTrue(FunctionalIterator.ofInt() instanceof EmptyIterator.OfInt);
+		assertTrue(FunctionalIterator.ofInt(1) instanceof SingletonIterator.OfInt);
+	}
+
+	@Test
+	void testOfInt_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterator.ofInt((int[]) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	void testOfLong(Iterable<Long> iterable)
+	{
+		FunctionalPrimitiveIterable.OfLong expected = Reducible.unboxLong(iterable);
+		long[] arguments = new long[Math.toIntExact(expected.count())];
+		expected.collect(arguments);
+		FunctionalPrimitiveIterator.OfLong actual = FunctionalIterator.ofLong(arguments);
+		assertIteratorEquals(expected.iterator(), actual);
+	}
+
+	@Test
+	void testOfLongTypes()
+	{
+		assertTrue(FunctionalIterator.ofLong() instanceof EmptyIterator.OfLong);
+		assertTrue(FunctionalIterator.ofLong(1L) instanceof SingletonIterator.OfLong);
+	}
+
+	@Test
+	void testOfLong_Null()
+	{
+		assertThrows(NullPointerException.class, () -> FunctionalIterator.ofLong((long[]) null));
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalIteratorTest.java b/prism/unit-tests/common/iterable/FunctionalIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1b2e4766676ea42fb0e1ca8e20e265be2ce35c7
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalIteratorTest.java
@@ -0,0 +1,193 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface FunctionalIteratorTest<E, T extends FunctionalIterator<E>> extends ReducibleTest<E, T>
+{
+	/**
+	 * Collect elements from an iterator without using method from Reducible.
+	 *
+	 * @param supplier the supplier yielding the iterator
+	 * @return a {@link List} of the iterator elements
+	 */
+	default List<E> collectElements(Supplier<T> supplier)
+	{
+		ArrayList<E> elements = new ArrayList<>();
+		for (Iterator<E> iterator = supplier.get(); iterator.hasNext();) {
+			elements.add(iterator.next());
+		}
+		return elements;
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@DisplayName("release() empties the iterator.")
+	default void testRelease(Supplier<T> supplier)
+	{
+		FunctionalIterator<?> iterator = supplier.get();
+		iterator.release();
+		assertFalse(iterator.hasNext(), "Expected no next element after release()");
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testUnwrap(Supplier<T> supplier)
+	{
+		FunctionalIterator<?> iterator = supplier.get();
+		assertIteratorEquals(supplier.get(), iterator.unwrap());
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testRequireNext(Supplier<T> supplier)
+	{
+		FunctionalIterator<E> iterator = supplier.get().consume();
+		assertThrows(NoSuchElementException.class, iterator::requireNext);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testConsume(Supplier<T> supplier)
+	{
+		ReducibleTest.super.testConsume(supplier);
+		// Just test that the iterator is empty after calling #consume.
+		// There is no way to test whether consume does anything beyond this.
+		FunctionalIterator<E> iterator = supplier.get().consume();
+		assertTrue(iterator.isEmpty(), "Expected empty iterator after consume()");
+		assertFalse(iterator.hasNext(), "Expected no next element after consume()");
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getReducibles"})
+	@Override
+	default void testConcat(Supplier<T> supplier)
+	{
+		ArrayList<Object> expected = supplier.get().collect(new ArrayList<>());
+		supplier.get().collect(expected);
+		FunctionalIterator<E> actual = supplier.get().concat(supplier.get());
+		assertIteratorEquals(expected.iterator(), actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMap(Supplier<T> supplier)
+	{
+		FunctionalIterator<String> expected = supplier.get().map(String::valueOf);
+		FunctionalIterator<String> actual = supplier.get().flatMap(e -> new SingletonIterator.Of<>(String.valueOf(e)));
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToDouble(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		PrimitiveIterator.OfDouble expected = unboxDouble(range.iterator().map((int i) -> (double) i));
+		Range.RangeIterator index = range.iterator();
+		PrimitiveIterator.OfDouble actual = supplier.get().flatMapToDouble(e -> new SingletonIterator.OfDouble(index.next()));
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToInt(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		PrimitiveIterator.OfInt expected = range.iterator();
+		Range.RangeIterator index = range.iterator();
+		PrimitiveIterator.OfInt actual = supplier.get().flatMapToInt(e -> new SingletonIterator.OfInt(index.next()));
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@Override
+	default void testFlatMapToLong(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		PrimitiveIterator.OfLong expected = unboxLong(range.iterator().map((int i) -> (long) i));
+		Range.RangeIterator index = range.iterator();
+		PrimitiveIterator.OfLong actual = supplier.get().flatMapToLong(e -> new SingletonIterator.OfLong(index.next()));
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getSingletonReducibles")
+	@Override
+	default void testFlatMapToNull(Supplier<T> supplier)
+	{
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMap(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt(e -> null).consume());
+		assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong(e -> null).consume());
+	}
+
+	@Test
+	@Override
+	default void testFlatMap_Null()
+	{
+		FunctionalIterator<E> iterator = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> iterator.flatMap(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToInt(null));
+		assertThrows(NullPointerException.class, () -> iterator.flatMapToLong(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@DisplayName("forEach yields same sequence as the underlying iterator.")
+	@Override
+	default void testForEach(Supplier<T> supplier)
+	{
+		List<E> expected = collectElements(supplier);
+		List<Object> actual = new ArrayList<>();
+		supplier.get().forEach(actual::add);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	@DisplayName("forEachRemaining() yields same sequence as the underlying iterator.")
+	default void testForEachRemaining(Supplier<T> supplier)
+	{
+		List<E> expected = collectElements(supplier);
+		List<Object> actual = new ArrayList<>();
+		supplier.get().forEachRemaining(actual::add);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	default void testForEachRemaining_Null()
+	{
+		FunctionalIterator<E> iterator = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> iterator.forEachRemaining(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testIsEmpty(Supplier<T> supplier)
+	{
+		T iterator = supplier.get();
+		assertTrue(iterator.isEmpty() ^ iterator.hasNext());
+	}
+
+
+
+	interface Of<E, T extends FunctionalIterator<E>> extends FunctionalIteratorTest<E, T>, ReducibleTest.Of<E, T>
+	{
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalPrimitiveIterableTest.java b/prism/unit-tests/common/iterable/FunctionalPrimitiveIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a95d63b3aa202dbc77d198fd24f817cdd07481b2
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalPrimitiveIterableTest.java
@@ -0,0 +1,282 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.*;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface FunctionalPrimitiveIterableTest
+{
+	interface OfDouble<T extends FunctionalPrimitiveIterable.OfDouble> extends FunctionalIterableTest<Double,T>, PrimitiveReducibleTest.OfDouble<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToObj(Supplier<T> supplier)
+		{
+			FunctionalIterable<String> expected = supplier.get().map((DoubleFunction<String>) String::valueOf);
+			Iterable<String> actual = supplier.get().flatMap((double d) -> List.of(String.valueOf(d)));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = range.mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfDouble actual = supplier.get().flatMapToDouble((double d) -> new SingletonIterable.OfDouble(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveIterable.OfInt actual = supplier.get().flatMapToInt((double d) -> new SingletonIterable.OfInt(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = range.mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfLong actual = supplier.get().flatMapToLong((double d) -> new SingletonIterable.OfLong(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapDoubleToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((double d) -> null).consume());
+		}
+
+		@Test
+		default void testFlatMapOfDouble_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((DoubleFunction<? extends Iterable<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((DoubleFunction<PrimitiveIterable.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((DoubleFunction<PrimitiveIterable.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((DoubleFunction<PrimitiveIterable.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as the underlying iterator.")
+		@Override
+		default void testForEachDoubleConsumer(Supplier<T> supplier)
+		{
+			T expected = supplier.get();
+			List<Double> actual = new ArrayList<>();
+			supplier.get().forEach((DoubleConsumer) actual::add);
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterable<Double> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterable.OfDouble);
+			// boxed
+			FunctionalIterable<Double> boxed = getAnyReducible().map((DoubleFunction<Double>) Double::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterable.OfDouble);
+		}
+	}
+
+
+
+	interface OfInt<T extends FunctionalPrimitiveIterable.OfInt> extends FunctionalIterableTest<Integer,T>, PrimitiveReducibleTest.OfInt<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToObj(Supplier<T> supplier)
+		{
+			FunctionalIterable<String> expected = supplier.get().map((IntFunction<String>) String::valueOf);
+			FunctionalIterable<String> actual = supplier.get().flatMap((int i) -> List.of(String.valueOf(i)));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = range.mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfDouble actual = supplier.get().flatMapToDouble((int i) -> new SingletonIterable.OfDouble(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveIterable.OfInt actual = supplier.get().flatMapToInt((int i) -> new SingletonIterable.OfInt(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = range.mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfLong actual = supplier.get().flatMapToLong((int i) -> new SingletonIterable.OfLong(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapIntToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((int i) -> null).consume());
+		}
+
+		@Test
+		default void testFlatMapOfInt_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((IntFunction<? extends Iterable<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((IntFunction<PrimitiveIterable.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((IntFunction<PrimitiveIterable.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((IntFunction<PrimitiveIterable.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as the underlying iterator.")
+		@Override
+		default void testForEachIntConsumer(Supplier<T> supplier)
+		{
+			T expected = supplier.get();
+			List<Integer> actual = new ArrayList<>();
+			supplier.get().forEach((IntConsumer) actual::add);
+			assertIterableEquals(unboxInt(expected), unboxInt(actual));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterable<Integer> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterable.OfInt);
+			// boxed
+			FunctionalIterable<Integer> boxed = getAnyReducible().map((IntFunction<Integer>) Integer::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterable.OfInt);
+		}
+	}
+
+
+
+	interface OfLong<T extends FunctionalPrimitiveIterable.OfLong> extends FunctionalIterableTest<Long,T>, PrimitiveReducibleTest.OfLong<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToObj(Supplier<T> supplier)
+		{
+			FunctionalIterable<String> expected = supplier.get().map((LongFunction<String>) String::valueOf);
+			FunctionalIterable<String> actual = supplier.get().flatMap((long l) -> List.of(String.valueOf(l)));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = range.mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfDouble actual = supplier.get().flatMapToDouble((long l) -> new SingletonIterable.OfDouble(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveIterable.OfInt actual = supplier.get().flatMapToInt((long l) -> new SingletonIterable.OfInt(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = range.mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterable.OfLong actual = supplier.get().flatMapToLong((long l) -> new SingletonIterable.OfLong(index.next()));
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapLongToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((long l) -> null).consume());
+		}
+
+		@Test
+		default void testFlatMapOfLong_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((LongFunction<? extends Iterable<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((LongFunction<PrimitiveIterable.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((LongFunction<PrimitiveIterable.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((LongFunction<PrimitiveIterable.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as the underlying iterator.")
+		@Override
+		default void testForEachLongConsumer(Supplier<T> supplier)
+		{
+			T expected = supplier.get();
+			List<Long> actual = new ArrayList<>();
+			supplier.get().forEach((LongConsumer) actual::add);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterable<Long> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterable.OfLong);
+			// boxed
+			FunctionalIterable<Long> boxed = getAnyReducible().map((LongFunction<Long>) Long::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterable.OfLong);
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/FunctionalPrimitiveIteratorTest.java b/prism/unit-tests/common/iterable/FunctionalPrimitiveIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..eba93c3571a0c6bfc9bc5467d0346b2f6bb8c7ad
--- /dev/null
+++ b/prism/unit-tests/common/iterable/FunctionalPrimitiveIteratorTest.java
@@ -0,0 +1,405 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.*;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.Assertions.*;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface FunctionalPrimitiveIteratorTest
+{
+	interface OfDouble<T extends FunctionalPrimitiveIterator.OfDouble> extends FunctionalIteratorTest<Double,T>, PrimitiveReducibleTest.OfDouble<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToObj(Supplier<T> supplier)
+		{
+			FunctionalIterator<String> expected = supplier.get().map((DoubleFunction<String>) String::valueOf);
+			Iterator<String> actual = supplier.get().flatMap((double d) -> List.of(String.valueOf(d)).iterator());
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfDouble expected = range.iterator().mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfDouble actual = supplier.get().flatMapToDouble((double d) -> new SingletonIterator.OfDouble(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToInt(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfInt expected = range.iterator();
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfInt actual = supplier.get().flatMapToInt((double d) -> new SingletonIterator.OfInt(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapDoubleToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfLong expected = range.iterator().mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfLong actual = supplier.get().flatMapToLong((double d) -> new SingletonIterator.OfLong(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapDoubleToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((double d) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((double d) -> null).consume());
+		}
+
+		@Test
+		@Override
+		default void testFlatMapOfDouble_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((DoubleFunction<? extends Iterator<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((DoubleFunction<PrimitiveIterator.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((DoubleFunction<PrimitiveIterator.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((DoubleFunction<PrimitiveIterator.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as forEachRemaining().")
+		@Override
+		default void testForEachDoubleConsumer(Supplier<T> supplier)
+		{
+			List<Double> expected = new ArrayList<>();
+			supplier.get().forEachRemaining((DoubleConsumer) expected::add);
+			T iterator = supplier.get();
+			List<Double> actual = new ArrayList<>();
+			iterator.forEach((DoubleConsumer) actual::add);
+			assertTrue(iterator.isEmpty());
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEachRemaining() yields same sequence as the underlying iterator.")
+		default void testForEachRemainingDoubleConsumer(Supplier<T> supplier)
+		{
+			List<Double> expected = collectElements(supplier);
+			T iterator = supplier.get();
+			List<Double> actual = new ArrayList<>();
+			iterator.forEachRemaining((DoubleConsumer) actual::add);
+			assertTrue(iterator.isEmpty());
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@Test
+		default void testForEachRemainingDoubleConsumer_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.forEachRemaining((DoubleConsumer) null));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterator<Double> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterator.OfDouble);
+			// boxed
+			FunctionalIterator<Double> boxed = getAnyReducible().map((DoubleFunction<Double>) Double::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterator.OfDouble);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMin_Consumed(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.empty();
+			OptionalDouble actual = supplier.get().consume().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMax_Consumed(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.empty();
+			OptionalDouble actual = supplier.get().consume().max();
+			assertEquals(expected, actual);
+		}
+	}
+
+
+
+	interface OfInt<T extends FunctionalPrimitiveIterator.OfInt> extends FunctionalIteratorTest<Integer,T>, PrimitiveReducibleTest.OfInt<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToObj(Supplier<T> supplier)
+		{
+			FunctionalIterator<String> expected = supplier.get().map((IntFunction<String>) String::valueOf);
+			Iterator<String> actual = supplier.get().flatMap((int i) -> List.of(String.valueOf(i)).iterator());
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfDouble expected = range.iterator().mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfDouble actual = supplier.get().flatMapToDouble((int i) -> new SingletonIterator.OfDouble(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToInt(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfInt expected = range.iterator();
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfInt actual = supplier.get().flatMapToInt((int i) -> new SingletonIterator.OfInt(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapIntToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfLong expected = range.iterator().mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfLong actual = supplier.get().flatMapToLong((int i) -> new SingletonIterator.OfLong(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapIntToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((int i) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((int i) -> null).consume());
+		}
+
+		@Test
+		default void testFlatMapOfInt_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((IntFunction<? extends Iterator<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((IntFunction<PrimitiveIterator.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((IntFunction<PrimitiveIterator.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((IntFunction<PrimitiveIterator.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as the underlying iterator.")
+		@Override
+		default void testForEachIntConsumer(Supplier<T> supplier)
+		{
+			List<Integer> expected = new ArrayList<>();
+			supplier.get().forEachRemaining((IntConsumer) expected::add);
+			T iterator = supplier.get();
+			List<Integer> actual = new ArrayList<>();
+			iterator.forEach((IntConsumer) actual::add);
+			assertTrue(iterator.isEmpty());
+			assertIterableEquals(unboxInt(expected), unboxInt(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEachRemaining() yields same sequence as the underlying iterator.")
+		default void testForEachRemainingIntConsumer(Supplier<T> supplier)
+		{
+			List<Integer> expected = collectElements(supplier);
+			List<Integer> actual = new ArrayList<>();
+			supplier.get().forEachRemaining((IntConsumer) actual::add);
+			assertIterableEquals(unboxInt(expected), unboxInt(actual));
+		}
+
+		@Test
+		default void testForEachRemainingIntConsumer_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.forEachRemaining((IntConsumer) null));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterator<Integer> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterator.OfInt);
+			// boxed
+			FunctionalIterator<Integer> boxed = getAnyReducible().map((IntFunction<Integer>) Integer::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterator.OfInt);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMin_Consumed(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.empty();
+			OptionalInt actual = supplier.get().consume().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMax_Consumed(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.empty();
+			OptionalInt actual = supplier.get().consume().max();
+			assertEquals(expected, actual);
+		}
+	}
+
+
+
+	interface OfLong<T extends FunctionalPrimitiveIterator.OfLong> extends FunctionalIteratorTest<Long,T>, PrimitiveReducibleTest.OfLong<T>
+	{
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToObj(Supplier<T> supplier)
+		{
+			FunctionalIterator<String> expected = supplier.get().map((LongFunction<String>) String::valueOf);
+			Iterator<String> actual = supplier.get().flatMap((long l) -> List.of(String.valueOf(l)).iterator());
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfDouble expected = range.iterator().mapToDouble((int i) -> (double) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfDouble actual = supplier.get().flatMapToDouble((long l) -> new SingletonIterator.OfDouble(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToInt(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfInt expected = range.iterator();
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfInt actual = supplier.get().flatMapToInt((long l) -> new SingletonIterator.OfInt(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFlatMapLongToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterator.OfLong expected = range.iterator().mapToLong((int i) -> (long) i);
+			Range.RangeIterator index = range.iterator();
+			PrimitiveIterator.OfLong actual = supplier.get().flatMapToLong((long l) -> new SingletonIterator.OfLong(index.next()));
+			assertIteratorEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testFlatMapLongToNull(Supplier<T> supplier)
+		{
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMap((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToDouble((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToInt((long l) -> null).consume());
+			assertThrows(NullPointerException.class, () -> supplier.get().flatMapToLong((long l) -> null).consume());
+		}
+
+		@Test
+		default void testFlatMapOfLong_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.flatMap((LongFunction<? extends Iterator<?>>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToDouble((LongFunction<PrimitiveIterator.OfDouble>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToInt((LongFunction<PrimitiveIterator.OfInt>) null));
+			assertThrows(NullPointerException.class, () -> iterator.flatMapToLong((LongFunction<PrimitiveIterator.OfLong>) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEach() yields same sequence as the underlying iterator.")
+		@Override
+		default void testForEachLongConsumer(Supplier<T> supplier)
+		{
+			List<Long> expected = new ArrayList<>();
+			supplier.get().forEachRemaining((LongConsumer) expected::add);
+			T iterator = supplier.get();
+			List<Long> actual = new ArrayList<>();
+			iterator.forEach((LongConsumer) actual::add);
+			assertTrue(iterator.isEmpty());
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@DisplayName("forEachRemaining() yields same sequence as the underlying iterator.")
+		default void testForEachRemainingLongConsumer(Supplier<T> supplier)
+		{
+			List<Long> expected = collectElements(supplier);
+			List<Long> actual = new ArrayList<>();
+			supplier.get().forEachRemaining((LongConsumer) actual::add);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@Test
+		default void testForEachRemainingLongConsumer_Null()
+		{
+			T iterator = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> iterator.forEachRemaining((LongConsumer) null));
+		}
+
+		@Test
+		default void testConcatTypes()
+		{
+			// primitive with boxed signature
+			FunctionalIterator<Long> primitive = getAnyReducible();
+			assertTrue(getAnyReducible().concat(primitive) instanceof FunctionalPrimitiveIterator.OfLong);
+			// boxed
+			FunctionalIterator<Long> boxed = getAnyReducible().map((LongFunction<Long>) Long::valueOf);
+			assertFalse(getAnyReducible().concat(boxed) instanceof FunctionalPrimitiveIterator.OfLong);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMin_Consumed(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.empty();
+			OptionalLong actual = supplier.get().consume().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMax_Consumed(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.empty();
+			OptionalLong actual = supplier.get().consume().max();
+			assertEquals(expected, actual);
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/IterableAdaptorTest.java b/prism/unit-tests/common/iterable/IterableAdaptorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b0436ca3f47f0b3e0f390d20ea2daf0cd4724444
--- /dev/null
+++ b/prism/unit-tests/common/iterable/IterableAdaptorTest.java
@@ -0,0 +1,156 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class IterableAdaptorTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements FunctionalIterableTest.Of<Object,IterableAdaptor.Of<Object>>
+	{
+		@Override
+		public IterableAdaptor.Of<Object> getReducible(Object[] objects)
+		{
+			return new IterableAdaptor.Of<>(Arrays.asList(objects));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOf(Object[] objects)
+		{
+			List<Object> expected = Arrays.asList(objects);
+			IterableAdaptor.Of<Object> actual = new IterableAdaptor.Of<>(expected);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOf_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IterableAdaptor.Of<>(null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements FunctionalPrimitiveIterableTest.OfDouble<IterableAdaptor.OfDouble>
+	{
+		@Override
+		public IterableAdaptor.OfDouble getReducible(double[] numbers)
+		{
+			PrimitiveIterable.OfDouble iterable = asNonFunctionalIterable(numbers);
+			return new IterableAdaptor.OfDouble(iterable);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfDouble(double[] numbers)
+		{
+			PrimitiveIterable.OfDouble expected = asNonFunctionalIterable(numbers);
+			IterableAdaptor.OfDouble actual = new IterableAdaptor.OfDouble(expected);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IterableAdaptor.OfDouble(null));
+		}
+
+		private PrimitiveIterable.OfDouble asNonFunctionalIterable(double[] numbers)
+		{
+			List<Double> boxed = FunctionalIterator.ofDouble(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxDouble(boxed);
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements FunctionalPrimitiveIterableTest.OfInt<IterableAdaptor.OfInt>
+	{
+		@Override
+		public IterableAdaptor.OfInt getReducible(int[] numbers)
+		{
+			PrimitiveIterable.OfInt iterable = asNonFunctionalIterable(numbers);
+			return new IterableAdaptor.OfInt(iterable);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfInt(int[] numbers)
+		{
+			PrimitiveIterable.OfInt expected = asNonFunctionalIterable(numbers);
+			IterableAdaptor.OfInt actual = new IterableAdaptor.OfInt(expected);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfInt_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IterableAdaptor.OfInt(null));
+		}
+
+		private PrimitiveIterable.OfInt asNonFunctionalIterable(int[] numbers)
+		{
+			List<Integer> boxed = FunctionalIterator.ofInt(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxInt(boxed);
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements FunctionalPrimitiveIterableTest.OfLong<IterableAdaptor.OfLong>
+	{
+		@Override
+		public IterableAdaptor.OfLong getReducible(long[] numbers)
+		{
+			PrimitiveIterable.OfLong iterable = asNonFunctionalIterable(numbers);
+			return new IterableAdaptor.OfLong(iterable);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfLong(long[] numbers)
+		{
+			PrimitiveIterable.OfLong expected = asNonFunctionalIterable(numbers);
+			IterableAdaptor.OfLong actual = new IterableAdaptor.OfLong(expected);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfLong_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IterableAdaptor.OfLong(null));
+		}
+
+		private PrimitiveIterable.OfLong asNonFunctionalIterable(long[] numbers)
+		{
+			List<Long> boxed = FunctionalIterator.ofLong(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxLong(boxed);
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/IterableArrayTest.java b/prism/unit-tests/common/iterable/IterableArrayTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..041b26b146562d56c53d4cadec1e885e876b96be
--- /dev/null
+++ b/prism/unit-tests/common/iterable/IterableArrayTest.java
@@ -0,0 +1,239 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.Optional;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface IterableArrayTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements IterableArrayTest, FunctionalIterableTest.Of<Object,IterableArray.Of<Object>>
+	{
+		@Override
+		public IterableArray.Of<Object> getReducible(Object[] objects)
+		{
+			return new IterableArray.Of<>(objects);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		public void testOfArray(Object[] array)
+		{
+			IterableArray.Of<Object> iterator = new IterableArray.Of<>(array);
+			Object[] actual = iterator.collect(new Object[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		public void testOfArrayIntInt_All(Object[] array)
+		{
+			IterableArray.Of<Object> expected = new IterableArray.Of<>(array);
+			IterableArray.Of<Object> actual = new IterableArray.Of<>(array, 0, array.length);
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysAsArguments"})
+		public void testOfArrayIntInt_Range(Object[] array)
+		{
+			FunctionalIterable<Object> expected = new Range(1, array.length - 1).map((int i) -> array[i]);
+			IterableArray.Of<Object> actual = new IterableArray.Of<>(array, 1, array.length - 1);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOf_Errors()
+		{
+			Optional<Object[]> any = getMultitonArraysOfObject().findAny();
+			assert any.isPresent();
+			Object[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new IterableArray.Of<>((Object[]) null));
+			assertThrows(NullPointerException.class, () -> new IterableArray.Of<>(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.Of<>(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.Of<>(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.Of<>(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.Of<>(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.Of<>(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements IterableArrayTest, FunctionalPrimitiveIterableTest.OfDouble<IterableArray.OfDouble>
+	{
+		@Override
+		public IterableArray.OfDouble getReducible(double[] numbers)
+		{
+			return new IterableArray.OfDouble(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfDouble", "getSingletonArraysOfDouble", "getMultitonArraysOfDouble"})
+		public void testOfDouble(double[] array)
+		{
+			IterableArray.OfDouble iterator = new IterableArray.OfDouble(array);
+			double[] actual = iterator.collect(new double[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfDouble", "getSingletonArraysOfDouble", "getMultitonArraysOfDouble"})
+		public void testOfDoubleArrayIntInt_All(double[] array)
+		{
+			IterableArray.OfDouble expected = new IterableArray.OfDouble(array);
+			IterableArray.OfDouble actual = new IterableArray.OfDouble(array, 0, array.length);
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfDouble"})
+		public void testOfDoubleArrayIntInt_Range(double[] array)
+		{
+			FunctionalPrimitiveIterable.OfDouble expected = new Range(1, array.length - 1).mapToDouble((int i) -> array[i]);
+			IterableArray.OfDouble actual = new IterableArray.OfDouble(array, 1, array.length - 1);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOfDouble_Errors()
+		{
+			Optional<double[]> any = getMultitonArraysOfDouble().findAny();
+			assert any.isPresent();
+			double[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfDouble((double[]) null));
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfDouble(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfDouble(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfDouble(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfDouble(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfDouble(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfDouble(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements IterableArrayTest, FunctionalPrimitiveIterableTest.OfInt<IterableArray.OfInt>
+	{
+		@Override
+		public IterableArray.OfInt getReducible(int[] numbers)
+		{
+			return new IterableArray.OfInt(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfInt", "getSingletonArraysOfInt", "getMultitonArraysOfInt"})
+		public void testOfInt(int[] array)
+		{
+			IterableArray.OfInt iterator = new IterableArray.OfInt(array);
+			int[] actual = iterator.collect(new int[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfInt", "getSingletonArraysOfInt", "getMultitonArraysOfInt"})
+		public void testOfIntArrayIntInt_All(int[] array)
+		{
+			IterableArray.OfInt expected = new IterableArray.OfInt(array);
+			IterableArray.OfInt actual = new IterableArray.OfInt(array, 0, array.length);
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfInt"})
+		public void testOfIntArrayIntInt_Range(int[] array)
+		{
+			FunctionalPrimitiveIterable.OfInt expected = new Range(1, array.length - 1).mapToInt((int i) -> array[i]);
+			IterableArray.OfInt actual = new IterableArray.OfInt(array, 1, array.length - 1);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOfInt_Errors()
+		{
+			Optional<int[]> any = getMultitonArraysOfInt().findAny();
+			assert any.isPresent();
+			int[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfInt((int[]) null));
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfInt(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfInt(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfInt(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfInt(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfInt(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfInt(array, length+1, length+1));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements IterableArrayTest, FunctionalPrimitiveIterableTest.OfLong<IterableArray.OfLong>
+	{
+		@Override
+		public IterableArray.OfLong getReducible(long[] numbers)
+		{
+			return new IterableArray.OfLong(numbers);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfLong", "getSingletonArraysOfLong", "getMultitonArraysOfLong"})
+		public void testOfLong(long[] array)
+		{
+			IterableArray.OfLong iterator = new IterableArray.OfLong(array);
+			long[] actual = iterator.collect(new long[array.length]);
+			assertArrayEquals(array, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getEmptyArraysOfLong", "getSingletonArraysOfLong", "getMultitonArraysOfLong"})
+		public void testOfLongArrayIntInt_All(long[] array)
+		{
+			IterableArray.OfLong expected = new IterableArray.OfLong(array);
+			IterableArray.OfLong actual = new IterableArray.OfLong(array, 0, array.length);
+			assertIterableEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getMultitonArraysOfLong"})
+		public void testOfLongArrayIntInt_Range(long[] array)
+		{
+			FunctionalPrimitiveIterable.OfLong expected = new Range(1, array.length - 1).mapToLong((int i) -> array[i]);
+			IterableArray.OfLong actual = new IterableArray.OfLong(array, 1, array.length - 1);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOfLong_Errors()
+		{
+			Optional<long[]> any = getMultitonArraysOfLong().findAny();
+			assert any.isPresent();
+			long[] array = any.get();
+			int length = array.length;
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfLong((long[]) null));
+			assertThrows(NullPointerException.class, () -> new IterableArray.OfLong(null, 0, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfLong(array, -1, -1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfLong(array, -1, length));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfLong(array, 1, 0));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfLong(array, 0, length+1));
+			assertThrows(IndexOutOfBoundsException.class, () -> new IterableArray.OfLong(array, length+1, length+1));
+		}
+	}
+}
\ No newline at end of file
diff --git a/prism/unit-tests/common/iterable/IteratorAdaptorTest.java b/prism/unit-tests/common/iterable/IteratorAdaptorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f4cdfdac19508677618769e5b05132d8da4de98
--- /dev/null
+++ b/prism/unit-tests/common/iterable/IteratorAdaptorTest.java
@@ -0,0 +1,198 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class IteratorAdaptorTest
+{
+	@Nested
+	@TestInstance(Lifecycle.PER_CLASS)
+	class Of implements FunctionalIteratorTest.Of<Object,IteratorAdaptor.Of<Object>>
+	{
+		@Override
+		public IteratorAdaptor.Of<Object> getReducible(Object[] objects)
+		{
+			return new IteratorAdaptor.Of<>(Arrays.asList(objects).iterator());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOf(Object[] objects)
+		{
+			List<Object> iterable = Arrays.asList(objects);
+			Iterator<Object> expected = iterable.iterator();
+			IteratorAdaptor.Of<Object> actual = new IteratorAdaptor.Of<>(iterable.iterator());
+			assertIteratorEquals(expected, actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOf_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IteratorAdaptor.Of<>(null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		@DisplayName("unwrap() answers the underlying iterator.")
+		public void testUnwrap(Object[] objects)
+		{
+			Iterator<Object> expected = Arrays.asList(objects).iterator();
+			Iterator<Object> actual = new IteratorAdaptor.Of<>(expected).unwrap();
+			assertSame(expected, actual);
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(Lifecycle.PER_CLASS)
+	class OfDouble implements FunctionalPrimitiveIteratorTest.OfDouble<IteratorAdaptor.OfDouble>
+	{
+		@Override
+		public IteratorAdaptor.OfDouble getReducible(double[] numbers)
+		{
+			PrimitiveIterable.OfDouble iterable = asNonFunctionalIterable(numbers);
+			return new IteratorAdaptor.OfDouble(iterable.iterator());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfDouble(double[] numbers)
+		{
+			PrimitiveIterable.OfDouble expected = asNonFunctionalIterable(numbers);
+			IteratorAdaptor.OfDouble actual = new IteratorAdaptor.OfDouble(expected.iterator());
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfDouble_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IteratorAdaptor.OfDouble(null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		@DisplayName("unwrap() answers the underlying iterator.")
+		public void testUnwrap(double[] numbers)
+		{
+			PrimitiveIterator.OfDouble expected = asNonFunctionalIterable(numbers).iterator();
+			PrimitiveIterator.OfDouble actual = new IteratorAdaptor.OfDouble(expected).unwrap();
+			assertSame(expected, actual);
+		}
+
+		private PrimitiveIterable.OfDouble asNonFunctionalIterable(double[] numbers)
+		{
+			List<Double> boxed = FunctionalIterator.ofDouble(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxDouble(boxed);
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(Lifecycle.PER_CLASS)
+	class OfInt implements FunctionalPrimitiveIteratorTest.OfInt<IteratorAdaptor.OfInt>
+	{
+		@Override
+		public IteratorAdaptor.OfInt getReducible(int[] numbers)
+		{
+			PrimitiveIterable.OfInt iterable = asNonFunctionalIterable(numbers);
+			return new IteratorAdaptor.OfInt(iterable.iterator());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfInt(int[] numbers)
+		{
+			PrimitiveIterable.OfInt expected = asNonFunctionalIterable(numbers);
+			IteratorAdaptor.OfInt actual = new IteratorAdaptor.OfInt(expected.iterator());
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfInt_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IteratorAdaptor.OfInt(null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		@DisplayName("unwrap() answers the underlying iterator.")
+		public void testUnwrap(int[] numbers)
+		{
+			PrimitiveIterator.OfInt expected = asNonFunctionalIterable(numbers).iterator();
+			PrimitiveIterator.OfInt actual = new IteratorAdaptor.OfInt(expected).unwrap();
+			assertSame(expected, actual);
+		}
+
+		private PrimitiveIterable.OfInt asNonFunctionalIterable(int[] numbers)
+		{
+			List<Integer> boxed = FunctionalIterator.ofInt(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxInt(boxed);
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(Lifecycle.PER_CLASS)
+	class OfLong implements FunctionalPrimitiveIteratorTest.OfLong<IteratorAdaptor.OfLong>
+	{
+		@Override
+		public IteratorAdaptor.OfLong getReducible(long[] numbers)
+		{
+			PrimitiveIterable.OfLong iterable = asNonFunctionalIterable(numbers);
+			return new IteratorAdaptor.OfLong(iterable.iterator());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		@DisplayName("Adaptor yields same sequence as the underlying iterator.")
+		public void testOfLong(long[] numbers)
+		{
+			PrimitiveIterable.OfLong expected = asNonFunctionalIterable(numbers);
+			IteratorAdaptor.OfLong actual = new IteratorAdaptor.OfLong(expected.iterator());
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		@DisplayName("Adapter on null throws NullPointerException.")
+		public void testOfLong_Null()
+		{
+			assertThrows(NullPointerException.class, () -> new IteratorAdaptor.OfLong(null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		@DisplayName("unwrap() answers the underlying iterator.")
+		public void testUnwrap(long[] numbers)
+		{
+			PrimitiveIterator.OfLong expected = asNonFunctionalIterable(numbers).iterator();
+			PrimitiveIterator.OfLong actual = new IteratorAdaptor.OfLong(expected).unwrap();
+			assertSame(expected, actual);
+		}
+
+		private PrimitiveIterable.OfLong asNonFunctionalIterable(long[] numbers)
+		{
+			List<Long> boxed = FunctionalIterator.ofLong(numbers).collect(new ArrayList<>());
+			return PrimitiveIterable.unboxLong(boxed);
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/MappingIterableTest.java b/prism/unit-tests/common/iterable/MappingIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0792ef87afc46221b0c9d8f2730cc4f8570abd9
--- /dev/null
+++ b/prism/unit-tests/common/iterable/MappingIterableTest.java
@@ -0,0 +1,587 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface MappingIterableTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToObj implements MappingIterableTest, FunctionalIterableTest.Of<Object,MappingIterable.ObjToObj<Object,Object>>
+	{
+		@Override
+		public MappingIterable.ObjToObj<Object, Object> getReducible(Object[] objects)
+		{
+			Map<Object, Object> lookup = new LinkedHashMap<>();
+			for (int i=0, length=objects.length; i<length; i++) {
+				lookup.put(objects[i], objects[length-1-i]);
+			}
+			return new MappingIterable.ObjToObj<>(lookup.keySet(), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		void testOf(Object[] objects)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (Object each : objects) {
+				expected.add(Objects.toString(each));
+			}
+			Iterable<Object> iterable = new IterableArray.Of<>(objects);
+			Iterable<String> actual = new MappingIterable.ObjToObj<>(iterable, Objects::toString);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			EmptyIterable.Of<Object> iterable = EmptyIterable.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToObj<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToDouble implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfDouble<MappingIterable.ObjToDouble<Double>>
+	{
+		@Override
+		public MappingIterable.ObjToDouble<Double> getReducible(double[] numbers)
+		{
+			List<Double> boxed = FunctionalIterable.ofDouble(numbers).collect(new ArrayList<>());
+			return new MappingIterable.ObjToDouble<>(boxed, Double::doubleValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testObjToDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(d);
+			}
+			FunctionalPrimitiveIterable.OfDouble actual = new MappingIterable.ObjToDouble<>(expected, each -> each);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testObjToDouble_Null()
+		{
+			EmptyIterable.Of<Double> iterable = EmptyIterable.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToDouble<Double>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToDouble<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToInt implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfInt<MappingIterable.ObjToInt<Integer>>
+	{
+		@Override
+		public MappingIterable.ObjToInt<Integer> getReducible(int[] numbers)
+		{
+			List<Integer> boxed = FunctionalIterable.ofInt(numbers).collect(new ArrayList<>());
+			return new MappingIterable.ObjToInt<>(boxed, Integer::intValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testObjToInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i);
+			}
+			FunctionalPrimitiveIterable.OfInt actual = new MappingIterable.ObjToInt<>(expected, each -> each);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testObjToInt_Null()
+		{
+			EmptyIterable.Of<Integer> iterable = EmptyIterable.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToInt<Integer>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToInt<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToLong implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfLong<MappingIterable.ObjToLong<Long>>
+	{
+		@Override
+		public MappingIterable.ObjToLong<Long> getReducible(long[] numbers)
+		{
+			List<Long> boxed = FunctionalIterable.ofLong(numbers).collect(new ArrayList<>());
+			return new MappingIterable.ObjToLong<>(boxed, Long::longValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testObjToLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l);
+			}
+			FunctionalPrimitiveIterable.OfLong actual = new MappingIterable.ObjToLong<>(expected, each -> each);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testObjToLong_Null()
+		{
+			EmptyIterable.Of<Long> iterable = EmptyIterable.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToLong<Long>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.ObjToLong<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToObj implements MappingIterableTest, FunctionalIterableTest.Of<Object,MappingIterable.DoubleToObj<Object>>
+	{
+		@Override
+		public MappingIterable.DoubleToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Double, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterable.of(objects).reduce(1.5, (d, e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterable.DoubleToObj<>(unboxDouble(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToObj(double[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(Objects.toString(d));
+			}
+			IterableArray.OfDouble iterable = new IterableArray.OfDouble(numbers);
+			Iterable<String> actual = new MappingIterable.DoubleToObj<>(iterable, Objects::toString);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testDoubleToObj_Null()
+		{
+			EmptyIterable.OfDouble iterable = EmptyIterable.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToObj<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToDouble implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfDouble<MappingIterable.DoubleToDouble>
+	{
+		@Override
+		public MappingIterable.DoubleToDouble getReducible(double[] numbers)
+		{
+			Map<Double, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofDouble(numbers).reduce(1.5, (double d, double e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterable.DoubleToDouble(unboxDouble(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(d + 1.0);
+			}
+			IterableArray.OfDouble iterable = new IterableArray.OfDouble(numbers);
+			PrimitiveIterable.OfDouble actual = new MappingIterable.DoubleToDouble(iterable, d -> d + 1.0);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testDoubleToDouble_Null()
+		{
+			EmptyIterable.OfDouble iterable = EmptyIterable.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToDouble(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToInt implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfInt<MappingIterable.DoubleToInt>
+	{
+		@Override
+		public MappingIterable.DoubleToInt getReducible(int[] numbers)
+		{
+			Map<Double, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofInt(numbers).reduce(1.5, (Double d, Integer e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterable.DoubleToInt(unboxDouble(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToInt(double[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add((int) d + 1);
+			}
+			IterableArray.OfDouble iterable = new IterableArray.OfDouble(numbers);
+			PrimitiveIterable.OfInt actual = new MappingIterable.DoubleToInt(iterable, d -> (int) d + 1);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testDoubleToInt_Null()
+		{
+			EmptyIterable.OfDouble iterable = EmptyIterable.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToInt(null, each -> (int) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToInt(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToLong implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfLong<MappingIterable.DoubleToLong>
+	{
+		@Override
+		public MappingIterable.DoubleToLong getReducible(long[] numbers)
+		{
+			Map<Double, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofLong(numbers).reduce(1.5, (double d, long e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterable.DoubleToLong(unboxDouble(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToLong(double[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add((long) d + 1);
+			}
+			IterableArray.OfDouble iterable = new IterableArray.OfDouble(numbers);
+			PrimitiveIterable.OfLong actual = new MappingIterable.DoubleToLong(iterable, d -> (long) d + 1);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testDoubleToLong_Null()
+		{
+			EmptyIterable.OfDouble iterable = EmptyIterable.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToLong(null, each -> (long) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.DoubleToLong(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToObj implements MappingIterableTest, FunctionalIterableTest.Of<Object,MappingIterable.IntToObj<Object>>
+	{
+		@Override
+		public MappingIterable.IntToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Integer, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterable.of(objects).reduce(1, (int i, Object e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterable.IntToObj<>(unboxInt(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToObj(int[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(Objects.toString(i));
+			}
+			IterableArray.OfInt iterable = new IterableArray.OfInt(numbers);
+			Iterable<String> actual = new MappingIterable.IntToObj<>(iterable, Objects::toString);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testIntToObj_Null()
+		{
+			EmptyIterable.OfInt iterable = EmptyIterable.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToObj<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToDouble implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfDouble<MappingIterable.IntToDouble>
+	{
+		@Override
+		public MappingIterable.IntToDouble getReducible(double[] numbers)
+		{
+			Map<Integer, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofDouble(numbers).reduce(1, (int i, double e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterable.IntToDouble(unboxInt(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToDouble(int[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i + 1.0);
+			}
+			IterableArray.OfInt iterable = new IterableArray.OfInt(numbers);
+			PrimitiveIterable.OfDouble actual = new MappingIterable.IntToDouble(iterable, i -> i + 1.0);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testIntToDouble_Null()
+		{
+			EmptyIterable.OfInt iterable = EmptyIterable.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToDouble(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToInt implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfInt<MappingIterable.IntToInt>
+	{
+		@Override
+		public MappingIterable.IntToInt getReducible(int[] numbers)
+		{
+			Map<Integer, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofInt(numbers).reduce(1, (int i, int e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterable.IntToInt(unboxInt(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i + 1);
+			}
+			IterableArray.OfInt iterable = new IterableArray.OfInt(numbers);
+			PrimitiveIterable.OfInt actual = new MappingIterable.IntToInt(iterable, i -> i + 1);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testIntToInt_Null()
+		{
+			EmptyIterable.OfInt iterable = EmptyIterable.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToInt(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToInt(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToLong implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfLong<MappingIterable.IntToLong>
+	{
+		@Override
+		public MappingIterable.IntToLong getReducible(long[] numbers)
+		{
+			Map<Integer, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofLong(numbers).reduce(1, (Integer i, Long e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterable.IntToLong(unboxInt(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToLong(int[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add((long) i + 1);
+			}
+			IterableArray.OfInt iterable = new IterableArray.OfInt(numbers);
+			PrimitiveIterable.OfLong actual = new MappingIterable.IntToLong(iterable, i -> (long) i + 1);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testIntToLong_Null()
+		{
+			EmptyIterable.OfInt iterable = EmptyIterable.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToLong(null, each -> (long) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.IntToLong(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToObj implements MappingIterableTest, FunctionalIterableTest.Of<Object,MappingIterable.LongToObj<Object>>
+	{
+		@Override
+		public MappingIterable.LongToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Long, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterable.of(objects).reduce(1L, (long l, Object e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterable.LongToObj<>(unboxLong(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToObj(long[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(Objects.toString(l));
+			}
+			IterableArray.OfLong iterable = new IterableArray.OfLong(numbers);
+			Iterable<String> actual = new MappingIterable.LongToObj<>(iterable, Objects::toString);
+			assertIterableEquals(expected, actual);
+		}
+
+		@Test
+		public void testLongToObj_Null()
+		{
+			EmptyIterable.OfLong iterable = EmptyIterable.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToObj<>(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToDouble implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfDouble<MappingIterable.LongToDouble>
+	{
+		@Override
+		public MappingIterable.LongToDouble getReducible(double[] numbers)
+		{
+			Map<Long, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofDouble(numbers).reduce(1L, (long l, double e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterable.LongToDouble(unboxLong(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToDouble(long[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l + 1.0);
+			}
+			IterableArray.OfLong iterable = new IterableArray.OfLong(numbers);
+			PrimitiveIterable.OfDouble actual = new MappingIterable.LongToDouble(iterable, l -> l + 1.0);
+			assertIterableEquals(unboxDouble(expected), actual);
+		}
+
+		@Test
+		public void testLongToDouble_Null()
+		{
+			EmptyIterable.OfLong iterable = EmptyIterable.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToDouble(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToInt implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfInt<MappingIterable.LongToInt>
+	{
+		@Override
+		public MappingIterable.LongToInt getReducible(int[] numbers)
+		{
+			Map<Long, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofInt(numbers).reduce(1L, (Long l, Integer e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterable.LongToInt(unboxLong(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToInt(long[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add((int) l + 1);
+			}
+			IterableArray.OfLong iterable = new IterableArray.OfLong(numbers);
+			PrimitiveIterable.OfInt actual = new MappingIterable.LongToInt(iterable, l -> (int) l + 1);
+			assertIterableEquals(unboxInt(expected), actual);
+		}
+
+		@Test
+		public void testLongToInt_Null()
+		{
+			EmptyIterable.OfLong iterable = EmptyIterable.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToInt(null, each -> (int) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToInt(iterable, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToLong implements MappingIterableTest, FunctionalPrimitiveIterableTest.OfLong<MappingIterable.LongToLong>
+	{
+		@Override
+		public MappingIterable.LongToLong getReducible(long[] numbers)
+		{
+			Map<Long, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterable.ofLong(numbers).reduce(1L, (long l, long e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterable.LongToLong(unboxLong(lookup.keySet()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l + 1);
+			}
+			IterableArray.OfLong iterable = new IterableArray.OfLong(numbers);
+			PrimitiveIterable.OfLong actual = new MappingIterable.LongToLong(iterable, l -> l + 1);
+			assertIterableEquals(unboxLong(expected), actual);
+		}
+
+		@Test
+		public void testLongToLong_Null()
+		{
+			EmptyIterable.OfLong iterable = EmptyIterable.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToLong(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterable.LongToLong(iterable, null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/MappingIteratorTest.java b/prism/unit-tests/common/iterable/MappingIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..33b5eaab11b7096e49194531b4ca20e451d3f6c2
--- /dev/null
+++ b/prism/unit-tests/common/iterable/MappingIteratorTest.java
@@ -0,0 +1,589 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+interface MappingIteratorTest
+{
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToObj implements MappingIteratorTest, FunctionalIteratorTest.Of<Object,MappingIterator.ObjToObj<Object,Object>>
+	{
+		@Override
+		public MappingIterator.ObjToObj<Object, Object> getReducible(Object[] objects)
+		{
+			Map<Object, Object> lookup = new LinkedHashMap<>();
+			for (int i=0, length=objects.length; i<length; i++) {
+				lookup.put(objects[i], objects[length-1-i]);
+			}
+			return new MappingIterator.ObjToObj<>(lookup.keySet().iterator(), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysAsArguments")
+		void testOf(Object[] objects)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (Object each : objects) {
+				expected.add(Objects.toString(each));
+			}
+			Iterator<Object> iterator = new ArrayIterator.Of<>(objects);
+			Iterator<String> actual = new MappingIterator.ObjToObj<>(iterator, Objects::toString);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testOf_Null()
+		{
+			Iterator<Object> iterator = EmptyIterator.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToObj<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToDouble implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<MappingIterator.ObjToDouble<Double>>
+	{
+		@Override
+		public MappingIterator.ObjToDouble<Double> getReducible(double[] numbers)
+		{
+			List<Double> boxed = FunctionalIterator.ofDouble(numbers).collect(new ArrayList<>());
+			return new MappingIterator.ObjToDouble<>(boxed.iterator(), Double::doubleValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testObjToDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(d);
+			}
+			Iterator<Double> iterator = expected.iterator();
+			FunctionalPrimitiveIterator.OfDouble actual = new MappingIterator.ObjToDouble<>(iterator, each -> each);
+			assertIteratorEquals(unboxDouble(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testObjToDouble_Null()
+		{
+			Iterator<Double> iterator = EmptyIterator.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToDouble<Double>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToDouble<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToInt implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<MappingIterator.ObjToInt<Integer>>
+	{
+		@Override
+		public MappingIterator.ObjToInt<Integer> getReducible(int[] numbers)
+		{
+			List<Integer> boxed = FunctionalIterator.ofInt(numbers).collect(new ArrayList<>());
+			return new MappingIterator.ObjToInt<>(boxed.iterator(), Integer::intValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testObjToInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i);
+			}
+			Iterator<Integer> iterator = expected.iterator();
+			FunctionalPrimitiveIterator.OfInt actual = new MappingIterator.ObjToInt<>(iterator, each -> each);
+			assertIteratorEquals(unboxInt(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testObjToInt_Null()
+		{
+			Iterator<Integer> iterator = EmptyIterator.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToInt<Integer>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToInt<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class ObjToLong implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<MappingIterator.ObjToLong<Long>>
+	{
+		@Override
+		public MappingIterator.ObjToLong<Long> getReducible(long[] numbers)
+		{
+			List<Long> boxed = FunctionalIterator.ofLong(numbers).collect(new ArrayList<>());
+			return new MappingIterator.ObjToLong<>(boxed.iterator(), Long::longValue);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testObjToLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l);
+			}
+			Iterator<Long> iterator = expected.iterator();
+			FunctionalPrimitiveIterator.OfLong actual = new MappingIterator.ObjToLong<>(iterator, each -> each);
+			assertIteratorEquals(unboxLong(expected.iterator()), actual);
+		}
+
+		@Test
+		public void testObjToLong_Null()
+		{
+			Iterator<Long> iterator = EmptyIterator.of();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToLong<Long>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.ObjToLong<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToObj implements MappingIteratorTest, FunctionalIteratorTest.Of<Object,MappingIterator.DoubleToObj<Object>>
+	{
+		@Override
+		public MappingIterator.DoubleToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Double, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterator.of(objects).reduce(1.5, (d, e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterator.DoubleToObj<>(unboxDouble(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToObj(double[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(Objects.toString(d));
+			}
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(numbers);
+			Iterator<String> actual = new MappingIterator.DoubleToObj<>(iterator, Objects::toString);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testDoubleToObj_Null()
+		{
+			PrimitiveIterator.OfDouble iterator = EmptyIterator.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToObj<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToDouble implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<MappingIterator.DoubleToDouble>
+	{
+		@Override
+		public MappingIterator.DoubleToDouble getReducible(double[] numbers)
+		{
+			Map<Double, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofDouble(numbers).reduce(1.5, (double d, double e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterator.DoubleToDouble(unboxDouble(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToDouble(double[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add(d + 1.0);
+			}
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(numbers);
+			PrimitiveIterator.OfDouble actual = new MappingIterator.DoubleToDouble(iterator, d -> d + 1.0);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testDoubleToDouble_Null()
+		{
+			PrimitiveIterator.OfDouble iterator = EmptyIterator.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToDouble(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToInt implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<MappingIterator.DoubleToInt>
+	{
+		@Override
+		public MappingIterator.DoubleToInt getReducible(int[] numbers)
+		{
+			Map<Double, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofInt(numbers).reduce(1.5, (Double d, Integer e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterator.DoubleToInt(unboxDouble(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToInt(double[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add((int) d + 1);
+			}
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(numbers);
+			PrimitiveIterator.OfInt actual = new MappingIterator.DoubleToInt(iterator, d -> (int) d + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testDoubleToInt_Null()
+		{
+			PrimitiveIterator.OfDouble iterator = EmptyIterator.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToInt(null, each -> (int) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToInt(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class DoubleToLong implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<MappingIterator.DoubleToLong>
+	{
+		@Override
+		public MappingIterator.DoubleToLong getReducible(long[] numbers)
+		{
+			Map<Double, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofLong(numbers).reduce(1.5, (double d, long e) -> {lookup.put(d, e); return d + 1;});
+			return new MappingIterator.DoubleToLong(unboxDouble(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfDouble")
+		public void testDoubleToLong(double[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (double d : numbers) {
+				expected.add((long) d + 1);
+			}
+			ArrayIterator.OfDouble iterator = new ArrayIterator.OfDouble(numbers);
+			PrimitiveIterator.OfLong actual = new MappingIterator.DoubleToLong(iterator, d -> (long) d + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testDoubleToLong_Null()
+		{
+			PrimitiveIterator.OfDouble iterator = EmptyIterator.ofDouble();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToLong(null, each -> (long) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.DoubleToLong(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToObj implements MappingIteratorTest, FunctionalIteratorTest.Of<Object,MappingIterator.IntToObj<Object>>
+	{
+		@Override
+		public MappingIterator.IntToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Integer, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterator.of(objects).reduce(1, (int i, Object e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterator.IntToObj<>(unboxInt(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToObj(int[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(Objects.toString(i));
+			}
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(numbers);
+			Iterator<String> actual = new MappingIterator.IntToObj<>(iterator, Objects::toString);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testIntToObj_Null()
+		{
+			PrimitiveIterator.OfInt iterator = EmptyIterator.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToObj<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToDouble implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<MappingIterator.IntToDouble>
+	{
+		@Override
+		public MappingIterator.IntToDouble getReducible(double[] numbers)
+		{
+			Map<Integer, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofDouble(numbers).reduce(1, (int i, double e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterator.IntToDouble(unboxInt(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToDouble(int[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i + 1.0);
+			}
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(numbers);
+			PrimitiveIterator.OfDouble actual = new MappingIterator.IntToDouble(iterator, i -> i + 1.0);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testIntToDouble_Null()
+		{
+			PrimitiveIterator.OfInt iterator = EmptyIterator.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToDouble(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToInt implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<MappingIterator.IntToInt>
+	{
+		@Override
+		public MappingIterator.IntToInt getReducible(int[] numbers)
+		{
+			Map<Integer, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofInt(numbers).reduce(1, (int i, int e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterator.IntToInt(unboxInt(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToInt(int[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add(i + 1);
+			}
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(numbers);
+			PrimitiveIterator.OfInt actual = new MappingIterator.IntToInt(iterator, i -> i + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testIntToInt_Null()
+		{
+			PrimitiveIterator.OfInt iterator = EmptyIterator.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToInt(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToInt(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class IntToLong implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<MappingIterator.IntToLong>
+	{
+		@Override
+		public MappingIterator.IntToLong getReducible(long[] numbers)
+		{
+			Map<Integer, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofLong(numbers).reduce(1, (Integer i, Long e) -> {lookup.put(i, e); return i + 1;});
+			return new MappingIterator.IntToLong(unboxInt(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfInt")
+		public void testIntToLong(int[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (int i : numbers) {
+				expected.add((long) i + 1);
+			}
+			ArrayIterator.OfInt iterator = new ArrayIterator.OfInt(numbers);
+			PrimitiveIterator.OfLong actual = new MappingIterator.IntToLong(iterator, i -> (long) i + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testIntToLong_Null()
+		{
+			PrimitiveIterator.OfInt iterator = EmptyIterator.ofInt();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToLong(null, each -> (long) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.IntToLong(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToObj implements MappingIteratorTest, FunctionalIteratorTest.Of<Object,MappingIterator.LongToObj<Object>>
+	{
+		@Override
+		public MappingIterator.LongToObj<Object> getReducible(Object[] objects)
+		{
+			Map<Long, Object> lookup = new LinkedHashMap<>();
+			FunctionalIterator.of(objects).reduce(1L, (long l, Object e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterator.LongToObj<>(unboxLong(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToObj(long[] numbers)
+		{
+			ArrayList<String> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(Objects.toString(l));
+			}
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(numbers);
+			Iterator<String> actual = new MappingIterator.LongToObj<>(iterator, Objects::toString);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testLongToObj_Null()
+		{
+			PrimitiveIterator.OfLong iterator = EmptyIterator.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToObj<>(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToObj<>(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToDouble implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfDouble<MappingIterator.LongToDouble>
+	{
+		@Override
+		public MappingIterator.LongToDouble getReducible(double[] numbers)
+		{
+			Map<Long, Double> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofDouble(numbers).reduce(1L, (long l, double e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterator.LongToDouble(unboxLong(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToDouble(long[] numbers)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l + 1.0);
+			}
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(numbers);
+			PrimitiveIterator.OfDouble actual = new MappingIterator.LongToDouble(iterator, l -> l + 1.0);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testLongToDouble_Null()
+		{
+			PrimitiveIterator.OfLong iterator = EmptyIterator.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToDouble(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToDouble(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToInt implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfInt<MappingIterator.LongToInt>
+	{
+		@Override
+		public MappingIterator.LongToInt getReducible(int[] numbers)
+		{
+			Map<Long, Integer> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofInt(numbers).reduce(1L, (Long l, Integer e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterator.LongToInt(unboxLong(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToInt(long[] numbers)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add((int) l + 1);
+			}
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(numbers);
+			PrimitiveIterator.OfInt actual = new MappingIterator.LongToInt(iterator, l -> (int) l + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testLongToInt_Null()
+		{
+			PrimitiveIterator.OfLong iterator = EmptyIterator.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToInt(null, each -> (int) each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToInt(iterator, null));
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class LongToLong implements MappingIteratorTest, FunctionalPrimitiveIteratorTest.OfLong<MappingIterator.LongToLong>
+	{
+		@Override
+		public MappingIterator.LongToLong getReducible(long[] numbers)
+		{
+			Map<Long, Long> lookup = new LinkedHashMap<>();
+			FunctionalIterator.ofLong(numbers).reduce(1L, (long l, long e) -> {lookup.put(l, e); return l + 1;});
+			return new MappingIterator.LongToLong(unboxLong(lookup.keySet().iterator()), lookup::get);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getArraysOfLong")
+		public void testLongToLong(long[] numbers)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			for (long l : numbers) {
+				expected.add(l + 1);
+			}
+			ArrayIterator.OfLong iterator = new ArrayIterator.OfLong(numbers);
+			PrimitiveIterator.OfLong actual = new MappingIterator.LongToLong(iterator, l -> l + 1);
+			assertIteratorEquals(expected.iterator(), actual);
+		}
+
+		@Test
+		public void testLongToLong_Null()
+		{
+			PrimitiveIterator.OfLong iterator = EmptyIterator.ofLong();
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToLong(null, each -> each));
+			assertThrows(NullPointerException.class, () -> new MappingIterator.LongToLong(iterator, null));
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/PrimitiveIterableTest.java b/prism/unit-tests/common/iterable/PrimitiveIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5947038d56e0f562b090e47adb6df32e57f851d
--- /dev/null
+++ b/prism/unit-tests/common/iterable/PrimitiveIterableTest.java
@@ -0,0 +1,117 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.PrimitiveIterator;
+
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class PrimitiveIterableTest
+{
+	@Test
+	public void testUnboxDoubleIterable()
+	{
+		Iterable<Double> numbers = List.of(0.0, 1.0, 2.0);
+		PrimitiveIterable.OfDouble actual = PrimitiveIterable.unboxDouble(numbers);
+		PrimitiveIterator.OfDouble expected = PrimitiveIterable.unboxDouble(numbers.iterator());
+		assertIteratorEquals(expected, actual.iterator());
+	}
+
+	@Test
+	public void testUnboxDoubleIterator()
+	{
+		Iterable<Double> numbers = List.of(0.0, 1.0, 2.0);
+		PrimitiveIterator.OfDouble actual = PrimitiveIterable.unboxDouble(numbers).iterator();
+		for (double d : numbers) {
+			assertEquals(d, actual.nextDouble());
+		}
+	}
+
+	@Test
+	public void testUnboxDoubleIterator_NullValues()
+	{
+		Iterable<Double> numbers = Arrays.asList(new Double[] {null});
+		PrimitiveIterator.OfDouble actual = PrimitiveIterable.unboxDouble(numbers).iterator();
+		assertThrows(NullPointerException.class, actual::nextDouble);
+	}
+
+	@Test
+	public void testUnboxDouble_Null()
+	{
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxDouble((Iterator<Double>) null));
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxDouble((Iterable<Double>) null));
+	}
+
+	@Test
+	public void testUnboxIntIterable()
+	{
+		Iterable<Integer> numbers = List.of(0, 1, 2);
+		PrimitiveIterable.OfInt actual = PrimitiveIterable.unboxInt(numbers);
+		PrimitiveIterator.OfInt expected = PrimitiveIterable.unboxInt(numbers.iterator());
+		assertIteratorEquals(expected, actual.iterator());
+	}
+
+	@Test
+	public void testUnboxIntIterator()
+	{
+		Iterable<Integer> numbers = List.of(0, 1, 2);
+		PrimitiveIterator.OfInt actual = PrimitiveIterable.unboxInt(numbers).iterator();
+		for (int i : numbers) {
+			assertEquals(i, actual.nextInt());
+		}
+	}
+
+	@Test
+	public void testUnboxIntIterator_NullValues()
+	{
+		Iterable<Integer> numbers = Arrays.asList(new Integer[] {null});
+		PrimitiveIterator.OfInt actual = PrimitiveIterable.unboxInt(numbers).iterator();
+		assertThrows(NullPointerException.class, actual::nextInt);
+	}
+
+	@Test
+	public void testUnboxInt_Null()
+	{
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxInt((Iterator<Integer>) null));
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxInt((Iterable<Integer>) null));
+	}
+
+	@Test
+	public void testLongDoubleIterable()
+	{
+		Iterable<Long> numbers = List.of(0L, 1L, 2L);
+		PrimitiveIterable.OfLong actual = PrimitiveIterable.unboxLong(numbers);
+		PrimitiveIterator.OfLong expected = PrimitiveIterable.unboxLong(numbers.iterator());
+		assertIteratorEquals(expected, actual.iterator());
+	}
+
+	@Test
+	public void testUnboxLongIterator()
+	{
+		Iterable<Long> numbers = List.of(0L, 1L, 2L);
+		PrimitiveIterator.OfLong actual = PrimitiveIterable.unboxLong(numbers).iterator();
+		for (long l : numbers) {
+			assertEquals(l, actual.nextLong());
+		}
+	}
+
+	@Test
+	public void testUnboxLongIterator_NullValues()
+	{
+		Iterable<Long> numbers = Arrays.asList(new Long[] {null});
+		PrimitiveIterator.OfLong actual = PrimitiveIterable.unboxLong(numbers).iterator();
+		assertThrows(NullPointerException.class, actual::nextLong);
+	}
+
+	@Test
+	public void testUnboxLong_Null()
+	{
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxLong((Iterator<Long>) null));
+		assertThrows(NullPointerException.class, () -> PrimitiveIterable.unboxLong((Iterable<Long>) null));
+	}
+}
diff --git a/prism/unit-tests/common/iterable/PrimitiveReducibleTest.java b/prism/unit-tests/common/iterable/PrimitiveReducibleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..48da47f616ba61da85205c8117d9ed4110ef04f3
--- /dev/null
+++ b/prism/unit-tests/common/iterable/PrimitiveReducibleTest.java
@@ -0,0 +1,1889 @@
+package common.iterable;
+
+import common.functions.DoubleLongToDoubleFunction;
+import common.functions.IntDoubleToIntFunction;
+import common.functions.IntLongToIntFunction;
+import common.functions.LongDoubleToLongFunction;
+import common.functions.ObjDoubleFunction;
+import common.functions.ObjIntFunction;
+import common.functions.ObjLongFunction;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.Assertions.*;
+import static common.iterable.PrimitiveIterable.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+
+public interface PrimitiveReducibleTest
+{
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	interface OfDouble<T extends PrimitiveReducible.OfDouble<?>> extends ReducibleTest<Double, T>
+	{
+		@Override
+		default Stream<Supplier<T>> getDuplicatesReducibles()
+		{
+			return getDuplicatesArraysOfDouble().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getEmptyReducibles()
+		{
+			return getEmptyArraysOfDouble().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getSingletonReducibles()
+		{
+			return getSingletonArraysOfDouble().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getMultitonReducibles()
+		{
+			return getMultitonArraysOfDouble().map(args-> () -> getReducible(args));
+		}
+
+		T getReducible(double[] arguments);
+
+		@Override
+		default Iterable<Object> getExcluded(T reducible)
+		{
+			List<Double> candidates = getExclusionListOfDouble();
+			reducible.forEach((double d) -> {
+				candidates.remove(d);
+				if (d == 0.0) {
+					candidates.remove(-1 * d);
+				}
+			});
+			List<Object> excluded = new ArrayList<>();
+			excluded.addAll(getUniqueObjects());
+			excluded.addAll(candidates);
+			return excluded;
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectDoubleArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count();
+			double[] expected = new double[n];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((double d) -> expected[index.nextInt()] = d);
+			double[] actual = supplier.get().collect(new double[n]);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectDoubleArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			double[] expected = new double[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((double d) -> expected[index.nextInt()] = d);
+			double[] actual = supplier.get().collect(new double[offset + n + tail], offset);
+			assertArrayEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectOfDouble_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collect((double[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collect((double[]) null, 0));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountDoubleArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), tail = 3;
+			double[] expected = new double[n + tail];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((double d) -> expected[index.nextInt()] = d);
+			double[] actual = new double[n + tail];
+			long count = supplier.get().collectAndCount(actual);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountDoubleArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			double[] expected = new double[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((double d) -> expected[index.nextInt()] = d);
+			double[] actual = new double[offset + n + tail];
+			long count = supplier.get().collectAndCount(actual, offset);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectAndCountOfDouble_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((double[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((double[]) null, 0));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+		@Override
+		default void testCollectDistinct(Supplier<T> supplier)
+		{
+			Set<Double> expected = new HashSet<>();
+			supplier.get().forEach((double d) -> {
+				if (!expected.contains(d) && !(d == 0.0 && expected.contains(-1.0 * d))) {
+					expected.add(d);
+				}
+			});
+			List<Double> actual = supplier.get().collectDistinct().collect(new ArrayList<>());
+			assertTrue(expected.containsAll(actual), "actual =< expected");
+			assertTrue(actual.containsAll(expected), "actual >= expected");
+
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFilterDouble(Supplier<T> supplier)
+		{
+			ArrayList<Double> expected = new ArrayList<>();
+			supplier.get().forEach((double d) -> {
+				if (d % 2 == 0) {
+					expected.add(d);
+				}
+			});
+			PrimitiveReducible.OfDouble<?> actual = supplier.get().filter((double d) -> d % 2 == 0);
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testFilterOfDouble_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.filter((DoublePredicate) null));
+		}
+
+		void testFlatMapDoubleToObj(Supplier<T> supplier);
+
+		void testFlatMapDoubleToDouble(Supplier<T> supplier);
+
+		void testFlatMapDoubleToInt(Supplier<T> supplier);
+
+		void testFlatMapDoubleToLong(Supplier<T> supplier);
+
+		void testFlatMapDoubleToNull(Supplier<T> supplier);
+
+		void testFlatMapOfDouble_Null();
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapDoubleToObj(Supplier<T> supplier)
+		{
+			String prefix = "Item: ";
+			List<String> expected = new ArrayList<>();
+			supplier.get().forEach((double d) -> expected.add(prefix + d));
+			Reducible<String, ?> actual = supplier.get().map((double d) -> prefix + d);
+			assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapDoubleToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = unboxDouble(range.map((int i) -> (double) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfDouble<?> actual = supplier.get().mapToDouble((double d) -> index.next());
+			assertIterableEquals(expected, unboxDouble(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapDoubleToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveReducible.OfInt<?> actual = supplier.get().mapToInt((double d) -> index.next());
+			assertIterableEquals(expected, unboxInt(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapDoubleToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = unboxLong(range.map((int i) -> (long) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfLong<?> actual = supplier.get().mapToLong((double d) -> index.next());
+			assertIterableEquals(expected, unboxLong(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testMapOfDouble_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.map((DoubleFunction<?>) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToDouble((DoubleUnaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToInt((DoubleToIntFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToLong((DoubleToLongFunction) null));
+		}
+
+		void testForEachDoubleConsumer(Supplier<T> supplier);
+
+		@Test
+		default void testForEachDoubleConsumer_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.forEach((DoubleConsumer) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testReduceDoubleBinaryOperator_Empty(Supplier<T> supplier)
+		{
+			DoubleBinaryOperator dummy = (res, each) -> Double.MIN_VALUE;
+			OptionalDouble actual = supplier.get().reduce(dummy);
+			assertEquals(OptionalDouble.empty(), actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testReduceDoubleBinaryOperator_Singleton(Supplier<T> supplier)
+		{
+			DoubleBinaryOperator dummy = (res, each) -> Double.MIN_VALUE;
+			double expected = supplier.get().detect((double d) -> true);
+			OptionalDouble actual = supplier.get().reduce(dummy);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testReduceDoubleBinaryOperator_Multiton(Supplier<T> supplier)
+		{
+			List<Double> expected = supplier.get().collect(new ArrayList<>());
+			List<Double> actual = new ArrayList<>();
+			double probe = -31; // "unique" value
+			DoubleBinaryOperator collect = (res, each) -> {
+				if (actual.isEmpty()) {
+					actual.add(res);
+					actual.add(each);
+					return probe;
+				} else {
+					actual.add(each);
+					return res;
+				}
+			};
+			OptionalDouble result = supplier.get().reduce(collect);
+			assertTrue(result.isPresent());
+			assertEquals(probe, result.getAsDouble());
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceIntDoubleToIntFunction(Supplier<T> supplier)
+		{
+			List<Double> expected = supplier.get().collect(new ArrayList<>());
+			int init = Integer.MIN_VALUE;
+			List<Double> actual = new ArrayList<>();
+			IntDoubleToIntFunction collect = (res, each) -> {actual.add(each); return res;};
+			int result = supplier.get().reduce(init, collect);
+			assertEquals(init, result);
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceLongDoubleToLongFunction(Supplier<T> supplier)
+		{
+			List<Double> expected = supplier.get().collect(new ArrayList<>());
+			long init = Long.MIN_VALUE;
+			List<Double> actual = new ArrayList<>();
+			LongDoubleToLongFunction collect = (res, each) -> {actual.add(each); return res;};
+			long result = supplier.get().reduce(init, collect);
+			assertEquals(init, result);
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceObjDouble(Supplier<T> supplier)
+		{
+			List<Double> expected = supplier.get().collect(new ArrayList<>());
+			ObjDoubleFunction<List<Double>, List<Double>> collect = (seq, each) -> {seq.add(each); return seq;};
+			List<Double> actual = supplier.get().reduce(new ArrayList<>(), collect);
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+			assertDoesNotThrow(() -> supplier.get().reduce(null, (Object obj, double each) -> null));
+		}
+
+		@Test
+		default void testReduceOfDouble_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.reduce((DoubleBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(new Object(), (ObjDoubleFunction<Object, Object>) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0.0, (DoubleBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0, (IntDoubleToIntFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0L, (LongDoubleToLongFunction) null));
+		}
+
+		void testConcatTypes();
+
+		@ParameterizedTest
+		@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+		@Override
+		default void testDistinct(Supplier<T> supplier)
+		{
+			List<Double> expected = new ArrayList<>();
+			supplier.get().forEach((double d) -> {
+				if (!expected.contains(d) && !(d == 0.0 && expected.contains(-1.0 * d))) {
+					expected.add(d);
+				}
+			});
+			List<Double> actual = supplier.get().distinct().collect(new ArrayList<>());
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+		default void testDedupe(Supplier<T> supplier)
+		{
+			List<Double> expected = new ArrayList<>();
+			supplier.get().forEach((double d) -> {
+				if (expected.isEmpty()) {
+					expected.add(d);
+				} else {
+					double last = expected.get(expected.size() - 1);
+					if (last != d && !(Double.isNaN(last) && Double.isNaN(d))) {
+						expected.add(d);
+					}
+				}
+			});
+			List<Double> actual = supplier.get().dedupe().collect(new ArrayList<>());
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAllMatchDoublePredicate(Supplier<T> supplier)
+		{
+			// match all elements
+			assertTrue(supplier.get().allMatch((double each) -> true), "Expected allMatch() == true");
+			// match not all elements
+			DoublePredicate matchNotAll = new DoublePredicate() {
+				// match: no element if singleton, otherwise every odd element
+				boolean flag = supplier.get().count() == 1;
+				@Override
+				public boolean test(double d)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().allMatch(matchNotAll), "Expected allMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testAllMatchDoublePredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfDouble<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((double each) -> false), "Exepted allMatch() == true if iterator is empty");
+		}
+
+		@Test
+		default void testAllMatchDoublePredicate_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.allMatch((DoublePredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAnyMatchDoublePredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertFalse(supplier.get().anyMatch((double each) -> false), "Expected anyMatch() == false");
+			// match some elements
+			DoublePredicate matchSome = new DoublePredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(double d)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertTrue(supplier.get().anyMatch(matchSome), "Expected anyMatch() == true");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testAnyMatchDoublePredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfDouble<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((double each) -> true), "Exepted anyMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testAnyMatchDoublePredicate_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.anyMatch((DoublePredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testNoneMatchDoublePredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertTrue(supplier.get().noneMatch((double each) -> false), "Expected noneMatch() == true");
+			// match some elements
+			DoublePredicate matchSome = new DoublePredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(double d)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().noneMatch(matchSome), "Expected noneMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testNoneMatchDoublePredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfDouble<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((double each) -> false), "Exepted noneMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testNoneMatchDoublePredicate_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.noneMatch((DoublePredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@Override
+		default void testContains(Supplier<T> supplier)
+		{
+			assertFalse(supplier.get().contains(null));
+			testContainsDouble(supplier);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testContainsDouble(Supplier<T> supplier)
+		{
+			List<Double> numbers = supplier.get().collect(new ArrayList<>());
+			for (Double each : numbers) { // boxed double to trigger contains(Double d)
+				assertTrue(supplier.get().contains(each), "Expected contains(" + each + ") == true");
+				if (each == 0.0) {
+					assertTrue(supplier.get().contains(-1.0 * each), "Expected contains(" + (-1.0 * each) + ") == true");
+				}
+			}
+			for (Object each : getExcluded(supplier.get())) { // boxed double to trigger contains(Double d)
+				assertFalse(supplier.get().contains(each), "Expected contains(" + each + ") == false");
+			}
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCountDoublePredicate(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {0L};
+			supplier.get().forEach((double d) -> {
+				if (d % 2 == 1) {
+					expected[0]++;
+				}
+			});
+			DoublePredicate odd = d -> d % 2 == 1;
+			long actual = supplier.get().count(odd);
+			assertEquals(expected[0], actual);
+		}
+
+		@Test
+		default void testCountDoublePredicate_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.count((DoublePredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testDetectDoublePredicate_AllFalse(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfDouble<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((double each) -> false));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testDetectDoublePredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfDouble<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((double each) -> true));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testDetectDoublePredicate_Singleton(Supplier<T> supplier)
+		{
+			// match first element
+			double expected = supplier.get().collect(new ArrayList<>()).get(0);
+			double actual = supplier.get().detect((double each) -> true);
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testDetectDoublePredicate_Multiton(Supplier<T> supplier)
+		{
+			// match second element
+			double expected = supplier.get().collect(new ArrayList<>()).get(1);
+			DoublePredicate second = new DoublePredicate() {
+				boolean flag = true;
+				@Override
+				public boolean test(double d)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			double actual = supplier.get().detect(second);
+			assertEquals(expected, actual);
+		}
+
+		@Test
+		default void testDetectDoublePredicate_Null()
+		{
+			PrimitiveReducible.OfDouble<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.detect((DoublePredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMin_Empty(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.empty();
+			OptionalDouble actual = supplier.get().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMin_Singleton(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.of(supplier.get().detect((double d) -> true));
+			OptionalDouble actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertDoubleEquals(expected.getAsDouble(), actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMin_Multiton(Supplier<T> supplier)
+		{
+			double[] expected = new double[] {supplier.get().detect((double d) -> true)};
+			supplier.get().forEach((double d) -> expected[0] = Math.min(expected[0], d));
+			OptionalDouble actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertDoubleEquals(expected[0], actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMax_Empty(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.empty();
+			OptionalDouble actual = supplier.get().max();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMax_Singleton(Supplier<T> supplier)
+		{
+			OptionalDouble expected = OptionalDouble.of(supplier.get().detect((double d) -> true));
+			OptionalDouble actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertDoubleEquals(expected.getAsDouble(), actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMax_Multiton(Supplier<T> supplier)
+		{
+			double[] expected = new double[] {supplier.get().detect((double d) -> true)};
+			supplier.get().forEach((double d) -> expected[0] = Math.max(expected[0], d));
+			OptionalDouble actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertDoubleEquals(expected[0], actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getReducibles"})
+		default void testSum(Supplier<T> supplier)
+		{
+			double[] expected = new double[] {0.0};
+			supplier.get().forEach((double d) -> expected[0] += d);
+			double actual = supplier.get().sum();
+			assertEquals(expected[0], actual, 1e-15);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testSum_Empty(Supplier<T> supplier)
+		{
+			double expected = 0.0;
+			double actual = supplier.get().sum();
+			assertDoubleEquals(expected, actual);
+		}
+	}
+
+
+
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	interface OfInt<T extends PrimitiveReducible.OfInt<?>> extends ReducibleTest<Integer, T>
+	{
+		@Override
+		default Stream<Supplier<T>> getDuplicatesReducibles()
+		{
+			return getDuplicatesArraysOfInt().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getEmptyReducibles()
+		{
+			return getEmptyArraysOfInt().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getSingletonReducibles()
+		{
+			return getSingletonArraysOfInt().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getMultitonReducibles()
+		{
+			return getMultitonArraysOfInt().map(args-> () -> getReducible(args));
+		}
+
+		T getReducible(int[] arguments);
+
+		@Override
+		default Iterable<Object> getExcluded(T reducible)
+		{
+			List<Integer> candidates = getExclusionListOfInt();
+			reducible.forEach((Consumer<Integer>) candidates::remove);
+			List<Object> excluded = new ArrayList<>();
+			excluded.addAll(getUniqueObjects());
+			excluded.addAll(candidates);
+			return excluded;
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectIntArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count();
+			int[] expected = new int[n];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((int e) -> expected[index.nextInt()] = e);
+			int[] actual = supplier.get().collect(new int[n]);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectIntArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			int[] expected = new int[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((int i) -> expected[index.nextInt()] = i);
+			int[] actual = supplier.get().collect(new int[offset + n + tail], offset);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectBitSet(Supplier<T> supplier)
+		{
+			BitSet expected = new BitSet();
+			supplier.get().forEach((int i) -> {if (i >= 0) expected.set(i);});
+			BitSet actual = supplier.get().filter((int i) -> i >= 0).collect(new BitSet());
+			assertEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectOfInt_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collect((int[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collect((int[]) null, 0));
+			assertThrows(NullPointerException.class, () -> reducible.collect((BitSet) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountIntArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), tail = 3;
+			int[] expected = new int[n + tail];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((int i) -> expected[index.nextInt()] = i);
+			int[] actual = new int[n + tail];
+			long count = supplier.get().collectAndCount(actual);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountIntArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			int[] expected = new int[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((int i) -> expected[index.nextInt()] = i);
+			int[] actual = new int[offset + n + tail];
+			long count = supplier.get().collectAndCount(actual, offset);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountBitSet(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().filter((int i) -> i >= 0).count();
+			BitSet expected = new BitSet();
+			supplier.get().forEach((int i) -> {if (i >= 0) expected.set(i);});
+			BitSet actual = new BitSet();
+			long count = supplier.get().filter((int i) -> i >= 0).collectAndCount(actual);
+			assertEquals(n, count);
+			assertEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectAndCountOfInt_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((int[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((int[]) null, 0));
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((BitSet) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFilterInt(Supplier<T> supplier)
+		{
+			ArrayList<Integer> expected = new ArrayList<>();
+			supplier.get().forEach((int i) -> {
+				if (i % 2 == 0) {
+					expected.add(i);
+				}
+			});
+			PrimitiveReducible.OfInt<?> actual = supplier.get().filter((int i) -> i % 2 == 0);
+			assertIterableEquals(unboxInt(expected), unboxInt(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testFilterOfInt_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.filter((IntPredicate) null));
+		}
+
+		void testFlatMapIntToObj(Supplier<T> supplier);
+
+		void testFlatMapIntToDouble(Supplier<T> supplier);
+
+		void testFlatMapIntToInt(Supplier<T> supplier);
+
+		void testFlatMapIntToLong(Supplier<T> supplier);
+
+		void testFlatMapIntToNull(Supplier<T> supplier);
+
+		void testFlatMapOfInt_Null();
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapIntToObj(Supplier<T> supplier)
+		{
+			String prefix = "Item: ";
+			List<String> expected = new ArrayList<>();
+			supplier.get().forEach((int i) -> expected.add(prefix + i));
+			Reducible<String, ?> actual = supplier.get().map((int i) -> prefix + i);
+			assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapIntToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = unboxDouble(range.map((int i) -> (double) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfDouble<?> actual = supplier.get().mapToDouble((int i) -> index.next());
+			assertIterableEquals(expected, unboxDouble(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapIntToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveReducible.OfInt<?> actual = supplier.get().mapToInt((int i) -> index.next());
+			assertIterableEquals(expected, unboxInt(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = unboxLong(range.map((int i) -> (long) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfLong<?> actual = supplier.get().mapToLong((int i) -> index.next());
+			assertIterableEquals(expected, unboxLong(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testMapOfInt_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.map((IntFunction<?>) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToDouble((IntToDoubleFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToInt((IntUnaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToLong((IntToLongFunction) null));
+		}
+
+		void testForEachIntConsumer(Supplier<T> supplier);
+
+		@Test
+		default void testForEachIntConsumer_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.forEach((IntConsumer) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testReduceIntBinaryOperator_Empty(Supplier<T> supplier)
+		{
+			IntBinaryOperator dummy = (res, each) -> Integer.MIN_VALUE;
+			OptionalInt actual = supplier.get().reduce(dummy);
+			assertEquals(OptionalInt.empty(), actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testReduceIntBinaryOperator_Singleton(Supplier<T> supplier)
+		{
+			IntBinaryOperator dummy = (res, each) -> Integer.MIN_VALUE;
+			int expected = supplier.get().detect((int i) -> true);
+			OptionalInt actual = supplier.get().reduce(dummy);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.getAsInt());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testReduceIntBinaryOperator_Multiton(Supplier<T> supplier)
+		{
+			List<Integer> expected = supplier.get().collect(new ArrayList<>());
+			List<Integer> actual = new ArrayList<>();
+			int probe = -31; // "unique" value
+			IntBinaryOperator collect = (res, each) -> {
+				if (actual.isEmpty()) {
+					actual.add(res);
+					actual.add(each);
+					return probe;
+				} else {
+					actual.add(each);
+					return res;
+				}
+			};
+			OptionalInt result = supplier.get().reduce(collect);
+			assertTrue(result.isPresent());
+			assertEquals(probe, result.getAsInt());
+			assertIterableEquals(unboxInt(expected), unboxInt(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testReduceDoubleBinaryOperator_Empty(Supplier<T> supplier)
+		{
+			DoubleBinaryOperator dummy = (res, each) -> Double.MIN_VALUE;
+			OptionalDouble actual = supplier.get().reduce(dummy);
+			assertEquals(OptionalDouble.empty(), actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testReduceDoubleBinaryOperator_Singleton(Supplier<T> supplier)
+		{
+			DoubleBinaryOperator dummy = (res, each) -> Double.MIN_VALUE;
+			double expected = supplier.get().detect((int i) -> true);
+			OptionalDouble actual = supplier.get().reduce(dummy);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.getAsDouble());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testReduceDoubleBinaryOperator_Multiton(Supplier<T> supplier)
+		{
+			List<Double> expected = supplier.get().mapToDouble((int i) -> (double) i).collect(new ArrayList<>());
+			List<Double> actual = new ArrayList<>();
+			double probe = -31; // "unique" value
+			DoubleBinaryOperator collect = (res, each) -> {
+				if (actual.isEmpty()) {
+					actual.add(res);
+					actual.add(each);
+					return probe;
+				} else {
+					actual.add(each);
+					return res;
+				}
+			};
+			OptionalDouble result = supplier.get().reduce(collect);
+			assertTrue(result.isPresent());
+			assertEquals(probe, result.getAsDouble());
+			assertIterableEquals(unboxDouble(expected), unboxDouble(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testReduceLongBinaryOperator_Empty(Supplier<T> supplier)
+		{
+			LongBinaryOperator dummy = (res, each) -> Long.MIN_VALUE;
+			OptionalLong actual = supplier.get().reduce(dummy);
+			assertEquals(OptionalLong.empty(), actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testReduceLongBinaryOperator_Singleton(Supplier<T> supplier)
+		{
+			LongBinaryOperator dummy = (res, each) -> Long.MIN_VALUE;
+			long expected = supplier.get().detect((int i) -> true);
+			OptionalLong actual = supplier.get().reduce(dummy);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testReduceLongBinaryOperator_Multiton(Supplier<T> supplier)
+		{
+			List<Long> expected = supplier.get().mapToLong((int i) -> (long) i).collect(new ArrayList<>());
+			List<Long> actual = new ArrayList<>();
+			long probe = -31; // "unique" value
+			LongBinaryOperator collect = (res, each) -> {
+				if (actual.isEmpty()) {
+					actual.add(res);
+					actual.add(each);
+					return probe;
+				} else {
+					actual.add(each);
+					return res;
+				}
+			};
+			OptionalLong result = supplier.get().reduce(collect);
+			assertTrue(result.isPresent());
+			assertEquals(probe, result.getAsLong());
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceObjIntFunction(Supplier<T> supplier)
+		{
+			List<Integer> expected = supplier.get().collect(new ArrayList<>());
+			ObjIntFunction<List<Integer>, List<Integer>> collect = (seq, each) -> {seq.add(each); return seq;};
+			List<Integer> actual = supplier.get().reduce(new ArrayList<>(), collect);
+			assertIterableEquals(unboxInt(expected), unboxInt(actual));
+			assertDoesNotThrow(() -> supplier.get().reduce(null, (Object obj, int each) -> null));
+		}
+
+		@Test
+		default void testReduceOfInt_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.reduce((DoubleBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce((IntBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce((LongBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(new Object(), (ObjIntFunction<Object, Object>) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0.0, (DoubleBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0, (IntBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0L, (LongBinaryOperator) null));
+		}
+
+		void testConcatTypes();
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAllMatchIntPredicate(Supplier<T> supplier)
+		{
+			// match all elements
+			assertTrue(supplier.get().allMatch((int each) -> true), "Expected allMatch() == true");
+			// match not all elements
+			IntPredicate matchNotAll = new IntPredicate() {
+				// match: no element if singleton, otherwise every odd element
+				boolean flag = supplier.get().count() == 1;
+				@Override
+				public boolean test(int i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().allMatch(matchNotAll), "Expected allMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testAllMatchIntPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfInt<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((int each) -> false), "Exepted allMatch() == true if iterator is empty");
+		}
+
+		@Test
+		default void testAllMatchIntPredicate_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.allMatch((IntPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAnyMatchIntPredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertFalse(supplier.get().anyMatch((int each) -> false), "Expected anyMatch() == false");
+			// match some elements
+			IntPredicate matchSome = new IntPredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(int i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertTrue(supplier.get().anyMatch(matchSome), "Expected anyMatch() == true");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testAnyMatchIntPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfInt<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((int each) -> true), "Exepted anyMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testAnyMatchIntPredicate_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.anyMatch((IntPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testNoneMatchIntPredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertTrue(supplier.get().noneMatch((int each) -> false), "Expected noneMatch() == true");
+			// match some elements
+			IntPredicate matchSome = new IntPredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(int i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().noneMatch(matchSome), "Expected noneMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testNoneMatchIntPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfInt<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((int each) -> false), "Exepted noneMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testNoneMatchIntPredicate_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.noneMatch((IntPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@Override
+		default void testContains(Supplier<T> supplier)
+		{
+			assertFalse(supplier.get().contains(null), "Expected contains(null) == false");
+			testContainsInt(supplier);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testContainsInt(Supplier<T> supplier)
+		{
+			List<Integer> numbers = supplier.get().collect(new ArrayList<>());
+			for (Integer each : numbers) { // boxed int to trigger contains(Integer i)
+				assertTrue(supplier.get().contains(each), "Expected contains(" + each + ") == true");
+			}
+			for (Object each : getExcluded(supplier.get())) { // boxed int to trigger contains(Integer i)
+				assertFalse(supplier.get().contains(each), "Expected contains(" + each + ") == false");
+			}
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCountIntPredicate(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {0L};
+			supplier.get().forEach((int i) -> {
+				if (i % 2 == 1) {
+					expected[0]++;
+				}
+			});
+			IntPredicate odd = i -> i % 2 == 1;
+			long actual = supplier.get().count(odd);
+			assertEquals(expected[0], actual);
+		}
+
+		@Test
+		default void testCountIntPredicate_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.count((IntPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testDetectIntPredicate(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfInt<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((int each) -> false));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testDetectIntPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfInt<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((int each) -> true));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testDetectIntPredicate_Singleton(Supplier<T> supplier)
+		{
+			// match first element
+			int expected = supplier.get().collect(new ArrayList<>()).get(0);
+			int actual = supplier.get().detect((int each) -> true);
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testDetectIntPredicate_Multiton(Supplier<T> supplier)
+		{
+			// match second element
+			int expected = supplier.get().collect(new ArrayList<>()).get(1);
+			IntPredicate second = new IntPredicate() {
+				boolean flag = true;
+				@Override
+				public boolean test(int i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			int actual = supplier.get().detect(second);
+			assertEquals(expected, actual);
+		}
+
+		@Test
+		default void testDetectIntPredicate_Null()
+		{
+			PrimitiveReducible.OfInt<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.detect((IntPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMin_Empty(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.empty();
+			OptionalInt actual = supplier.get().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMin_Singleton(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.of(supplier.get().detect((int i) -> true));
+			OptionalInt actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertEquals(expected.getAsInt(), actual.getAsInt());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMin_Multiton(Supplier<T> supplier)
+		{
+			int[] expected = new int[] {supplier.get().detect((int i) -> true)};
+			supplier.get().forEach((int i) -> expected[0] = Math.min(expected[0], i));
+			OptionalInt actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertEquals(expected[0], actual.getAsInt());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMax_Empty(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.empty();
+			OptionalInt actual = supplier.get().max();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMax_Singleton(Supplier<T> supplier)
+		{
+			OptionalInt expected = OptionalInt.of(supplier.get().detect((int i) -> true));
+			OptionalInt actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertEquals(expected.getAsInt(), actual.getAsInt());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMax_Multiton(Supplier<T> supplier)
+		{
+			int[] expected = new int[] {supplier.get().detect((int i) -> true)};
+			supplier.get().forEach((int i) -> expected[0] = Math.max(expected[0], i));
+			OptionalInt actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertEquals(expected[0], actual.getAsInt());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testSum(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {0L};
+			supplier.get().forEach((int i) -> expected[0] += i);
+			long actual = supplier.get().sum();
+			assertEquals(expected[0], actual);
+		}
+	}
+
+
+
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	interface OfLong<T extends PrimitiveReducible.OfLong<?>> extends ReducibleTest<Long, T>
+	{
+		@Override
+		default Stream<Supplier<T>> getDuplicatesReducibles()
+		{
+			return getDuplicatesArraysOfLong().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getEmptyReducibles()
+		{
+			return getEmptyArraysOfLong().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getSingletonReducibles()
+		{
+			return getSingletonArraysOfLong().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getMultitonReducibles()
+		{
+			return getMultitonArraysOfLong().map(args-> () -> getReducible(args));
+		}
+
+		T getReducible(long[] numbers);
+
+		@Override
+		default Iterable<Object> getExcluded(T reducible)
+		{
+			List<Long> candidates = getExclusionListOfLong();
+			reducible.forEach((Consumer<Long>) candidates::remove);
+			List<Object> excluded = new ArrayList<>();
+			excluded.addAll(getUniqueObjects());
+			excluded.addAll(candidates);
+			return excluded;
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectLongArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count();
+			long[] expected = new long[n];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((long l) -> expected[index.nextInt()] = l);
+			long[] actual = supplier.get().collect(new long[n]);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectLongArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			long[] expected = new long[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((long l) -> expected[index.nextInt()] = l);
+			long[] actual = supplier.get().collect(new long[offset + n + tail], offset);
+			assertArrayEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectOfLong_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collect((long[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collect((long[]) null, 0));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountLongArray(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), tail = 3;
+			long[] expected = new long[n + tail];
+			Range.RangeIterator index = new Range(n).iterator();
+			supplier.get().forEach((long l) -> expected[index.nextInt()] = l);
+			long[] actual = new long[n + tail];
+			long count = supplier.get().collectAndCount(actual);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCollectAndCountArrayOffset(Supplier<T> supplier)
+		{
+			int n = (int) supplier.get().count(), offset = 2, tail = 3;
+			long[] expected = new long[offset + n + tail];
+			Range.RangeIterator index = new Range(offset, offset + n).iterator();
+			supplier.get().forEach((long l) -> expected[index.nextInt()] = l);
+			long[] actual = new long[offset + n + tail];
+			long count = supplier.get().collectAndCount(actual, offset);
+			assertEquals(n, count);
+			assertArrayEquals(expected, actual);
+		}
+
+		@Test
+		default void testCollectAndCountOfLong_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((long[]) null));
+			assertThrows(NullPointerException.class, () -> reducible.collectAndCount((long[]) null, 0));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testFilterLong(Supplier<T> supplier)
+		{
+			ArrayList<Long> expected = new ArrayList<>();
+			supplier.get().forEach((long l) -> {
+				if (l % 2 == 0) {
+					expected.add(l);
+				}
+			});
+			PrimitiveReducible.OfLong<?> actual = supplier.get().filter((long l) -> l % 2 == 0);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testFilterOfLong_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.filter((LongPredicate) null));
+		}
+
+		void testFlatMapLongToObj(Supplier<T> supplier);
+
+		void testFlatMapLongToDouble(Supplier<T> supplier);
+
+		void testFlatMapLongToInt(Supplier<T> supplier);
+
+		void testFlatMapLongToLong(Supplier<T> supplier);
+
+		void testFlatMapLongToNull(Supplier<T> supplier);
+
+		void testFlatMapOfLong_Null();
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapLongToObj(Supplier<T> supplier)
+		{
+			String prefix = "Item: ";
+			List<String> expected = new ArrayList<>();
+			supplier.get().forEach((long l) -> expected.add(prefix + l));
+			Reducible<String, ?> actual = supplier.get().map((long l) -> prefix + l);
+			assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapLongToDouble(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfDouble expected = unboxDouble(range.map((int i) -> (double) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfDouble<?> actual = supplier.get().mapToDouble((long l) -> index.next());
+			assertIterableEquals(expected, unboxDouble(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapLongToInt(Supplier<T> supplier)
+		{
+			Range expected = new Range((int) supplier.get().count());
+			Range.RangeIterator index = expected.iterator();
+			PrimitiveReducible.OfInt<?> actual = supplier.get().mapToInt((long l) -> index.next());
+			assertIterableEquals(expected, unboxInt(actual.collect(new ArrayList<>())));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testMapToLong(Supplier<T> supplier)
+		{
+			Range range = new Range((int) supplier.get().count());
+			PrimitiveIterable.OfLong expected = unboxLong(range.map((int i) -> (long) i));
+			Range.RangeIterator index = range.iterator();
+			PrimitiveReducible.OfLong<?> actual = supplier.get().mapToLong((long l) -> index.next());
+			assertIterableEquals(expected, unboxLong(actual.collect(new ArrayList<>())));
+		}
+
+		@Test
+		default void testMapOfLong_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.map((LongFunction<?>) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToDouble((LongToDoubleFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToInt((LongToIntFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.mapToLong((LongUnaryOperator) null));
+		}
+
+		void testForEachLongConsumer(Supplier<T> supplier);
+
+		@Test
+		default void testForEachLongConsumer_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.forEach((LongConsumer) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testReduceLongBinaryOperator_Empty(Supplier<T> supplier)
+		{
+			LongBinaryOperator dummy = (res, each) -> Long.MIN_VALUE;
+			OptionalLong actual = supplier.get().reduce(dummy);
+			assertEquals(OptionalLong.empty(), actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testReduceLongBinaryOperator_Singleton(Supplier<T> supplier)
+		{
+			LongBinaryOperator dummy = (res, each) -> Long.MIN_VALUE;
+			long expected = supplier.get().detect((long l) -> true);
+			OptionalLong actual = supplier.get().reduce(dummy);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testReduceLongBinaryOperator_Multiton(Supplier<T> supplier)
+		{
+			List<Long> expected = supplier.get().collect(new ArrayList<>());
+			List<Long> actual = new ArrayList<>();
+			long probe = -31; // "unique" value
+			LongBinaryOperator collect = (res, each) -> {
+				if (actual.isEmpty()) {
+					actual.add(res);
+					actual.add(each);
+					return probe;
+				} else {
+					actual.add(each);
+					return res;
+				}
+			};
+			OptionalLong result = supplier.get().reduce(collect);
+			assertTrue(result.isPresent());
+			assertEquals(probe, result.getAsLong());
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceDoubleLongToDoubleFunction(Supplier<T> supplier)
+		{
+			List<Long> expected = supplier.get().collect(new ArrayList<>());
+			double init = Double.MIN_VALUE;
+			List<Long> actual = new ArrayList<>();
+			DoubleLongToDoubleFunction collect = (res, each) -> {actual.add(each); return res;};
+			double result = supplier.get().reduce(init, collect);
+			assertEquals(init, result);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceIntLongToIntFunction(Supplier<T> supplier)
+		{
+			List<Long> expected = supplier.get().collect(new ArrayList<>());
+			int init = Integer.MIN_VALUE;
+			List<Long> actual = new ArrayList<>();
+			IntLongToIntFunction collect = (res, each) -> {actual.add(each); return res;};
+			int result = supplier.get().reduce(init, collect);
+			assertEquals(init, result);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testReduceObjLong(Supplier<T> supplier)
+		{
+			List<Long> expected = supplier.get().collect(new ArrayList<>());
+			ObjLongFunction<List<Long>, List<Long>> collect = (seq, each) -> {seq.add(each); return seq;};
+			List<Long> actual = supplier.get().reduce(new ArrayList<>(), collect);
+			assertIterableEquals(unboxLong(expected), unboxLong(actual));
+			assertDoesNotThrow(() -> supplier.get().reduce(null, (Object obj, long each) -> null));
+		}
+
+		@Test
+		default void testReduceOfLong_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.reduce((LongBinaryOperator) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(new Object(), (ObjLongFunction<Object, Object>) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0.0, (DoubleLongToDoubleFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0, (IntLongToIntFunction) null));
+			assertThrows(NullPointerException.class, () -> reducible.reduce(0L, (LongBinaryOperator) null));
+		}
+
+		void testConcatTypes();
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAllMatchLongPredicate(Supplier<T> supplier)
+		{
+			// match all elements
+			assertTrue(supplier.get().allMatch((long each) -> true), "Expected allMatch() == true");
+			// match not all elements
+			LongPredicate matchNotAll = new LongPredicate() {
+				// match: no element if singleton, otherwise every odd element
+				boolean flag = supplier.get().count() == 1;
+				@Override
+				public boolean test(long i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().allMatch(matchNotAll), "Expected allMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testAllMatchLongPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfLong<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((long each) -> false), "Exepted allMatch() == true if iterator is empty");
+		}
+
+		@Test
+		default void testAllMatchLongPredicate_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.allMatch((LongPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testAnyMatchLongPredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertFalse(supplier.get().anyMatch((long each) -> false), "Expected anyMatch() == false");
+			// match some elements
+			LongPredicate matchSome = new LongPredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(long i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertTrue(supplier.get().anyMatch(matchSome), "Expected anyMatch() == true");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testAnyMatchLongPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfLong<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((long each) -> true), "Exepted anyMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testAnyMatchLongPredicate_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.anyMatch((LongPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+		default void testNoneMatchLongPredicate(Supplier<T> supplier)
+		{
+			// match no element
+			assertTrue(supplier.get().noneMatch((long each) -> false), "Expected noneMatch() == true");
+			// match some elements
+			LongPredicate matchSome = new LongPredicate() {
+				// match: first element if singleton, otherwise every even element
+				boolean flag = supplier.get().count() > 1;
+				@Override
+				public boolean test(long i)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			assertFalse(supplier.get().noneMatch(matchSome), "Expected noneMatch() == false");
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testNoneMatchLongPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfLong<?> reducible = supplier.get();
+			assertTrue(reducible.allMatch((long each) -> false), "Exepted noneMatch() == false if iterator is empty");
+		}
+
+		@Test
+		default void testNoneMatchLongPredicate_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.noneMatch((LongPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		@Override
+		default void testContains(Supplier<T> supplier)
+		{
+			assertFalse(supplier.get().contains(null), "Expected contains(null) == false");
+			testContainsLong(supplier);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testContainsLong(Supplier<T> supplier)
+		{
+			List<Long> numbers = supplier.get().collect(new ArrayList<>());
+			for (Long each : numbers) { // boxed long to trigger contains(Long l)
+				assertTrue(supplier.get().contains(each), "Expected contains(" + each + ") == true");
+			}
+			for (Object each : getExcluded(supplier.get())) { // boxed long to trigger contains(Long l)
+				assertFalse(supplier.get().contains(each), "Expected contains(" + each + ") == false");
+			}
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testCountLongPredicate(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {0L};
+			supplier.get().forEach((long l) -> {
+				if (l % 2 == 1) {
+					expected[0]++;
+				}
+			});
+			LongPredicate odd = l -> l % 2 == 1;
+			long actual = supplier.get().count(odd);
+			assertEquals(expected[0], actual);
+		}
+
+		@Test
+		default void testCountLongPredicate_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.count((LongPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testDetectLongPredicate(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfLong<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((long each) -> false));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testDetectLongPredicate_Empty(Supplier<T> supplier)
+		{
+			PrimitiveReducible.OfLong<?> reducible = supplier.get();
+			assertThrows(NoSuchElementException.class, () -> reducible.detect((long each) -> true));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testDetectLongPredicate_Singleton(Supplier<T> supplier)
+		{
+			// match first element
+			long expected = supplier.get().collect(new ArrayList<>()).get(0);
+			long actual = supplier.get().detect((long each) -> true);
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testDetectLongPredicate_Multiton(Supplier<T> supplier)
+		{
+			// match second element
+			long expected = supplier.get().collect(new ArrayList<>()).get(1);
+			LongPredicate second = new LongPredicate() {
+				boolean flag = true;
+				@Override
+				public boolean test(long l)
+				{
+					flag = !flag;
+					return flag;
+				}
+			};
+			long actual = supplier.get().detect(second);
+			assertEquals(expected, actual);
+		}
+
+		@Test
+		default void testDetectLongPredicate_Null()
+		{
+			PrimitiveReducible.OfLong<?> reducible = getAnyReducible();
+			assertThrows(NullPointerException.class, () -> reducible.detect((LongPredicate) null));
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMin_Empty(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.empty();
+			OptionalLong actual = supplier.get().min();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMin_Singleton(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.of(supplier.get().detect((long l) -> true));
+			OptionalLong actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertEquals(expected.getAsLong(), actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMin_Multiton(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {supplier.get().detect((long l) -> true)};
+			supplier.get().forEach((long l) -> expected[0] = Math.min(expected[0], l));
+			OptionalLong actual = supplier.get().min();
+			assertTrue(actual.isPresent());
+			assertEquals(expected[0], actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getEmptyReducibles")
+		default void testMax_Empty(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.empty();
+			OptionalLong actual = supplier.get().max();
+			assertEquals(expected, actual);
+		}
+
+		@ParameterizedTest
+		@MethodSource("getSingletonReducibles")
+		default void testMax_Singleton(Supplier<T> supplier)
+		{
+			OptionalLong expected = OptionalLong.of(supplier.get().detect((long l) -> true));
+			OptionalLong actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertEquals(expected.getAsLong(), actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getMultitonReducibles")
+		default void testMax_Multiton(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {supplier.get().detect((long l) -> true)};
+			supplier.get().forEach((long l) -> expected[0] = Math.max(expected[0], l));
+			OptionalLong actual = supplier.get().max();
+			assertTrue(actual.isPresent());
+			assertEquals(expected[0], actual.getAsLong());
+		}
+
+		@ParameterizedTest
+		@MethodSource("getReducibles")
+		default void testSum(Supplier<T> supplier)
+		{
+			long[] expected = new long[] {0L};
+			supplier.get().forEach((long l) -> expected[0] += l);
+			long actual = supplier.get().sum();
+			assertEquals(expected[0], actual);
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/RangeTest.java b/prism/unit-tests/common/iterable/RangeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9645e757ebae60f6b0a83584e5c6efd9137726b5
--- /dev/null
+++ b/prism/unit-tests/common/iterable/RangeTest.java
@@ -0,0 +1,390 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PrimitiveIterator;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static common.iterable.PrimitiveReducibleTest.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+class RangeTest implements FunctionalPrimitiveIterableTest.OfInt<Range>
+{
+	static Range asRange(Arguments args)
+	{
+		Object[] params = args.get();
+		return new Range((Integer) params[0], (Integer) params[1], (Integer) params[2]);
+	}
+
+	static Stream<Arguments> getEmptyRangeArguments()
+	{
+		return Stream.of(Arguments.of(0, 0, 1),
+				Arguments.of(0, 0, -1),
+				Arguments.of(Integer.MAX_VALUE, Integer.MAX_VALUE, 1),
+				Arguments.of(Integer.MIN_VALUE, Integer.MIN_VALUE, -1));
+	}
+
+	static Stream<Arguments> getSingletonRangeArguments()
+	{
+		return Stream.of(Arguments.of(0, 1, 1),
+				Arguments.of(0, -1, -1),
+				Arguments.of(Integer.MAX_VALUE-1, Integer.MAX_VALUE, 1),
+				Arguments.of(Integer.MIN_VALUE+1, Integer.MIN_VALUE, -1));
+	}
+
+	static Stream<Arguments> getMultitonRangeArguments()
+	{
+		return Stream.of(Arguments.of(0, 10, 1),
+				Arguments.of(0, -10, -1),
+				Arguments.of(-10, 10, 3),
+				Arguments.of(10, -10, -3),
+				Arguments.of(Integer.MIN_VALUE, Integer.MIN_VALUE+20, 7),
+				Arguments.of(Integer.MAX_VALUE, Integer.MAX_VALUE-20, -7));
+	}
+
+	@Override
+	public Range getReducible(int[] arguments)
+	{
+		throw new RuntimeException("Should not be called");
+	}
+
+	@Override
+	public Stream<Supplier<Range>> getDuplicatesReducibles()
+	{
+		return Stream.empty();
+	}
+
+	@Override
+	public Stream<Supplier<Range>> getEmptyReducibles()
+	{
+		return getEmptyRangeArguments().map(args -> () -> asRange(args));
+	}
+
+	@Override
+	public Stream<Supplier<Range>> getSingletonReducibles()
+	{
+		return getSingletonRangeArguments().map(args -> () -> asRange(args));
+	}
+
+	@Override
+	public Stream<Supplier<Range>> getMultitonReducibles()
+	{
+		return getMultitonRangeArguments().map(args -> () -> asRange(args));
+	}
+
+	@Override
+	public Iterable<Object> getExcluded(Range range)
+	{
+		List<Object> excluded = new ArrayList<>();
+		excluded.addAll(getUniqueObjects());
+		// add lower and upper bounds
+		range.min().ifPresent(min -> {
+			if (min > Integer.MIN_VALUE) {
+				excluded.add(min - 1);
+				excluded.add(Integer.MIN_VALUE);
+			}
+		});
+		range.max().ifPresent(max -> {
+			if (max < Integer.MAX_VALUE) {
+				excluded.add(max + 1);
+				excluded.add(Integer.MAX_VALUE);
+			}
+		});
+		// add ints between first and last that are no steps
+		if (range.isAscending()) {
+			for (int i = range.first; i <= range.last; i++) {
+				if ((i - range.first) % range.step != 0) {
+					excluded.add((Integer) i);
+				}
+			}
+		} else {
+			for (int i = range.first; i >= range.last; i--) {
+				if ((i - range.first) % range.step != 0) {
+					excluded.add((Integer) i);
+				}
+			}
+		}
+		return excluded;
+	}
+
+	public static void assertEqualsClosedForLoop(int start, int stop, int step, Range actual)
+	{
+		assertEqualsClosedForLoop(start, stop, step, actual.iterator());
+	}
+
+	public static void assertEqualsOpenForLoop(int start, int stop, int step, Range actual)
+	{
+		assertEqualsOpenForLoop(start, stop, step, actual.iterator());
+	}
+
+	public static void assertEqualsClosedForLoop(int start, int stop, int step, PrimitiveIterator.OfInt actual)
+	{
+		if (step > 0) {
+			// closed ascending loop: <=
+			for (int i = start; i <= stop; i = Math.addExact(i, step)) {
+				assertEquals(i, actual.nextInt());
+			}
+		} else if (step < 0) {
+			// open descending loop: >=
+			for (int i = start; i >= stop; i = Math.addExact(i, step)) {
+				assertEquals(i, actual.nextInt());
+			}
+		} else {
+			fail("expected step != 0");
+		}
+		assertFalse(actual.hasNext(), "Expected exhausted iterator");
+	}
+
+	public static void assertEqualsOpenForLoop(int start, int stop, int step, PrimitiveIterator.OfInt actual)
+	{
+		if (step > 0) {
+			// open ascending loop: <
+			for (int i = start; i < stop; i = Math.addExact(i, step)) {
+				assertEquals(i, actual.nextInt());
+			}
+		} else if (step < 0) {
+			// open descending loop: >
+			for (int i = start; i > stop; i = Math.addExact(i, step)) {
+				assertEquals(i, actual.nextInt());
+			}
+		} else {
+			fail("expected step != 0");
+		}
+		assertFalse(actual.hasNext(), "Expected exhausted iterator");
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#closed(int)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testClosedInt(int start, int stop, int step)
+	{
+		if (step > 0 && start == 0) {
+			// range with positive step width starting at 0
+			// adjust stop to maybe be included
+			int closed = (step > 0) ? stop - 1 : stop + 1;
+			Range actual = Range.closed(closed);
+			assertEqualsClosedForLoop(0, closed, 1, actual);
+		}
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#closed(int, int)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testClosedIntInt(int start, int stop, int step)
+	{
+		if (step > 0) {
+			// range with positive step width
+			// adjust stop to maybe be included
+			int closed = (step > 0) ? stop - 1 : stop + 1;
+			Range actual = Range.closed(start, closed);
+			assertEqualsClosedForLoop(start, closed, 1, actual);
+		}
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#closed(int, int, int)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testClosedIntIntInt(int start, int stop, int step)
+	{
+		// adjust stop to maybe be included
+		int closed = (step > 0) ? stop - 1 : stop + 1;
+		Range actual = Range.closed(start, closed, step);
+		assertEqualsClosedForLoop(start, closed, step, actual);
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#Range(int)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testRangeInt(int start, int stop, int step)
+	{
+		if (step > 0 && start == 0) {
+			// range with positive step width starting at 0
+			Range actual = new Range(stop);
+			assertEqualsOpenForLoop(0, stop, 1, actual);
+		}
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#Range(int, int)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testRangeIntInt(int start, int stop, int step)
+	{
+		if (step > 0) {
+			// range with positive step width
+			Range actual = new Range(start, stop);
+			assertEqualsOpenForLoop(start, stop, 1, actual);
+		}
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#Range(int, int, int, boolean)}.
+	 */
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments", "getSingletonRangeArguments", "getMultitonRangeArguments"})
+	void testRangeIntIntInt(int start, int stop, int step)
+	{
+		Range actual = new Range(start, stop, step);
+		assertEqualsOpenForLoop(start, stop, step, actual);
+	}
+
+	@Test
+	void testRangeStepZero()
+	{
+		assertThrows(IllegalArgumentException.class, () -> new Range(1, 2, 0));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	void testIsAscending(Supplier<Range> supplier)
+	{
+		Range range = supplier.get();
+		if (range.isEmpty() || range.isSingleton()) {
+			assertTrue(range.isAscending());
+		} else if (range.step > 0) {
+			assertTrue(range.isAscending());
+		} else if(range.step < 0) {
+			assertFalse(range.isAscending());
+		} else {
+			fail("expected step != 0");
+		}
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	void testIsSingleton(Supplier<Range> supplier)
+	{
+		Range range = supplier.get();
+		assertTrue(range.count() !=1 ^ range.isSingleton());
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	void testReversed(Supplier<Range> supplier)
+	{
+		Range range = supplier.get();
+		if (range.first == Integer.MIN_VALUE && !range.isEmpty()) {
+			// reverse() at min value throws
+			assertThrows(ArithmeticException.class, range::reversed);
+		} else if (range.first == Integer.MAX_VALUE && !range.isEmpty()) {
+			// reverse() at min value throws
+			assertThrows(ArithmeticException.class, range::reversed);
+		} else {
+			assertEqualsClosedForLoop(range.last, range.first, -range.step, range.reversed());
+		}
+	}
+
+	/**
+	 * Test method for {@link common.iterable.Range#toString()}.
+	 */
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	void testToString(Supplier<Range> supplier)
+	{
+		Range range = supplier.get();
+		String expected = "Range.closed(" + range.first + ", " + range.last + ", " + range.step + ")";
+		String actual = range.toString();
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	void testEqualsAndHash()
+	{
+		Range range = Range.closed(-2, 4, 3); // {-2, 1, 4}
+
+		// equal to itself
+		assertEquals(range, range);
+
+		// equal to a clone
+		Range clone = Range.closed(-2, 4, 3);
+		assertEquals(range, clone);
+		assertEquals(range.hashCode(), clone.hashCode());
+
+		// not equal to null or other type
+		assertFalse(range.equals(null));
+		assertFalse(range.equals("no"));
+
+		// not equal to an arbitrary range
+		Range otherStart = Range.closed(-5, 4, 3); // {-5, -2, 1, 4}
+		assertNotEquals(otherStart, range);
+		Range otherStop = Range.closed(-2, 7, 3); // {-2, 1, 4, 7}
+		assertNotEquals(otherStop, range);
+		Range otherStep = Range.closed(-2, 4, 2); // {-2, 0, 2, 4}
+		assertNotEquals(otherStep, range);
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getEmptyRangeArguments"})
+	void testEqualsAndHashEmpty(int start, int stop, int step)
+	{
+		Range expected = new Range(0);
+		Range actual = new Range(start, stop, step);
+		assertEquals(expected, actual);
+		assertEquals(expected.hashCode(), actual.hashCode());
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getSingletonRangeArguments"})
+	void testEqualsAndHashSingleton(int start, int stop, int step)
+	{
+		Range expected = new Range(start, stop, step > 0 ? 1 : -1);
+		Range actual = new Range(start, stop, step);
+		assertEquals(expected, actual);
+		assertEquals(expected.hashCode(), actual.hashCode());
+	}
+
+
+
+	@Nested
+	@TestInstance(Lifecycle.PER_CLASS)
+	class RangeIteratorTest implements FunctionalPrimitiveIteratorTest.OfInt<Range.RangeIterator>
+	{
+		@Override
+		public Range.RangeIterator getReducible(int[] arguments)
+		{
+			throw new RuntimeException("Should not be called");
+		}
+
+		@Override
+		public Stream<Supplier<Range.RangeIterator>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<Range.RangeIterator>> getEmptyReducibles()
+		{
+			return getEmptyRangeArguments().map(args -> () -> asRange(args).iterator());
+		}
+
+		@Override
+		public Stream<Supplier<Range.RangeIterator>> getSingletonReducibles()
+		{
+			return getSingletonRangeArguments().map(args -> () -> asRange(args).iterator());
+		}
+
+		@Override
+		public Stream<Supplier<Range.RangeIterator>> getMultitonReducibles()
+		{
+			return getMultitonRangeArguments().map(args -> () -> asRange(args).iterator());
+		}
+	}
+}
diff --git a/prism/unit-tests/common/iterable/ReducibleStaticTest.java b/prism/unit-tests/common/iterable/ReducibleStaticTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..68ed32871b0c583ff770c7487569cd1d750eeff8
--- /dev/null
+++ b/prism/unit-tests/common/iterable/ReducibleStaticTest.java
@@ -0,0 +1,440 @@
+package common.iterable;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+import static common.iterable.Assertions.assertIterableEquals;
+import static common.iterable.Assertions.assertIteratorEquals;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
+import static org.junit.jupiter.api.Assertions.*;
+
+class ReducibleStaticTest
+{
+	static Stream<Iterable<Object>> getIterables()
+	{
+		return Stream.of(Collections.singleton(null),
+		                 Collections.emptyList(),
+		                 Collections.singleton("one"),
+		                 Arrays.asList("one", "two", "three"));
+	}
+
+	static <T> Stream<Iterable<T>> getIterablesNull()
+	{
+		return Stream.of(Collections.singleton(null));
+	}
+
+	static Stream<Iterable<Double>> getIterablesDouble()
+	{
+		return Stream.of(Collections.emptyList(),
+		                 Collections.singleton(1.0),
+		                 Arrays.asList(1.0, 2.0, 3.0));
+	}
+
+	static Stream<Iterable<Integer>> getIterablesInt()
+	{
+		return Stream.of(Collections.emptyList(),
+		                 Collections.singleton(1),
+		                 Arrays.asList(1, 2, 3));
+	}
+
+	static Stream<Iterable<Long>> getIterablesLong()
+	{
+		return Stream.of(Collections.emptyList(),
+		                 Collections.singleton(1L),
+		                 Arrays.asList(1L, 2L, 3L));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	@DisplayName("extend() yields same sequence as the underlying iterable.")
+	void testExtendIterable(Iterable<?> iterable)
+	{
+		FunctionalIterable<?> actual = Reducible.extend(iterable);
+		assertIterableEquals(iterable, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	@DisplayName("extend() yields same sequence as the underlying iterator.")
+	void testExtendIterator(Iterable<?> iterable)
+	{
+		Iterator<?> expected = iterable.iterator();
+		FunctionalIterator<?> actual = Reducible.extend(iterable.iterator());
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	@DisplayName("extend() does not extend a FunctionalIterable.")
+	void testExtendFunctionalIterable(Iterable<?> iterable)
+	{
+		Iterable<?> expected = Reducible.extend(iterable);
+		FunctionalIterable<?> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterables")
+	@DisplayName("extend() does not extend a FunctionalIterator.")
+	void testExtendFunctionalIterator(Iterable<?> iterable)
+	{
+		Iterator<?> expected = Reducible.extend(iterable.iterator());
+		FunctionalIterator<?> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterable.OfDouble.")
+	void testExtendFunctionalPrimitiveIterableOfDouble(Iterable<Double> iterable)
+	{
+		PrimitiveIterable.OfDouble expected = Reducible.extend(PrimitiveIterable.unboxDouble(iterable));
+		FunctionalIterable<Double> functional = Reducible.extend(expected);
+		assertSame(expected, functional);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterator.OfDouble.")
+	void testExtendFunctionalPrimitiveIteratorOfDouble(Iterable<Double> iterable)
+	{
+		PrimitiveIterator.OfDouble expected = Reducible.extend(PrimitiveIterable.unboxDouble(iterable.iterator()));
+		FunctionalIterator<Double> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterable.OfInt.")
+	void testExtendFunctionalPrimitiveIterableOfInt(Iterable<Integer> iterable)
+	{
+		PrimitiveIterable.OfInt expected = Reducible.extend(PrimitiveIterable.unboxInt(iterable));
+		FunctionalIterable<Integer> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterator.OfInt.")
+	void testExtendFunctionalPrimitiveIteratorOfInt(Iterable<Integer> iterable)
+	{
+		PrimitiveIterator.OfInt expected = Reducible.extend(PrimitiveIterable.unboxInt(iterable.iterator()));
+		FunctionalIterator<Integer> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterable.OfLong.")
+	void testExtendFunctionalPrimitiveIterableOfLong(Iterable<Long> iterable)
+	{
+		PrimitiveIterable.OfLong expected = Reducible.extend(PrimitiveIterable.unboxLong(iterable));
+		FunctionalIterable<Long> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("extend() does not extend a FunctionalPrimitiveIterator.OfLong.")
+	void testExtendFunctionalPrimitiveIteratorOfLong(Iterable<Long> iterable)
+	{
+		PrimitiveIterator.OfLong expected = Reducible.extend(PrimitiveIterable.unboxLong(iterable).iterator());
+		FunctionalIterator<Long> actual = Reducible.extend(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("extend() extends OfDouble to FunctionalPrimitiveIterable.")
+	void testExtendPrimitiveIterableOfDouble(Iterable<Double> iterable)
+	{
+		Iterable<Double> primitive = PrimitiveIterable.unboxDouble(iterable);
+		FunctionalIterable<Double> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterable);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("extend() extends OfDouble to FunctionalPrimitiveIterator.")
+	void testExtendPrimitiveIteratorOfDouble(Iterable<Double> iterable)
+	{
+		Iterator<Double> primitive = PrimitiveIterable.unboxDouble(iterable).iterator();
+		FunctionalIterator<Double> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterator);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("extend() extends OfInt to FunctionalPrimitiveIterable.")
+	void testExtendPrimitiveIterableOfInt(Iterable<Integer> iterable)
+	{
+		Iterable<Integer> primitive = PrimitiveIterable.unboxInt(iterable);
+		FunctionalIterable<Integer> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterable);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("extend() extends OfInt to FunctionalPrimitiveIterator.")
+	void testExtendPrimitiveIteratorOfInt(Iterable<Integer> iterable)
+	{
+		Iterator<Integer> primitive = PrimitiveIterable.unboxInt(iterable.iterator());
+		FunctionalIterator<Integer> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterator);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("extend() extends OfLong to FunctionalPrimitiveIterable.")
+	void testExtendPrimitiveIterableOfLong(Iterable<Long> iterable)
+	{
+		Iterable<Long> primitive = PrimitiveIterable.unboxLong(iterable);
+		FunctionalIterable<Long> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterable);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("extend() extends OfLong to FunctionalPrimitiveIterator.")
+	void testExtendPrimitiveIteratorOfLong(Iterable<Long> iterable)
+	{
+		Iterator<Long> primitive = PrimitiveIterable.unboxLong(iterable).iterator();
+		FunctionalIterator<Long> actual = Reducible.extend(primitive);
+		assertTrue(actual instanceof FunctionalPrimitiveIterator);
+	}
+
+	@Test
+	@DisplayName("extend() with null throws NullPointerException.")
+	void testExtendIterable_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.extend((Iterable<?>) null));
+	}
+
+	@Test
+	@DisplayName("extend() with null throws NullPointerException.")
+	void testExtendIterator_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.extend((Iterator<?>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("unboxDouble() yields same sequence as the underlying iterable.")
+	void testUnboxDoubleIterable(Iterable<Double> iterable)
+	{
+		PrimitiveIterable.OfDouble expected = PrimitiveIterable.unboxDouble(iterable);
+		FunctionalPrimitiveIterable.OfDouble actual = Reducible.unboxDouble(iterable);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("unboxDouble() yields same sequence as the underlying iterator.")
+	void testUnboxDoubleIterator(Iterable<Double> iterable)
+	{
+		PrimitiveIterator.OfDouble expected = PrimitiveIterable.unboxDouble(iterable.iterator());
+		FunctionalPrimitiveIterator.OfDouble actual = Reducible.unboxDouble(iterable.iterator());
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("unboxDouble() does not unbox a FunctionalPrimitiveIterable.OfDouble.")
+	void testUnboxDoubleIterableOfDouble(Iterable<Double> iterable)
+	{
+		PrimitiveIterable.OfDouble expected = Reducible.extend(PrimitiveIterable.unboxDouble(iterable));
+		FunctionalPrimitiveIterable.OfDouble actual = Reducible.unboxDouble(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesDouble")
+	@DisplayName("unboxDouble() does not unbox a FunctionalPrimitiveIterator.OfDouble.")
+	void testUnboxDoubleIteratorOfDouble(Iterable<Double> iterable)
+	{
+		PrimitiveIterator.OfDouble expected = Reducible.extend(PrimitiveIterable.unboxDouble(iterable.iterator()));
+		FunctionalPrimitiveIterator.OfDouble actual = Reducible.unboxDouble(expected);
+		assertSame(expected, actual);
+	}
+
+	@Test
+	@DisplayName("unboxDouble() with null throws NullPointerException.")
+	void testUnboxDoubleIterable_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxDouble((Iterable<Double>) null));
+	}
+
+	@Test
+	@DisplayName("unboxDouble() with null throws NullPointerException.")
+	void testUnboxDoubleIterator_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxDouble((Iterator<Double>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxDouble() with an Iterable containing null throws NullPointerException.")
+	void testUnboxDoubleIterable_NullValues(Iterable<Double> iterable)
+	{
+		FunctionalPrimitiveIterable.OfDouble primitive = Reducible.unboxDouble(iterable);
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxDouble() with an Iterator containing null throws NullPointerException.")
+	void testUnboxDoubleIterator_NullValues(Iterable<Double> iterable)
+	{
+		FunctionalPrimitiveIterator.OfDouble primitive = Reducible.unboxDouble(iterable.iterator());
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("unboxInt() yields same sequence as the underlying iterable.")
+	void testUnboxIntIterable(Iterable<Integer> iterable)
+	{
+		PrimitiveIterable.OfInt expected = PrimitiveIterable.unboxInt(iterable);
+		FunctionalPrimitiveIterable.OfInt actual = Reducible.unboxInt(iterable);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("unboxInt() yields same sequence as the underlying iterator.")
+	void testUnboxIntIterator(Iterable<Integer> iterable)
+	{
+		PrimitiveIterator.OfInt expected = PrimitiveIterable.unboxInt(iterable.iterator());
+		FunctionalPrimitiveIterator.OfInt actual = Reducible.unboxInt(iterable.iterator());
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("unboxInt() does not unbox a FunctionalPrimitiveIterable.OfInt.")
+	void testUnboxIntIterableOfInt(Iterable<Integer> iterable)
+	{
+		PrimitiveIterable.OfInt expected = Reducible.extend(PrimitiveIterable.unboxInt(iterable));
+		FunctionalPrimitiveIterable.OfInt actual = Reducible.unboxInt(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesInt")
+	@DisplayName("unboxInt() does not unbox a FunctionalPrimitiveIterator.OfInt.")
+	void testUnboxIntIteratorOfInt(Iterable<Integer> iterable)
+	{
+		PrimitiveIterator.OfInt expected = Reducible.extend(PrimitiveIterable.unboxInt(iterable.iterator()));
+		FunctionalPrimitiveIterator.OfInt actual = Reducible.unboxInt(expected);
+		assertSame(expected, actual);
+	}
+
+	@Test
+	@DisplayName("unboxInt() with null throws NullPointerException.")
+	void testUnboxIntIterable_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxInt((Iterable<Integer>) null));
+	}
+
+	@Test
+	@DisplayName("unboxInt() with null throws NullPointerException.")
+	void testUnboxIntIterator_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxInt((Iterator<Integer>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxInt() with an Iterable containing null throws NullPointerException.")
+	void testUnboxIntIterable_NullValues(Iterable<Integer> iterable)
+	{
+		FunctionalPrimitiveIterable.OfInt primitive = Reducible.unboxInt(iterable);
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxInt() with an Iterator containing null throws NullPointerException.")
+	void testUnboxIntIterator_NullValues(Iterable<Integer> iterable)
+	{
+		FunctionalPrimitiveIterator.OfInt primitive = Reducible.unboxInt(iterable.iterator());
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("unboxLong() yields same sequence as the underlying iterable.")
+	void testUnboxLongIterable(Iterable<Long> iterable)
+	{
+		PrimitiveIterable.OfLong expected = PrimitiveIterable.unboxLong(iterable);
+		FunctionalPrimitiveIterable.OfLong actual = Reducible.unboxLong(iterable);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("unboxLong() yields same sequence as the underlying iterator.")
+	void testUnboxLong(Iterable<Long> iterable)
+	{
+		PrimitiveIterator.OfLong expected = PrimitiveIterable.unboxLong(iterable.iterator());
+		FunctionalPrimitiveIterator.OfLong actual = Reducible.unboxLong(iterable.iterator());
+		assertIteratorEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("unboxLong() does not unbox a FunctionalPrimitiveIterable.OfDouble.")
+	void testUnboxLongIterableOfLong(Iterable<Long> iterable)
+	{
+		PrimitiveIterable.OfLong expected = Reducible.extend(PrimitiveIterable.unboxLong(iterable));
+		FunctionalPrimitiveIterable.OfLong actual = Reducible.unboxLong(expected);
+		assertSame(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesLong")
+	@DisplayName("unboxLong() does not unbox a FunctionalPrimitiveIterator.OfDouble.")
+	void testUnboxLongIteratorOfLong(Iterable<Long> iterable)
+	{
+		PrimitiveIterator.OfLong expected = Reducible.extend(PrimitiveIterable.unboxLong(iterable.iterator()));
+		FunctionalPrimitiveIterator.OfLong actual = Reducible.unboxLong(expected);
+		assertSame(expected, actual);
+	}
+
+	@Test
+	@DisplayName("unboxLong() with null throws NullPointerException.")
+	void testUnboxLongIterable_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxLong((Iterable<Long>) null));
+	}
+
+	@Test
+	@DisplayName("unboxLong() with null throws NullPointerException.")
+	void testUnboxLongIterator_Null()
+	{
+		assertThrows(NullPointerException.class, () -> Reducible.unboxLong((Iterator<Long>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxInt() with an Iterable containing null throws NullPointerException.")
+	void testUnboxLongIterable_NullValues(Iterable<Long> iterable)
+	{
+		FunctionalPrimitiveIterable.OfLong primitive = Reducible.unboxLong(iterable);
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getIterablesNull")
+	@DisplayName("unboxInt() with an Iterator containing null throws NullPointerException.")
+	void testUnboxLongIterator_NullValues(Iterable<Long> iterable)
+	{
+		FunctionalPrimitiveIterator.OfLong primitive = Reducible.unboxLong(iterable.iterator());
+		assertThrows(NullPointerException.class, primitive::consume);
+	}
+}
diff --git a/prism/unit-tests/common/iterable/ReducibleTest.java b/prism/unit-tests/common/iterable/ReducibleTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6932b474a9829e7fdd11ec1347967a63567cf2d1
--- /dev/null
+++ b/prism/unit-tests/common/iterable/ReducibleTest.java
@@ -0,0 +1,920 @@
+package common.iterable;
+
+import common.functions.DoubleObjToDoubleFunction;
+import common.functions.IntObjToIntFunction;
+import common.functions.LongObjToLongFunction;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.*;
+import java.util.function.*;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public interface ReducibleTest<E, T extends Reducible<E, ?>>
+{
+	default T getAnyReducible()
+	{
+		Optional<Supplier<T>> any = getReducibles().findAny();
+		assert any.isPresent();
+		return any.get().get();
+	}
+
+	default Stream<Supplier<T>> getReducibles()
+	{
+		return Stream.concat(Stream.concat(getEmptyReducibles(), getSingletonReducibles()), getMultitonReducibles());
+	}
+
+	Stream<Supplier<T>> getDuplicatesReducibles();
+
+	Stream<Supplier<T>> getEmptyReducibles();
+
+	Stream<Supplier<T>> getSingletonReducibles();
+
+	Stream<Supplier<T>> getMultitonReducibles();
+
+	Iterable<Object> getExcluded(T reducible);
+
+
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectArray(Supplier<T> supplier)
+	{
+		int n = (int) supplier.get().count();
+		Object[] expected = new Object[n];
+		Range.RangeIterator index = new Range(n).iterator();
+		supplier.get().forEach(e -> expected[index.nextInt()] = e);
+		Object[] actual = supplier.get().collect((E[]) new Object[n]);  // exploit that E[] is Object[] at runtime
+		assertArrayEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectArrayOffset(Supplier<T> supplier)
+	{
+		int n = (int) supplier.get().count(), offset = 2, tail = 3;
+		Object[] expected = new Object[offset + n + tail];
+		Range.RangeIterator index = new Range(offset, offset + n).iterator();
+		supplier.get().forEach(e -> expected[index.nextInt()] = e);
+		Object[] actual = supplier.get().collect((E[]) new Object[offset + n + tail], offset);  // exploit that E[] is Object[] at runtime
+		assertArrayEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectCollection(Supplier<T> supplier)
+	{
+		List<E> expected = new ArrayList<>();
+		supplier.get().forEach(expected::add);
+		List<E> actual = supplier.get().collect(new ArrayList<>());
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectSupplier(Supplier<T> supplier)
+	{
+		List<E> expected = new ArrayList<>();
+		supplier.get().forEach(expected::add);
+		List<E> actual = supplier.get().collect((Supplier<? extends List<E>>) ArrayList::new);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	default void testCollect_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.collect((E[]) null));
+		assertThrows(NullPointerException.class, () -> reducible.collect(null, 0));
+		assertThrows(NullPointerException.class, () -> reducible.collect((Collection<? super E>) null));
+		assertThrows(NullPointerException.class, () -> reducible.collect((Supplier<? extends Collection<? super E>>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectAndCountArray(Supplier<T> supplier)
+	{
+		int n = (int) supplier.get().count(), tail = 3;
+		Object[] expected = new Object[n + tail];
+		Range.RangeIterator index = new Range(n).iterator();
+		supplier.get().forEach(e -> expected[index.nextInt()] = e);
+		Object[] actual = new Object[n + tail];
+		long count = supplier.get().collectAndCount((E[]) actual);  // exploit that E[] is Object[] at runtime
+		assertEquals(n, count);
+		assertArrayEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectAndCountArrayOffset(Supplier<T> supplier)
+	{
+		int n = (int) supplier.get().count(), offset = 2, tail = 3;
+		Object[] expected = new Object[offset + n + tail];
+		Range.RangeIterator index = new Range(offset, offset + n).iterator();
+		supplier.get().forEach(e -> expected[index.nextInt()] = e);
+		Object[] actual = new Object[offset + n + tail];
+		long count = supplier.get().collectAndCount((E[]) actual, offset);  // exploit that E[] is Object[] at runtime
+		assertEquals(n, count);
+		assertArrayEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCollectAndCountCollection(Supplier<T> supplier)
+	{
+		List<E> expected = new ArrayList<>();
+		supplier.get().forEach(expected::add);
+		List<E> actual = new ArrayList<>();
+		long count = supplier.get().collectAndCount(actual);
+		assertEquals(expected.size(), count);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	default void testCollectAndCount_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.collectAndCount((E[]) null));
+		assertThrows(NullPointerException.class, () -> reducible.collectAndCount(null, 0));
+		assertThrows(NullPointerException.class, () -> reducible.collectAndCount((Collection<? super E>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+	default void testCollectDistinct(Supplier<T> supplier)
+	{
+		Set<E> expected = new HashSet<>();
+		supplier.get().forEach(expected::add);
+		List<E> actual = supplier.get().collectDistinct().collect(new ArrayList<>());
+		assertTrue(expected.containsAll(actual), "actual =< expected");
+		assertTrue(actual.containsAll(expected), "actual >= expected");
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testConsume(Supplier<T> supplier)
+	{
+		// Just test that consume yields the receiver.
+		// There is no way to test whether consume does anything beyond this.
+		T expected = supplier.get();
+		Reducible<E, ?> actual = expected.consume();
+		assertSame(expected, actual);
+	}
+
+	void testConcat(Supplier<T> supplier);
+
+	@ParameterizedTest
+	@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+	default void testDistinct(Supplier<T> supplier)
+	{
+		List<E> expected = new ArrayList<>();
+		supplier.get().forEach(e -> {if (! expected.contains(e)) expected.add(e);});
+		List<E> actual = supplier.get().distinct().collect(new ArrayList<>());
+		assertEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getReducibles", "getDuplicatesReducibles"})
+	default void testDedupe(Supplier<T> supplier)
+	{
+		List<E> expected = new ArrayList<>();
+		supplier.get().forEach(e -> {
+			Object last = expected.isEmpty() ? new Object() : expected.get(expected.size() - 1);
+			if (! Objects.equals(last, e)) {
+				expected.add(e);
+			}
+		});
+		List<Object> actual = supplier.get().dedupe().collect(new ArrayList<>());
+		assertEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testFilter(Supplier<T> supplier)
+	{
+		ArrayList<E> expected = new ArrayList<>();
+		int n = supplier.get().reduce(0, (int c, E e) -> {
+			if (c % 2 == 0) expected.add(e);
+			return ++c;
+		});
+		Range.RangeIterator index = new Range(n).iterator();
+		Reducible<E, ?> actual = supplier.get().filter(e -> index.nextInt() % 2 == 0);
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@Test
+	default void testFilter_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.filter(null));
+	}
+
+	void testFlatMap(Supplier<T> supplier);
+
+	void testFlatMapToDouble(Supplier<T> supplier);
+
+	void testFlatMapToInt(Supplier<T> supplier);
+
+	void testFlatMapToLong(Supplier<T> supplier);
+
+	void testFlatMapToNull(Supplier<T> supplier);
+
+	void testFlatMap_Null();
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testMap(Supplier<T> supplier)
+	{
+		String prefix = "Item: ";
+		List<String> expected = new ArrayList<>();
+		supplier.get().forEach(e -> expected.add(prefix + e));
+		Reducible<String, ?> actual = supplier.get().map(e -> prefix + e);
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testMapToDouble(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		FunctionalIterable<Double> expected = range.map((int i) -> (double) i);
+		Range.RangeIterator index = range.iterator();
+		PrimitiveReducible.OfDouble<?> actual = supplier.get().mapToDouble(e -> index.next());
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testMapToInt(Supplier<T> supplier)
+	{
+		Range expected = new Range((int) supplier.get().count());
+		Range.RangeIterator index = expected.iterator();
+		PrimitiveReducible.OfInt<?> actual = supplier.get().mapToInt(e -> index.next());
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testMapToLong(Supplier<T> supplier)
+	{
+		Range range = new Range((int) supplier.get().count());
+		FunctionalIterable<Long> expected = range.map((int i) -> (long) i);
+		Range.RangeIterator index = range.iterator();
+		PrimitiveReducible.OfLong<?> actual = supplier.get().mapToLong(e -> index.next());
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@Test
+	default void testMap_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.map(null));
+		assertThrows(NullPointerException.class, () -> reducible.mapToDouble(null));
+		assertThrows(NullPointerException.class, () -> reducible.mapToInt(null));
+		assertThrows(NullPointerException.class, () -> reducible.mapToLong(null));
+	}
+
+	void testForEach(Supplier<T> supplier);
+
+	@Test
+	default void testForEach_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.forEach(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testIsEmpty_Empty(Supplier<T> supplier)
+	{
+		assertTrue(supplier.get().isEmpty());
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+	default void testIsEmpty_NonEmpty(Supplier<T> supplier)
+	{
+		assertFalse(supplier.get().isEmpty());
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testReduceBinaryOperatorOfE_Empty(Supplier<T> supplier)
+	{
+		BinaryOperator<E> nop = (res, each) -> null;
+		Optional<E> actual = supplier.get().reduce(nop);
+		assertEquals(Optional.empty(), actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getSingletonReducibles")
+	default void testReduceBinaryOperatorOfE_Singleton(Supplier<T> supplier)
+	{
+		BinaryOperator<E> nop = (res, each) -> null;
+		E expected = supplier.get().detect(e -> true);
+		if (expected == null) {
+			assertThrows(NullPointerException.class, () -> supplier.get().reduce(nop));
+		} else {
+			Optional<E> actual = supplier.get().reduce(nop);
+			assertTrue(actual.isPresent());
+			assertEquals(expected, actual.get());
+		}
+	}
+
+	@ParameterizedTest
+	@MethodSource("getMultitonReducibles")
+	default void testReduceBinaryOperatorOfE_Multiton(Supplier<T> supplier)
+	{
+		ArrayList<E> expected = supplier.get().collect(new ArrayList<>());
+		List<E> actual = new ArrayList<>();
+		E probe = (E) new Object(); // "unique" value, exploit that E is Object at runtime
+		BinaryOperator<E> collect = (res, each) -> {
+			if (actual.isEmpty()) {
+				actual.add(res);
+				actual.add(each);
+				return (E) probe;
+			} else {
+				actual.add(each);
+				return res;
+			}
+		};
+		Optional<E> result = supplier.get().reduce(collect);
+		assertTrue(result.isPresent());
+		assertEquals(probe, result.get());
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getMultitonReducibles")
+	default void testReduceBinary_ResultNull(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertThrows(NullPointerException.class, () -> reducible.reduce((res, each) -> null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testReduce(Supplier<T> supplier)
+	{
+		ArrayList<E> expected = supplier.get().collect(new ArrayList<>());
+		BiFunction<List<E>, E, List<E>> collect = (seq, each) -> {seq.add(each); return seq;};
+		List<E> actual = supplier.get().reduce(new ArrayList<>(), collect);
+		assertIterableEquals(expected, actual);
+		assertDoesNotThrow(() -> supplier.get().reduce(null, (obj, each) -> null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testReduceDouble(Supplier<T> supplier)
+	{
+		List<E> expected = supplier.get().collect(new ArrayList<>());
+		List<E> actual = new ArrayList<>();
+		DoubleObjToDoubleFunction<E> collect = (res, each) -> {actual.add(each); return res;};
+		double result = supplier.get().reduce(Double.MIN_VALUE, collect);
+		assertEquals(Double.MIN_VALUE, result);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testReduceInt(Supplier<T> supplier)
+	{
+		List<E> expected = supplier.get().collect(new ArrayList<>());
+		List<E> actual = new ArrayList<>();
+		IntObjToIntFunction<E> collect = (res, each) -> {actual.add(each); return res;};
+		int result = supplier.get().reduce(Integer.MIN_VALUE, collect);
+		assertEquals(Integer.MIN_VALUE, result);
+		assertIterableEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testReduceLong(Supplier<T> supplier)
+	{
+		List<E> expected = supplier.get().collect(new ArrayList<>());
+		List<E> actual = new ArrayList<>();
+		LongObjToLongFunction<E> collect = (res, each) -> {actual.add(each); return res;};
+		long result = supplier.get().reduce(Long.MIN_VALUE, collect);
+		assertEquals(Long.MIN_VALUE, result);
+		assertIterableEquals(expected, actual);
+	}
+
+	@Test
+	default void testReduce_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.reduce(null));
+		assertThrows(NullPointerException.class, () -> reducible.reduce(new Object(),null));
+		assertThrows(NullPointerException.class, () -> reducible.reduce(0.0, null));
+		assertThrows(NullPointerException.class, () -> reducible.reduce(0, (IntObjToIntFunction<? super E>) null));
+		assertThrows(NullPointerException.class, () -> reducible.reduce(0L, (LongObjToLongFunction<? super E>) null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testNonNull(Supplier<T> supplier)
+	{
+		List<Object> expected = new ArrayList<>();
+		supplier.get().forEach(e -> {if (e != null) expected.add(e);});
+		Reducible<E, ?> actual = supplier.get().nonNull();
+		assertIterableEquals(expected, actual.collect(new ArrayList<>()));
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+	default void testAllMatch(Supplier<T> supplier)
+	{
+		// match all elements
+		assertTrue(supplier.get().allMatch(each -> true), "Expected allMatch() == true");
+		// match not all elements
+		Predicate<E> matchNotAll = new Predicate<>() {
+			// match: no element if singleton, otherwise every odd element
+			boolean flag = supplier.get().count() == 1;
+			@Override
+			public boolean test(E t)
+			{
+				flag = !flag;
+				return flag;
+			}
+		};
+		assertFalse(supplier.get().allMatch(matchNotAll), "Expected allMatch() == false");
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testAllMatch_Empty(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertTrue(reducible.allMatch(each -> false), "Exepted allMatch() == true if reducible is empty");
+	}
+
+	@Test
+	default void testAllMatch_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.allMatch(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+	default void testAnyMatch(Supplier<T> supplier)
+	{
+		// match no element
+		assertFalse(supplier.get().anyMatch(each -> false), "Expected anyMatch() == false");
+		// match some elements
+		Predicate<E> matchSome = new Predicate<>() {
+			// match: first element if singleton, otherwise every even element
+			boolean flag = supplier.get().count() > 1;
+			@Override
+			public boolean test(E t)
+			{
+				flag = !flag;
+				return flag;
+			}
+		};
+		assertTrue(supplier.get().anyMatch(matchSome), "Expected anyMatch() == true");
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testAnyMatch_Empty(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertFalse(reducible.anyMatch(each -> true), "Exepted anyMatch() == false if reducible is empty");
+	}
+
+	@Test
+	default void testAnyMatch_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.anyMatch(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource({"getSingletonReducibles", "getMultitonReducibles"})
+	default void testNoneMatch(Supplier<T> supplier)
+	{
+		// match no element
+		assertTrue(supplier.get().noneMatch(each -> false), "Expected noneMatch() == true");
+		// match some elements
+		Predicate<E> matchSome = new Predicate<>() {
+			// match: first element if singleton, otherwise every even element
+			boolean flag = supplier.get().count() > 1;
+			@Override
+			public boolean test(E t)
+			{
+				flag = !flag;
+				return flag;
+			}
+		};
+		assertFalse(supplier.get().noneMatch(matchSome), "Expected noneMatch() == false");
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testNoneMatch_Empty(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertTrue(reducible.allMatch(each -> false), "Exepted noneMatch() == false if iterator is empty");
+	}
+
+	@Test
+	default void testNoneMatch_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.noneMatch(null));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testAsString(Supplier<T> supplier)
+	{
+		String expected = "[" + supplier.get().reduce("", (str, each) -> str + (str.isEmpty() ? "" : ", ") + each) + "]";
+		String actual = supplier.get().asString();
+		assertEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testContains(Supplier<T> supplier)
+	{
+		ArrayList<E> elements = supplier.get().collect(new ArrayList<>());
+		for (E each : elements) {
+			assertTrue(supplier.get().contains(each), "Expected contains(" + each + ") == true");
+		}
+		for (Object each : getExcluded(supplier.get())) {
+			assertFalse(supplier.get().contains(each), "Expected contains(" + each + ") == false");
+		}
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCount(Supplier<T> supplier)
+	{
+		ArrayList<E> elements = supplier.get().collect(new ArrayList<>());
+		long expected = elements.size();
+		long actual = supplier.get().count();
+		assertEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testCountPredicate(Supplier<T> supplier)
+	{
+		ArrayList<E> elements = supplier.get().collect(new ArrayList<>());
+		long expected = (elements.size() + 1) / 2;
+		Predicate<E> odd = new Predicate<>() {
+			int i = 1;
+			@Override
+			public boolean test(E t)
+			{
+				return i++ % 2 == 1;
+			}
+		};
+		long actual = supplier.get().count(odd);
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	default void testCountPredicate_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.count(null));
+	}
+
+
+	@ParameterizedTest
+	@MethodSource("getReducibles")
+	default void testDetect_AllFalse(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertThrows(NoSuchElementException.class, () -> reducible.detect(each -> false));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getEmptyReducibles")
+	default void testDetect_Empty(Supplier<T> supplier)
+	{
+		Reducible<E, ?> reducible = supplier.get();
+		assertThrows(NoSuchElementException.class, () -> reducible.detect(each -> true));
+	}
+
+	@ParameterizedTest
+	@MethodSource("getSingletonReducibles")
+	default void testDetect_Singleton(Supplier<T> supplier)
+	{
+		// match first element
+		E expected = supplier.get().collect(new ArrayList<>()).get(0);
+		E actual = supplier.get().detect(each -> true);
+		assertEquals(expected, actual);
+	}
+
+	@ParameterizedTest
+	@MethodSource("getMultitonReducibles")
+	default void testDetect_Multiton(Supplier<T> supplier)
+	{
+		// match second element
+		E expected = supplier.get().collect(new ArrayList<>()).get(1);
+		Predicate<E> second = new Predicate<>() {
+			boolean flag = true;
+			@Override
+			public boolean test(E each)
+			{
+				flag = !flag;
+				return flag;
+			}
+		};
+		E actual = supplier.get().detect(second);
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	default void testDetect_Null()
+	{
+		Reducible<E, ?> reducible = getAnyReducible();
+		assertThrows(NullPointerException.class, () -> reducible.detect(null));
+	}
+
+
+
+	interface Of<E, T extends Reducible<E, ?>> extends ReducibleTest<E, T>
+	{
+		T getReducible(Object[] objects);
+
+		@Override
+		default Stream<Supplier<T>> getDuplicatesReducibles()
+		{
+			return getDuplicatesArraysOfObject().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getEmptyReducibles()
+		{
+			return getEmptyArraysOfObject().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getSingletonReducibles()
+		{
+			return getSingletonArraysOfObject().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Stream<Supplier<T>> getMultitonReducibles()
+		{
+			return getMultitonArraysOfObject().map(args-> () -> getReducible(args));
+		}
+
+		@Override
+		default Iterable<Object> getExcluded(T iterable)
+		{
+			return getUniqueObjects();
+		}
+	}
+
+
+
+	// Test-data sets
+
+	default Stream<Object[]> getArraysOfObject()
+	{
+		return Stream.concat(Stream.concat(getEmptyArraysOfObject(), getSingletonArraysOfObject()), getMultitonArraysOfObject());
+	}
+
+	/* Workaround to pass Object[] as argument */
+	default Stream<Arguments> getArraysAsArguments()
+	{
+		return getArraysOfObject().map(array -> Arguments.of((Object) array));
+	}
+
+	default Stream<Object[]> getDuplicatesArraysOfObject()
+	{
+		return Stream.of(
+				new Object[] {null, null,
+						"first", "first",
+						"second", "second"},
+				new Object[] {null, null,
+						"first", "first",
+						"third", "third",
+						"first", "first",
+						null, null});
+	}
+
+	default Stream<Object[]> getEmptyArraysOfObject()
+	{
+		return Stream.<Object[]>of(new Object[] {});
+	}
+
+	default Stream<Object[]> getSingletonArraysOfObject()
+	{
+		return Stream.of(new Object[] {"first"},
+				new Object[] {null});
+	}
+
+	default Stream<Object[]> getMultitonArraysOfObject()
+	{
+		return Stream.of(new Object[] {"first", "second", "third"},
+				new Object[] {null, "first", null, "second", null, "third", null});
+	}
+
+	/* Workaround to pass Object[] as argument */
+	default Stream<Arguments> getMultitonArraysAsArguments()
+	{
+		return getMultitonArraysOfObject().map(array -> Arguments.of((Object) array));
+	}
+
+	default List<Object> getUniqueObjects()
+	{
+		return List.of(new Object(), new Object(), new Object());
+	}
+
+	default Stream<double[]> getArraysOfDouble()
+	{
+		return Stream.concat(Stream.concat(getEmptyArraysOfDouble(), getSingletonArraysOfDouble()), getMultitonArraysOfDouble());
+	}
+
+	default Stream<double[]> getDuplicatesArraysOfDouble()
+	{
+		return Stream.of(new double[] {-3.5, -3.5,
+						-2.0, -2.0,
+						-1.0, -1.0,
+						-0.0, +0.0,
+						+1.0, +1.0,
+						+2.0, +2.0,
+						+3.5, +3.5},
+				new double[] {Double.NaN, Double.NaN,
+						Double.MIN_VALUE, Double.MIN_VALUE,
+						Double.MIN_NORMAL, Double.MIN_NORMAL,
+						Double.MAX_VALUE, Double.MAX_VALUE,
+						Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
+						Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY,
+						Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
+						Double.MAX_VALUE, Double.MAX_VALUE,
+						Double.MIN_NORMAL, Double.MIN_NORMAL,
+						Double.MIN_VALUE, Double.MIN_VALUE,
+						Double.NaN, Double.NaN});
+	}
+
+	default Stream<double[]> getEmptyArraysOfDouble()
+	{
+		return Stream.of(new double[] {});
+	}
+
+	default Stream<double[]> getSingletonArraysOfDouble()
+	{
+		return Stream.of(new double[] {1.0});
+	}
+
+	default Stream<double[]> getMultitonArraysOfDouble()
+	{
+		return Stream.of(new double[] {-3.5, -2.0, -1.0, 0.0, 1.0, 2.0, 3.5},
+				new double[] {Double.NaN,
+						Double.MIN_VALUE,
+						Double.MIN_NORMAL,
+						Double.MAX_VALUE,
+						Double.NEGATIVE_INFINITY,
+						Double.POSITIVE_INFINITY,});
+	}
+
+	default List<Double> getExclusionListOfDouble()
+	{
+		List<Double> excluded = new ArrayList<>();
+		excluded.add(Double.NaN);
+		excluded.add(Double.NEGATIVE_INFINITY);
+		excluded.add(-100000000.0);
+		excluded.add(-10000.0);
+		excluded.add(-100.0);
+		excluded.add(-10.0);
+		excluded.add(-2.0);
+		excluded.add(-1.0);
+		excluded.add(-0.0);
+		excluded.add(+0.0);
+		excluded.add(Double.MIN_VALUE);
+		excluded.add(Double.MIN_NORMAL);
+		excluded.add(1.0);
+		excluded.add(2.0);
+		excluded.add(10.0);
+		excluded.add(100.0);
+		excluded.add(10000.0);
+		excluded.add(100000000.0);
+		excluded.add(Double.MAX_VALUE);
+		excluded.add(Double.POSITIVE_INFINITY);
+		return excluded;
+	}
+
+	default Stream<int[]> getArraysOfInt()
+	{
+		return Stream.concat(Stream.concat(getEmptyArraysOfInt(), getSingletonArraysOfInt()), getMultitonArraysOfInt());
+	}
+
+	default Stream<int[]> getDuplicatesArraysOfInt()
+	{
+		return Stream.of(new int[] {-3, -3,
+						-1, -1,
+						-2, -2,
+						-0, +0,
+						+1, +1,
+						+2, +2,
+						+3, +3},
+				new int[] {Integer.MIN_VALUE, Integer.MIN_VALUE,
+						Integer.MAX_VALUE, Integer.MAX_VALUE,
+						Integer.MIN_VALUE, Integer.MIN_VALUE});
+	}
+
+	default Stream<int[]> getEmptyArraysOfInt()
+	{
+		return Stream.of(new int[] {});
+	}
+
+	default Stream<int[]> getSingletonArraysOfInt()
+	{
+		return Stream.of(new int[] {1});
+	}
+
+	default Stream<int[]> getMultitonArraysOfInt()
+	{
+		return Stream.of(new int[] {-3, -2, -1, 0, 1, 2, 3},
+				new int[] {Integer.MIN_VALUE, Integer.MAX_VALUE});
+	}
+
+	default List<Integer> getExclusionListOfInt()
+	{
+		List<Integer> excluded = new ArrayList<>();
+		excluded.add(Integer.MIN_VALUE);
+		excluded.add(-100000000);
+		excluded.add(-10000);
+		excluded.add(-100);
+		excluded.add(-10);
+		excluded.add(-2);
+		excluded.add(-1);
+		excluded.add(0);
+		excluded.add(1);
+		excluded.add(2);
+		excluded.add(10);
+		excluded.add(100);
+		excluded.add(10000);
+		excluded.add(100000000);
+		excluded.add(Integer.MAX_VALUE);
+		return excluded;
+	}
+
+	default Stream<long[]> getArraysOfLong()
+	{
+		return Stream.concat(Stream.concat(getEmptyArraysOfLong(), getSingletonArraysOfLong()), getMultitonArraysOfLong());
+	}
+
+	default Stream<long []> getDuplicatesArraysOfLong()
+	{
+		return Stream.of(new long [] {-3L, -3L,
+						-2L, -2L,
+						-1L, -1L,
+						-0L, +0L,
+						+1L, +1L,
+						+2L, +2L,
+						+3L, +3L},
+				new long[] {Long.MIN_VALUE, Long.MIN_VALUE,
+						Long.MAX_VALUE, Long.MAX_VALUE,
+						Long.MIN_VALUE, Long.MIN_VALUE});
+	}
+
+	default Stream<long []> getEmptyArraysOfLong()
+	{
+		return Stream.of(new long [] {});
+	}
+
+	default Stream<long []> getSingletonArraysOfLong()
+	{
+		return Stream.of(new long [] {1L});
+	}
+
+	default Stream<long []> getMultitonArraysOfLong()
+	{
+		return Stream.of(new long [] {-3L, -2L, -1L, 0L, 1L, 2L, 3L},
+				new long[] {Long.MIN_VALUE, Long.MAX_VALUE});
+	}
+
+	default List<Long> getExclusionListOfLong()
+	{
+		List<Long> excluded = new ArrayList<>();
+		excluded.add(Long.MIN_VALUE);
+		excluded.add(-100000000L);
+		excluded.add(-10000L);
+		excluded.add(-100L);
+		excluded.add(-10L);
+		excluded.add(-2L);
+		excluded.add(-1L);
+		excluded.add(0L);
+		excluded.add(1L);
+		excluded.add(2L);
+		excluded.add(10L);
+		excluded.add(100L);
+		excluded.add(10000L);
+		excluded.add(100000000L);
+		excluded.add(Long.MAX_VALUE);
+		return excluded;
+	}
+}
diff --git a/prism/unit-tests/common/iterable/SingletonIterableTest.java b/prism/unit-tests/common/iterable/SingletonIterableTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e5a4469bedebda2fead9e9e8164aaea040af71f
--- /dev/null
+++ b/prism/unit-tests/common/iterable/SingletonIterableTest.java
@@ -0,0 +1,322 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.TestInstance;
+
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+interface SingletonIterableTest<E, T extends SingletonIterable<E>> extends FunctionalIterableTest<E,T>
+{
+	@Override
+	default void testAllMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testAnyMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testDetect_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testDetect_Multiton(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testNoneMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testIsEmpty_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testReduceBinary_ResultNull(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */}
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Multiton(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements SingletonIterableTest<Object,SingletonIterable.Of<Object>>, FunctionalIterableTest.Of<Object,SingletonIterable.Of<Object>>
+	{
+		@Override
+		public SingletonIterable.Of<Object> getReducible(Object[] objects)
+		{
+			assert objects.length == 1 : "singleton array expected";
+			return new SingletonIterable.Of<>(objects[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.Of<Object>>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.Of<Object>>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.Of<Object>>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements SingletonIterableTest<Double,SingletonIterable.OfDouble>, FunctionalPrimitiveIterableTest.OfDouble<SingletonIterable.OfDouble>
+	{
+		@Override
+		public SingletonIterable.OfDouble getReducible(double[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterable.OfDouble(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfDouble>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfDouble>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfDouble>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatch_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testAllMatchDoublePredicate_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Multiton(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchDoublePredicate_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testSum_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterable.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements SingletonIterableTest<Integer,SingletonIterable.OfInt>, FunctionalPrimitiveIterableTest.OfInt<SingletonIterable.OfInt>
+	{
+		@Override
+		public SingletonIterable.OfInt getReducible(int[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterable.OfInt(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfInt>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfInt>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfInt>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatch_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testAllMatchIntPredicate_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectIntPredicate_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectIntPredicate_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchIntPredicate_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterable.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements SingletonIterableTest<Long,SingletonIterable.OfLong>, FunctionalPrimitiveIterableTest.OfLong<SingletonIterable.OfLong>
+	{
+		@Override
+		public SingletonIterable.OfLong getReducible(long[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterable.OfLong(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfLong>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfLong>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterable.OfLong>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchLongPredicate_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectLongPredicate_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectLongPredicate_Multiton(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchLongPredicate_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterable.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+}
diff --git a/prism/unit-tests/common/iterable/SingletonIteratorTest.java b/prism/unit-tests/common/iterable/SingletonIteratorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..56f146052e8aec390dacab864f41ca5d658bd42a
--- /dev/null
+++ b/prism/unit-tests/common/iterable/SingletonIteratorTest.java
@@ -0,0 +1,322 @@
+package common.iterable;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.TestInstance;
+
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+interface SingletonIteratorTest<E, T extends SingletonIterator<E>> extends FunctionalIteratorTest<E,T>
+{
+	@Override
+	default void testAllMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testAnyMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testDetect_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testDetect_Multiton(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testNoneMatch_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testIsEmpty_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testReduceBinary_ResultNull(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */}
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Empty(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+	@Override
+	default void testReduceBinaryOperatorOfE_Multiton(Supplier<T> supplier)
+	{ /* singletons hold exactly one value */ }
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class Of implements SingletonIteratorTest<Object,SingletonIterator.Of<Object>>, FunctionalIteratorTest.Of<Object,SingletonIterator.Of<Object>>
+	{
+		@Override
+		public SingletonIterator.Of<Object> getReducible(Object[] objects)
+		{
+			assert objects.length == 1 : "singleton array expected";
+			return new SingletonIterator.Of<>(objects[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.Of<Object>>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.Of<Object>>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.Of<Object>>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfDouble implements SingletonIteratorTest<Double,SingletonIterator.OfDouble>, FunctionalPrimitiveIteratorTest.OfDouble<SingletonIterator.OfDouble>
+	{
+		@Override
+		public SingletonIterator.OfDouble getReducible(double[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterator.OfDouble(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfDouble>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfDouble>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfDouble>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatch_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testAllMatchDoublePredicate_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectDoublePredicate_Multiton(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchDoublePredicate_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testSum_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterator.OfDouble> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfInt implements SingletonIteratorTest<Integer,SingletonIterator.OfInt>, FunctionalPrimitiveIteratorTest.OfInt<SingletonIterator.OfInt>
+	{
+		@Override
+		public SingletonIterator.OfInt getReducible(int[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterator.OfInt(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfInt>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfInt>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfInt>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatch_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testAllMatchIntPredicate_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectIntPredicate_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectIntPredicate_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchIntPredicate_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceDoubleBinaryOperator_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceIntBinaryOperator_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterator.OfInt> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+
+
+
+	@Nested
+	@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+	class OfLong implements SingletonIteratorTest<Long,SingletonIterator.OfLong>, FunctionalPrimitiveIteratorTest.OfLong<SingletonIterator.OfLong>
+	{
+		@Override
+		public SingletonIterator.OfLong getReducible(long[] numbers)
+		{
+			assert numbers.length == 1 : "singleton array expected";
+			return new SingletonIterator.OfLong(numbers[0]);
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfLong>> getDuplicatesReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfLong>> getEmptyReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public Stream<Supplier<SingletonIterator.OfLong>> getMultitonReducibles()
+		{
+			return Stream.empty();
+		}
+
+		@Override
+		public void testAllMatchLongPredicate_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectLongPredicate_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testDetectLongPredicate_Multiton(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Multiton(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Multiton(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testNoneMatchLongPredicate_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testReduceLongBinaryOperator_Multiton(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMin_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+
+		@Override
+		public void testMax_Empty(Supplier<SingletonIterator.OfLong> supplier)
+		{ /* singletons hold exactly one value */ }
+	}
+}
diff --git a/prism/unit-tests/param/BigRationalTests.java b/prism/unit-tests/param/BigRationalTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8d854abbff56f8bc802c8dd7e603d03066a60eb
--- /dev/null
+++ b/prism/unit-tests/param/BigRationalTests.java
@@ -0,0 +1,47 @@
+package param;
+
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+import java.util.BitSet;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class BigRationalTests
+{
+	@Test
+	public void testBigRationalDouble()
+	{
+		// constants
+		assertEquals(BigRational.ZERO, new BigRational(0.0));
+		assertEquals(BigRational.ONE, new BigRational(1.0));
+		assertEquals(BigRational.ONE.negate(), new BigRational(-1.0));
+		assertEquals(BigRational.HALF, new BigRational(0.5));
+		assertEquals(BigRational.HALF.negate(), new BigRational(-0.5));
+
+		// double +/- 0.1: +/- 0x1.999999999999ap-4
+		BigRational pointOne = new BigRational(new BigInteger("1999999999999a", 16), BigInteger.ONE.shiftLeft(4+52));
+		assertEquals(pointOne, new BigRational(0.1));
+		assertEquals(pointOne.negate(), new BigRational(-0.1));
+
+		// largest/smallest fraction: +/- 0x1.fffffffffffffp51
+		BigRational maxFrac = new BigRational(new BigInteger("1fffffffffffff", 16), BigInteger.TWO);
+		assertEquals(maxFrac, new BigRational(0x1.fffffffffffffp51));
+		assertEquals(maxFrac.negate(), new BigRational(-0x1.fffffffffffffp51));
+
+		// largest/smallest non-fraction: +/- 1.0p52
+		BigRational nonFrac = new BigRational(new BigInteger("10000000000000", 16));
+		assertEquals(nonFrac, new BigRational(0x1.0p52));
+		assertEquals(nonFrac.negate(), new BigRational(-0x1.0p52));
+
+		// largest/smallest integer: +/- 0x1.fffffffffffffp+1023
+		BigRational maxInt = new BigRational(new BigInteger("1fffffffffffff", 16).shiftLeft(1023-52));
+		assertEquals(maxInt, new BigRational(Double.MAX_VALUE));
+		assertEquals(maxInt.negate(), new BigRational(-Double.MAX_VALUE));
+
+		// smallest absolute value:  +/- 0x0.0000000000001p-1022
+		BigRational absMin = new BigRational(BigInteger.ONE, BigInteger.ONE.shiftLeft(1022+52));
+		assertEquals(absMin, new BigRational(Double.MIN_VALUE));
+		assertEquals(absMin.negate(), new BigRational(-Double.MIN_VALUE));
+	}
+}
diff --git a/prism/unit-tests/prism/PrismUtilsTest.java b/prism/unit-tests/prism/PrismUtilsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d36804ed2ef947a0b5266ab571a3c0019e4fd1e
--- /dev/null
+++ b/prism/unit-tests/prism/PrismUtilsTest.java
@@ -0,0 +1,52 @@
+package prism;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static prism.PrismUtils.formatDouble;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+public class PrismUtilsTest
+{
+	@Test
+	public void testCompareVersions()
+	{
+		 String v[] =  { "1", "2.0", "2.1.alpha", "2.1.alpha.r5555", "2.1.alpha.r5557", "2.1.beta", "2.1.beta4", "2.1", "2.1.dev", "2.1.dev.r6666", "2.1.dev1", "2.1.dev2", "2.1.2", "2.9", "3", "3.4"};
+		 for (int i = 0; i < v.length; i++) {
+			 for (int j = 0; j < v.length; j++) {
+				 int d = PrismUtils.compareVersions(v[i], v[j]);
+				 assertEquals(d, Integer.compare(i, j));
+			 }
+		 }
+	}
+
+	@Test
+	public void testFormatDouble()
+	{
+		// Special values
+		assertEquals("NaN", formatDouble(Double.NaN));
+		assertEquals("-Infinity", formatDouble(Double.NEGATIVE_INFINITY));
+		assertEquals("Infinity", formatDouble(Double.POSITIVE_INFINITY));
+		// Rounding if number of significant digits > precision
+		assertEquals("123457", formatDouble(6, 123456.7));
+		assertEquals("1.23457e-05", formatDouble(6, 0.00001234567));
+		// Small numbers
+		assertEquals("0.0001", formatDouble(6, 0.0001));
+		assertEquals("1e-05", formatDouble(6, 0.00001));
+		assertEquals("1.23456e-05", formatDouble(6, 0.0000123456));
+		// Big numbers
+		assertEquals("999999", formatDouble(6, 999999.0));
+		assertEquals("1e+06", formatDouble(6, 999999.9));
+		assertEquals("1.23457e+06", formatDouble(6, 1234567.8));
+	}
+
+	@ParameterizedTest
+	@ValueSource(doubles = {Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.MIN_VALUE, Double.MIN_NORMAL, Double.MAX_VALUE})
+	public void testFormatDoubleDefaultPrecision(double d)
+	{
+		String serialized = PrismUtils.formatDouble(d);
+		double parsed = Double.parseDouble(serialized);
+		assertEquals(d, parsed);
+	}
+}