diff --git a/resources/3rdparty/include_cudd.cmake b/resources/3rdparty/include_cudd.cmake
index 0970cd9e8..1f02462b2 100644
--- a/resources/3rdparty/include_cudd.cmake
+++ b/resources/3rdparty/include_cudd.cmake
@@ -34,25 +34,13 @@ if (CMAKE_OSX_SYSROOT)
     set(CUDD_INCLUDE_FLAGS "CPPFLAGS=--sysroot=${CMAKE_OSX_SYSROOT}")
 endif()
 
-set(CUDD_CXX_COMPILER "${CMAKE_CXX_COMPILER}")
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
-	if (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 12.0.0.12000032)
-		if (CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 20.1.0)
-			message(WARNING "There are some known issues compiling CUDD on some setups. We implemented a workaround that mostly works, but if you still have problems compiling CUDD, especially if you do not use the default compiler of your system, please contact the Storm developers.")
-			# The issue is known to occur using the Command Line Tools for XCode 12.2. Apparently, it is fixed in the beta for XCode 12.3. 
-			set(CUDD_CXX_COMPILER "c++")
-		endif()
-	endif()
-endif()
-
-
 ExternalProject_Add(
         cudd3
         DOWNLOAD_COMMAND ""
         SOURCE_DIR ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0
         PREFIX ${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0
         PATCH_COMMAND ${AUTORECONF}
-        CONFIGURE_COMMAND ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --with-pic=yes --prefix=${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0 --libdir=${CUDD_LIB_DIR} CC=${CMAKE_C_COMPILER} CXX=${CUDD_CXX_COMPILER} ${CUDD_INCLUDE_FLAGS}
+        CONFIGURE_COMMAND ${STORM_3RDPARTY_SOURCE_DIR}/cudd-3.0.0/configure --enable-shared --enable-obj --with-pic=yes --prefix=${STORM_3RDPARTY_BINARY_DIR}/cudd-3.0.0 --libdir=${CUDD_LIB_DIR} CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} ${CUDD_INCLUDE_FLAGS}
         BUILD_COMMAND make ${STORM_CUDD_FLAGS}
         INSTALL_COMMAND make install
         BUILD_IN_SOURCE 0
diff --git a/resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm b/resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm
new file mode 100644
index 000000000..fde207637
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/brp16_2_mon_incr.pm
@@ -0,0 +1,148 @@
+// bounded retransmission protocol [D'AJJL01]
+// gxn/dxp 23/05/2001
+
+dtmc
+
+// number of chunks
+const int N = 16;
+// maximum number of retransmissions
+const int MAX = 2;
+
+// reliability of channels
+const double pL;
+const double pK;
+
+// timeouts
+const double TOMsg;
+const double TOAck;
+
+module sender
+
+    s : [0..6];
+    // 0 idle
+    // 1 next_frame 
+    // 2 wait_ack
+    // 3 retransmit
+    // 4 success
+    // 5 error
+    // 6 wait sync
+    srep : [0..3];
+    // 0 bottom
+    // 1 not ok (nok)
+    // 2 do not know (dk)
+    // 3 ok (ok)
+    nrtr : [0..MAX];
+    i : [0..N];
+    bs : bool;
+    s_ab : bool;
+    fs : bool;
+    ls : bool;
+    
+    // idle
+    [NewFile] (s=0) -> (s'=1) & (i'=1) & (srep'=0);
+    // next_frame
+    [aF] (s=1) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=0);
+    // wait_ack
+    [aB] (s=2) -> (s'=4) & (s_ab'=!s_ab);
+    [TO_Msg] (s=2) -> (s'=3);
+    [TO_Ack] (s=2) -> (s'=3);
+    // retransmit
+    [aF] (s=3) & (nrtr<MAX) -> (s'=2) & (fs'=(i=1)) & (ls'=(i=N)) & (bs'=s_ab) & (nrtr'=nrtr+1);
+    [] (s=3) & (nrtr=MAX) & (i<N) -> (s'=5) & (srep'=1);
+    [] (s=3) & (nrtr=MAX) & (i=N) -> (s'=5) & (srep'=2);
+    // success
+    [] (s=4) & (i<N) -> (s'=1) & (i'=i+1);
+    [] (s=4) & (i=N) -> (s'=0) & (srep'=3);
+    // error
+    [SyncWait] (s=5) -> (s'=6); 
+    // wait sync
+    [SyncWait] (s=6) -> (s'=0) & (s_ab'=false); 
+    
+endmodule
+
+module receiver
+
+    r : [0..5];
+    // 0 new_file
+    // 1 fst_safe
+    // 2 frame_received
+    // 3 frame_reported
+    // 4 idle
+    // 5 resync
+    rrep : [0..4];
+    // 0 bottom
+    // 1 fst
+    // 2 inc
+    // 3 ok
+    // 4 nok
+    fr : bool;
+    lr : bool;
+    br : bool;
+    r_ab : bool;
+    recv : bool;
+    
+    
+    // new_file
+    [SyncWait] (r=0) -> (r'=0);
+    [aG] (r=0) -> (r'=1) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
+    // fst_safe_frame
+    [] (r=1) -> (r'=2) & (r_ab'=br);
+    // frame_received
+    [] (r=2) & (r_ab=br) & (fr=true) & (lr=false)  -> (r'=3) & (rrep'=1);
+    [] (r=2) & (r_ab=br) & (fr=false) & (lr=false) -> (r'=3) & (rrep'=2);
+    [] (r=2) & (r_ab=br) & (fr=false) & (lr=true)  -> (r'=3) & (rrep'=3);
+    [aA] (r=2) & !(r_ab=br) -> (r'=4);  
+    // frame_reported
+    [aA] (r=3) -> (r'=4) & (r_ab'=!r_ab);
+    // idle
+    [aG] (r=4) -> (r'=2) & (fr'=fs) & (lr'=ls) & (br'=bs) & (recv'=T);
+    [SyncWait] (r=4) & (ls=true) -> (r'=5);
+    [SyncWait] (r=4) & (ls=false) -> (r'=5) & (rrep'=4);
+    // resync
+    [SyncWait] (r=5) -> (r'=0) & (rrep'=0);
+    
+endmodule
+
+// prevents more than one file being sent
+module tester 
+
+    T : bool;
+    
+    [NewFile] (T=false) -> (T'=true);
+    
+endmodule
+
+module  channelK
+
+    k : [0..2];
+    
+    // idle
+    [aF] (k=0) -> 1-pK : (k'=1) + pK : (k'=2);
+    // sending
+    [aG] (k=1) -> (k'=0);
+    // lost
+    [TO_Msg] (k=2) -> (k'=0);
+    
+endmodule
+
+module  channelL
+
+    l : [0..2];
+    
+    // idle
+    [aA] (l=0) -> 1-pL : (l'=1) + pL : (l'=2);
+    // sending
+    [aB] (l=1) -> (l'=0);
+    // lost
+    [TO_Ack] (l=2) -> (l'=0);
+    
+endmodule
+
+label "error" = s=5;
+
+rewards
+	[TO_Msg] true : TOMsg;
+	[TO_Ack] true : TOAck;
+endrewards
+
+
diff --git a/resources/examples/testfiles/pdtmc/casestudy1.pm b/resources/examples/testfiles/pdtmc/casestudy1.pm
new file mode 100644
index 000000000..17d890838
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/casestudy1.pm
@@ -0,0 +1,34 @@
+dtmc
+
+const double p;
+
+module test
+
+	// local state
+	s : [0..4] init 0;
+	
+	[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
+	[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
+	[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
+	[] s=3 -> 1 : (s'=3);
+	[] s=4 -> 1 : (s'=4);
+	
+endmodule
+
+// Dot output:
+//digraph model {
+//	0 [ label = "0: {init}" ];
+//	1 [ label = "1: {}" ];
+//	2 [ label = "2: {}" ];
+//	3 [ label = "3: {}" ];
+//	4 [ label = "4: {}" ];
+//	0 -> 1 [ label= "(p)/(1)" ];
+//	0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	1 -> 3 [ label= "(p)/(1)" ];
+//	1 -> 4 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	2 -> 3 [ label= "(p)/(2)" ];
+//	2 -> 4 [ label= "(-1 * (p+(-2)))/(2)" ];
+//	3 -> 3 [ label= "1" ];
+//	4 -> 4 [ label= "1" ];
+//}
+
diff --git a/resources/examples/testfiles/pdtmc/casestudy2.pm b/resources/examples/testfiles/pdtmc/casestudy2.pm
new file mode 100644
index 000000000..9872270d7
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/casestudy2.pm
@@ -0,0 +1,39 @@
+dtmc
+
+const double p;
+
+module test
+
+	// local state
+	s : [0..5] init 0;
+	
+	[] s=0 -> 0.4*p : (s'=1) + (1-p) : (s'=2) + 0.6*p : (s'=3);
+	[] s=1 -> 0.5*p : (s'=4) + 0.5*p : (s'=3) + (1-p) : (s'=5);
+	[] s=2 -> 0.3*p : (s'=4) + (1-0.3*p) : (s'=5);
+	[] s=3 -> 0.7*p : (s'=4) + (1-0.7*p) : (s'=5);
+	[] s=4 -> 1 : (s'=4);
+	[] s=5 -> 1 : (s'=5);
+		
+endmodule
+
+// Dot output:
+//digraph model {
+//	0 [ label = "0: {init}" ];
+//	1 [ label = "1: {}" ];
+//	2 [ label = "2: {}" ];
+//	3 [ label = "3: {}" ];
+//	4 [ label = "4: {}" ];
+//	5 [ label = "5: {}" ];
+//	0 -> 1 [ label= "(2 * (p))/(5)" ];
+//	0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	0 -> 3 [ label= "(3 * (p))/(5)" ];
+//	1 -> 3 [ label= "(p)/(2)" ];
+//	1 -> 4 [ label= "(p)/(2)" ];
+//	1 -> 5 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	2 -> 4 [ label= "(3 * (p))/(10)" ];
+//	2 -> 5 [ label= "(-1 * (3*p+(-10)))/(10)" ];
+//	3 -> 4 [ label= "(7 * (p))/(10)" ];
+//	3 -> 5 [ label= "(-1 * (7*p+(-10)))/(10)" ];
+//	4 -> 4 [ label= "1" ];
+//	5 -> 5 [ label= "1" ];
+//}
\ No newline at end of file
diff --git a/resources/examples/testfiles/pdtmc/casestudy3.pm b/resources/examples/testfiles/pdtmc/casestudy3.pm
new file mode 100644
index 000000000..312784b10
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/casestudy3.pm
@@ -0,0 +1,33 @@
+dtmc
+
+const double p;
+
+module test
+
+	// local state
+	s : [0..4] init 0;
+	
+	[] s=0 -> p*(1-p) : (s'=1) + (1-p*(1-p)) : (s'=2);
+	[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
+	[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
+	[] s=3 -> 1 : (s'=3);
+	[] s=4 -> 1 : (s'=4);
+	
+endmodule
+
+// Dot output:
+//digraph model {
+//	0 [ label = "0: {init}" ];
+//	1 [ label = "1: {}" ];
+//	2 [ label = "2: {}" ];
+//	3 [ label = "3: {}" ];
+//	4 [ label = "4: {}" ];
+//	0 -> 1 [ label= "(-1 * ((p) * (p+(-1))))/(1)" ];
+//	0 -> 2 [ label= "(p^2+(-1)*p+1)/(1)" ];
+//	1 -> 3 [ label= "(p)/(1)" ];
+//	1 -> 4 [ label= "(1-p)/(1)" ];
+//	2 -> 3 [ label= "(1-(p)/(2))" ];
+//	2 -> 4 [ label= "(p)/(2)" ];
+//	3 -> 3 [ label= "1" ];
+//	4 -> 4 [ label= "1" ];
+//}
\ No newline at end of file
diff --git a/resources/examples/testfiles/pdtmc/parametric_die_2.pm b/resources/examples/testfiles/pdtmc/parametric_die_2.pm
new file mode 100644
index 000000000..25b5ec8ec
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/parametric_die_2.pm
@@ -0,0 +1,35 @@
+// Knuth's model of a fair die using only fair coins
+dtmc
+
+const double p;
+const double q;
+
+module die
+
+	// local state
+	s : [0..7] init 0;
+	// value of the dice
+	d : [0..6] init 0;
+	
+	[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
+	[] s=1 -> q : (s'=3) + (1-q) : (s'=4);
+	[] s=2 -> q : (s'=5) + (1-q) : (s'=6);
+	[] s=3 -> p : (s'=1) + (1-p) : (s'=7) & (d'=1);
+	[] s=4 -> p : (s'=7) & (d'=2) + (1-p) : (s'=7) & (d'=3);
+	[] s=5 -> p : (s'=7) & (d'=4) + (1-p) : (s'=7) & (d'=5);
+	[] s=6 -> p : (s'=2) + (1-p) : (s'=7) & (d'=6);
+	[] s=7 -> 1: (s'=7);
+	
+endmodule
+
+rewards "coin_flips"
+	[] s<7 : 1;
+endrewards
+
+label "one" = s=7&d=1;
+label "two" = s=7&d=2;
+label "three" = s=7&d=3;
+label "four" = s=7&d=4;
+label "five" = s=7&d=5;
+label "six" = s=7&d=6;
+label "done" = s=7;
diff --git a/resources/examples/testfiles/pdtmc/simple1.pm b/resources/examples/testfiles/pdtmc/simple1.pm
index 597da690d..a5a320f0a 100644
--- a/resources/examples/testfiles/pdtmc/simple1.pm
+++ b/resources/examples/testfiles/pdtmc/simple1.pm
@@ -15,3 +15,19 @@ module test
 	
 endmodule
 
+// Dot output:
+//digraph model {
+//	0 [ label = "0: {init}" ];
+//	1 [ label = "1: {}" ];
+//	2 [ label = "2: {}" ];
+//	3 [ label = "3: {}" ];
+//	4 [ label = "4: {}" ];
+//	0 -> 1 [ label= "(p)/(1)" ];
+//	0 -> 2 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	1 -> 3 [ label= "(p)/(1)" ];
+//	1 -> 4 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	2 -> 3 [ label= "(-1 * (p+(-1)))/(1)" ];
+//	2 -> 4 [ label= "(p)/(1)" ];
+//	3 -> 3 [ label= "1" ];
+//	4 -> 4 [ label= "1" ];
+//}
\ No newline at end of file
diff --git a/resources/examples/testfiles/pdtmc/simple3.pm b/resources/examples/testfiles/pdtmc/simple3.pm
deleted file mode 100644
index 18f515800..000000000
--- a/resources/examples/testfiles/pdtmc/simple3.pm
+++ /dev/null
@@ -1,18 +0,0 @@
-dtmc
-
-const double p;
-
-module test
-
-	// local state
-	s : [0..5] init 0;
-	
-	[] s=0 -> 0.4*p : (s'=1) + (1-p) : (s'=2) + 0.6*p : (s'=3);
-	[] s=1 -> 0.5*p : (s'=4) + 0.5*p : (s'=3) + (1-p) : (s'=5);
-	[] s=2 -> 0.3*p : (s'=4) + (1-0.3*p) : (s'=5);
-	[] s=3 -> 0.7*p : (s'=4) + (1-0.7*p) : (s'=5);
-	[] s=4 -> 1 : (s'=4);
-	[] s=5 -> 1 : (s'=5);
-		
-endmodule
-
diff --git a/resources/examples/testfiles/pdtmc/simple4.pm b/resources/examples/testfiles/pdtmc/simple4.pm
deleted file mode 100644
index 1bdc3e004..000000000
--- a/resources/examples/testfiles/pdtmc/simple4.pm
+++ /dev/null
@@ -1,17 +0,0 @@
-dtmc
-
-const double p;
-
-module test
-
-	// local state
-	s : [0..4] init 0;
-	
-	[] s=0 -> p*(1-p) : (s'=1) + (1-p*(1-p)) : (s'=2);
-	[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
-	[] s=2 -> (1-p) : (s'=3) + (p) : (s'=4);
-	[] s=3 -> 1 : (s'=3);
-	[] s=4 -> 1 : (s'=4);
-	
-endmodule
-
diff --git a/resources/examples/testfiles/pdtmc/simple2.pm b/resources/examples/testfiles/pdtmc/simple5.pm
similarity index 79%
rename from resources/examples/testfiles/pdtmc/simple2.pm
rename to resources/examples/testfiles/pdtmc/simple5.pm
index 33002c6ce..370300659 100644
--- a/resources/examples/testfiles/pdtmc/simple2.pm
+++ b/resources/examples/testfiles/pdtmc/simple5.pm
@@ -1,6 +1,7 @@
 dtmc
 
 const double p;
+const double q;
 
 module test
 
@@ -8,7 +9,7 @@ module test
 	s : [0..4] init 0;
 	
 	[] s=0 -> p : (s'=1) + (1-p) : (s'=2);
-	[] s=1 -> p : (s'=3) + (1-p) : (s'=4);
+	[] s=1 -> q : (s'=3) + (1-q) : (s'=4);
 	[] s=2 -> 0.5*p : (s'=3) + (1-0.5*p) : (s'=4);
 	[] s=3 -> 1 : (s'=3);
 	[] s=4 -> 1 : (s'=4);
diff --git a/resources/examples/testfiles/pdtmc/zeroconf4.pm b/resources/examples/testfiles/pdtmc/zeroconf4.pm
new file mode 100644
index 000000000..4245055de
--- /dev/null
+++ b/resources/examples/testfiles/pdtmc/zeroconf4.pm
@@ -0,0 +1,26 @@
+// Model taken from Daws04
+// This version by Ernst Moritz Hahn (emh@cs.uni-sb.de)
+
+dtmc
+
+const double pK;
+const double pL;
+const int n;
+
+module main
+  s: [-2..n+1];
+
+  [b] (s=-1) -> (s'=-2);
+  [a] (s=0) -> 1-pL : (s'=-1) + pL : (s'=1);
+  [a] (s>0) & (s<n+1) -> 1-pK : (s'=0) + pK : (s'=s+1);
+
+endmodule
+
+init
+  s = 0
+endinit
+
+rewards
+ [a] true : 1;
+ [b] true : n-1;
+endrewards
\ No newline at end of file
diff --git a/src/storm-pars-cli/storm-pars.cpp b/src/storm-pars-cli/storm-pars.cpp
index 65c68a6c9..5aa2d6af9 100644
--- a/src/storm-pars-cli/storm-pars.cpp
+++ b/src/storm-pars-cli/storm-pars.cpp
@@ -1,11 +1,11 @@
 
-#include "storm-pars/analysis/MonotonicityChecker.h"
 
 #include "storm-cli-utilities/cli.h"
 #include "storm-cli-utilities/model-handling.h"
 
 #include "storm-pars/api/storm-pars.h"
 #include "storm-pars/api/region.h"
+#include "storm-pars/analysis/MonotonicityHelper.h"
 
 #include "storm-pars/modelchecker/instantiation/SparseCtmcInstantiationModelChecker.h"
 #include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
@@ -81,8 +81,14 @@ namespace storm {
         std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::shared_ptr<storm::models::ModelBase> const& model) {
             std::vector<storm::storage::ParameterRegion<ValueType>> result;
             auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
+            boost::optional<int> splittingThreshold;
+            if (regionSettings.isSplittingThresholdSet()) {
+                splittingThreshold = regionSettings.getSplittingThreshold();
+            }
             if (regionSettings.isRegionSet()) {
-                result = storm::api::parseRegions<ValueType>(regionSettings.getRegionString(), *model);
+                result = storm::api::parseRegions<ValueType>(regionSettings.getRegionString(), *model, splittingThreshold);
+            } else if (regionSettings.isRegionBoundSet()) {
+                result = storm::api::createRegion<ValueType>(regionSettings.getRegionBoundString(), *model, splittingThreshold);
             }
             return result;
         }
@@ -157,13 +163,123 @@ namespace storm {
             return sampleInfo;
         }
 
+        template <typename ValueType>
+        std::shared_ptr<storm::models::ModelBase> eliminateScc(std::shared_ptr<storm::models::ModelBase> const& model) {
+                storm::utility::Stopwatch eliminationWatch(true);
+                std::shared_ptr<storm::models::ModelBase> result;
+                if (model->isOfType(storm::models::ModelType::Dtmc)) {
+                    STORM_PRINT("Applying scc elimination" << std::endl);
+                    auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
+                    auto matrix = sparseModel->getTransitionMatrix();
+                    auto backwardsTransitionMatrix = matrix.transpose();
+
+                    storm::storage::StronglyConnectedComponentDecompositionOptions const options;
+                    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
+
+                    storm::storage::BitVector selectedStates(matrix.getRowCount());
+                    storm::storage::BitVector selfLoopStates(matrix.getRowCount());
+                    for (size_t i = 0; i < decomposition.size(); ++i) {
+                        auto scc = decomposition.getBlock(i);
+                        if (scc.size() > 1) {
+                            auto statesScc = scc.getStates();
+                            std::vector<uint_fast64_t> entryStates;
+                            for (auto state : statesScc) {
+                                auto row = backwardsTransitionMatrix.getRow(state);
+                                bool found = false;
+                                for (auto backState : row) {
+                                    if (!scc.containsState(backState.getColumn())) {
+                                        found = true;
+                                    }
+                                }
+                                if (found) {
+                                    entryStates.push_back(state);
+                                    selfLoopStates.set(state);
+                                } else {
+                                    selectedStates.set(state);
+                                }
+                            }
+
+                            if (entryStates.size() != 1) {
+                                STORM_LOG_THROW(entryStates.size() > 1, storm::exceptions::NotImplementedException,
+                                                "state elimination not implemented for scc with more than 1 entry points");
+                            }
+                        }
+                    }
+
+                    storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(matrix);
+                    storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardsTransitionMatrix, true);
+                    auto actionRewards = std::vector<ValueType>(matrix.getRowCount(), storm::utility::zero<ValueType>());
+                    storm::solver::stateelimination::NondeterministicModelStateEliminator<ValueType> stateEliminator(flexibleMatrix, flexibleBackwardTransitions, actionRewards);
+                    for(auto state : selectedStates) {
+                        stateEliminator.eliminateState(state, true);
+                    }
+                    for (auto state : selfLoopStates) {
+                        auto row = flexibleMatrix.getRow(state);
+                        stateEliminator.eliminateLoop(state);
+                    }
+                    selectedStates.complement();
+                    auto keptRows = matrix.getRowFilter(selectedStates);
+                    storm::storage::SparseMatrix<ValueType> newTransitionMatrix = flexibleMatrix.createSparseMatrix(keptRows, selectedStates);
+                    // TODO @Jip: note that rewards get lost
+                    result = std::make_shared<storm::models::sparse::Dtmc<ValueType>>(std::move(newTransitionMatrix), sparseModel->getStateLabeling().getSubLabeling(selectedStates));
+
+                    eliminationWatch.stop();
+                    STORM_PRINT(std::endl << "Time for scc elimination: " << eliminationWatch << "." << std::endl << std::endl);
+                    result->printModelInformationToStream(std::cout);
+                } else if (model->isOfType(storm::models::ModelType::Mdp)) {
+                    STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unable to perform SCC elimination for monotonicity analysis on MDP: Not mplemented");
+                } else {
+                    STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
+                }
+                return result;
+        }
+
+        template <typename ValueType>
+        std::shared_ptr<storm::models::ModelBase> simplifyModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input) {
+            storm::utility::Stopwatch simplifyingWatch(true);
+            std::shared_ptr<storm::models::ModelBase> result;
+            if (model->isOfType(storm::models::ModelType::Dtmc)) {
+                storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<ValueType>> simplifier(*(model->template as<storm::models::sparse::Dtmc<ValueType>>()));
+
+                std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
+                STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
+
+                if (!simplifier.simplify(*(formulas[0]))) {
+                    STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
+                }
+                result = simplifier.getSimplifiedModel();
+            } else if (model->isOfType(storm::models::ModelType::Mdp)) {
+                storm::transformer::SparseParametricMdpSimplifier<storm::models::sparse::Mdp<ValueType>> simplifier(*(model->template as<storm::models::sparse::Mdp<ValueType>>()));
+
+                std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
+                STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
+
+                if (!simplifier.simplify(*(formulas[0]))) {
+                    STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
+                }
+                result = simplifier.getSimplifiedModel();
+            } else {
+                STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
+            }
+
+            simplifyingWatch.stop();
+            STORM_PRINT(std::endl << "Time for model simplification: " << simplifyingWatch << "." << std::endl << std::endl);
+            result->printModelInformationToStream(std::cout);
+            return result;
+        }
+
         template <typename ValueType>
         PreprocessResult preprocessSparseModel(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, storm::cli::ModelProcessingInformation const& mpi) {
             auto bisimulationSettings = storm::settings::getModule<storm::settings::modules::BisimulationSettings>();
             auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
             auto transformationSettings = storm::settings::getModule<storm::settings::modules::TransformationSettings>();
+            auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
 
             PreprocessResult result(model, false);
+            if (monSettings.isMonotonicityAnalysisSet() || parametricSettings.isUseMonotonicitySet()) {
+                result.model = storm::pars::simplifyModel<ValueType>(result.model, input);
+                result.changed = true;
+            }
             
             if (result.model->isOfType(storm::models::ModelType::MarkovAutomaton)) {
                 result.model = storm::cli::preprocessSparseMarkovAutomaton(result.model->template as<storm::models::sparse::MarkovAutomaton<ValueType>>());
@@ -195,6 +311,11 @@ namespace storm {
                 result.changed = true;
             }
 
+            if (monSettings.isSccEliminationSet()) {
+                result.model = storm::pars::eliminateScc<ValueType>(result.model);
+                result.changed = true;
+            }
+
             return result;
         }
 
@@ -424,36 +545,133 @@ namespace storm {
         }
 
         template <typename ValueType>
-        void computeRegionExtremumWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
+        void analyzeMonotonicity(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
+            std::ofstream outfile;
+            auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
+
+            if (monSettings.isExportMonotonicitySet()) {
+                utility::openFile(monSettings.getExportMonotonicityFilename(), outfile);
+            }
+            std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
+            storm::utility::Stopwatch monotonicityWatch(true);
+            STORM_LOG_THROW(regions.size() <= 1, storm::exceptions::InvalidArgumentException, "Monotonicity analysis only allowed on single region");
+            if (!monSettings.isMonSolutionSet()) {
+                auto monotonicityHelper = storm::analysis::MonotonicityHelper<ValueType, double>(model, formulas, regions, monSettings.getNumberOfSamples(), storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision(), monSettings.isDotOutputSet());
+                if (monSettings.isExportMonotonicitySet()) {
+                    monotonicityHelper.checkMonotonicityInBuild(outfile, monSettings.isUsePLABoundsSet(), monSettings.getDotOutputFilename());
+                } else {
+                    monotonicityHelper.checkMonotonicityInBuild(std::cout, monSettings.isUsePLABoundsSet(), monSettings.getDotOutputFilename());
+                }
+            } else {
+                // Checking monotonicity based on solution function
+
+                auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
+                auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
+                auto engine = regionSettings.getRegionCheckEngine();
+
+                std::function<std::unique_ptr<storm::modelchecker::CheckResult>(std::shared_ptr<storm::logic::Formula const> const& formula)> verificationCallback;
+                std::function<void(std::unique_ptr<storm::modelchecker::CheckResult> const&)> postprocessingCallback;
+
+                // Check the given set of regions with or without refinement
+                verificationCallback = [&] (std::shared_ptr<storm::logic::Formula const> const& formula) {
+                    std::unique_ptr<storm::modelchecker::CheckResult> result = storm::api::verifyWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true));
+                    return result;
+                };
+
+                for (auto & property : input.properties) {
+                    auto result = verificationCallback(property.getRawFormula())->asExplicitQuantitativeCheckResult<ValueType>().getValueVector();
+                    ValueType valuation;
+
+                    auto states= model->getInitialStates();
+                    for (auto state : states) {
+                        valuation += result[state];
+                    }
+
+                    storm::analysis::MonotonicityResult<storm::RationalFunctionVariable> monRes;
+                    for (auto & var : storm::models::sparse::getProbabilityParameters(*model)) {
+                        auto res = storm::analysis::MonotonicityChecker<ValueType>::checkDerivative(valuation.derivative(var), regions[0]);
+
+                        if (res.first && res.second) {
+                            monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Constant);
+                        } else if (res.first) {
+                            monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr);
+                        } else if (res.second) {
+                            monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Decr);
+                        } else {
+                            monRes.addMonotonicityResult(var, analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Not);
+                        }
+                    }
+                    if (monSettings.isExportMonotonicitySet()) {
+                        outfile << monRes.toString();
+                    } else {
+                        STORM_PRINT(monRes.toString());
+                    }
+                }
+            }
+
+            if (monSettings.isExportMonotonicitySet()) {
+                utility::closeFile(outfile);
+            }
+
+            monotonicityWatch.stop();
+            STORM_PRINT(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
+            return;
+        }
+
+        template <typename ValueType>
+        void computeRegionExtremumWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
             STORM_LOG_ASSERT(!regions.empty(), "Can not analyze an empty set of regions.");
             auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
+            auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
             auto engine = regionSettings.getRegionCheckEngine();
             storm::solver::OptimizationDirection direction = regionSettings.getExtremumDirection();
             ValueType precision = storm::utility::convertNumber<ValueType>(regionSettings.getExtremumValuePrecision());
+            bool generateSplitEstimates = regionSettings.isSplittingThresholdSet();
             for (auto const& property : input.properties) {
                 for (auto const& region : regions) {
-                    STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": " << *property.getRawFormula() << " within region " << region << "..." << std::endl);
+                    if (monotonicitySettings.useMonotonicity) {
+                        STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": "
+                                                                                     << *property.getRawFormula()
+                                                                                     << " within region " << region
+                                                                                     << " and using monotonicity ..." << std::endl);
+                    } else {
+                        STORM_PRINT_AND_LOG("Computing extremal value for property " << property.getName() << ": "
+                                                                                     << *property.getRawFormula()
+                                                                                     << " within region " << region
+                                                                                     << "..." << std::endl);
+                    }
                     storm::utility::Stopwatch watch(true);
-                    auto valueValuation = storm::api::computeExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision);
-                    watch.stop();
-                    std::stringstream valuationStr;
-                    bool first = true;
-                    for (auto const& v : valueValuation.second) {
-                        if (first) {
-                            first = false;
+                    // TODO: hier eventueel checkExtremalValue van maken
+                    if (regionSettings.isExtremumSuggestionSet()) {
+                        ValueType suggestion = storm::utility::convertNumber<ValueType>(regionSettings.getExtremumSuggestion());
+                        if (storm::api::checkExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision, suggestion, monotonicitySettings, generateSplitEstimates, monotoneParameters)) {
+                            STORM_PRINT_AND_LOG(suggestion << " is the extremum ");
                         } else {
-                            valuationStr << ", ";
+                            STORM_PRINT_AND_LOG(suggestion << " is NOT the extremum ");
                         }
-                        valuationStr << v.first << "=" << v.second;
+
+                    } else {
+                        auto valueValuation = storm::api::computeExtremalValue<ValueType>(model, storm::api::createTask<ValueType>(property.getRawFormula(), true), region, engine, direction, precision, monotonicitySettings, generateSplitEstimates, monotoneParameters);
+                        watch.stop();
+                        std::stringstream valuationStr;
+                        bool first = true;
+                        for (auto const& v : valueValuation.second) {
+                            if (first) {
+                                first = false;
+                            } else {
+                                valuationStr << ", ";
+                            }
+                            valuationStr << v.first << "=" << v.second;
+                        }
+                        STORM_PRINT_AND_LOG("Result at initial state: " << valueValuation.first << " ( approx. " << storm::utility::convertNumber<double>(valueValuation.first) << ") at [" << valuationStr.str() << "]." << std::endl)
+                        STORM_PRINT_AND_LOG("Time for model checking: " << watch << "." << std::endl);
                     }
-                    STORM_PRINT_AND_LOG("Result at initial state: " << valueValuation.first << " ( approx. " << storm::utility::convertNumber<double>(valueValuation.first) << ") at [" << valuationStr.str() << "]." << std::endl)
-                    STORM_PRINT_AND_LOG("Time for model checking: " << watch << "." << std::endl);
                 }
             }
         }
         
         template <typename ValueType>
-        void verifyRegionsWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions) {
+        void verifyRegionsWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), uint64_t monThresh = 0) {
             STORM_LOG_ASSERT(!regions.empty(), "Can not analyze an empty set of regions.");
 
             auto parametricSettings = storm::settings::getModule<storm::settings::modules::ParametricSettings>();
@@ -475,6 +693,9 @@ namespace storm {
             }
             auto engine = regionSettings.getRegionCheckEngine();
             STORM_PRINT_AND_LOG(" using " << engine);
+            if (monotonicitySettings.useMonotonicity) {
+                STORM_PRINT_AND_LOG(" with local monotonicity and");
+            }
 
             // Check the given set of regions with or without refinement
             if (regionSettings.isRefineSet()) {
@@ -486,7 +707,8 @@ namespace storm {
                     if (regionSettings.isDepthLimitSet()) {
                         optionalDepthLimit = regionSettings.getDepthLimit();
                     }
-                    std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> result = storm::api::checkAndRefineRegionWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true), regions.front(), engine, refinementThreshold, optionalDepthLimit, regionSettings.getHypothesis());
+                    // TODO @Jip: change allow model simplification when not using monotonicity, for benchmarking purposes simplification is moved forward.
+                    std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> result = storm::api::checkAndRefineRegionWithSparseEngine<ValueType>(model, storm::api::createTask<ValueType>(formula, true), regions.front(), engine, refinementThreshold, optionalDepthLimit, regionSettings.getHypothesis(), false, monotonicitySettings, monThresh);
                     return result;
                 };
             } else {
@@ -507,15 +729,21 @@ namespace storm {
         }
 
         template <typename ValueType>
-        void verifyWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples) {
+        void verifyWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none, uint64_t monThresh = 0) {
             if (regions.empty()) {
                 storm::pars::verifyPropertiesWithSparseEngine(model, input, samples);
             } else {
                 auto regionSettings = storm::settings::getModule<storm::settings::modules::RegionSettings>();
-                if (regionSettings.isExtremumSet()) {
-                    storm::pars::computeRegionExtremumWithSparseEngine(model, input, regions);
+                auto monSettings = storm::settings::getModule<storm::settings::modules::MonotonicitySettings>();
+                if (monSettings.isMonotonicityAnalysisSet()) {
+                    storm::pars::analyzeMonotonicity(model, input, regions);
+                } else if (regionSettings.isExtremumSet()) {
+                    storm::pars::computeRegionExtremumWithSparseEngine(model, input, regions, monotonicitySettings, monotoneParameters);
                 } else {
-                    storm::pars::verifyRegionsWithSparseEngine(model, input, regions);
+                    assert (monotoneParameters == boost::none);
+                    assert (!monotonicitySettings.useOnlyGlobalMonotonicity);
+                    assert (!monotonicitySettings.useBoundsFromPLA);
+                    storm::pars::verifyRegionsWithSparseEngine(model, input, regions, monotonicitySettings, monThresh);
                 }
             }
         }
@@ -554,10 +782,12 @@ namespace storm {
         }
 
         template <storm::dd::DdType DdType, typename ValueType>
-        void verifyParametricModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples) {
+        void verifyParametricModel(std::shared_ptr<storm::models::ModelBase> const& model, SymbolicInput const& input, std::vector<storm::storage::ParameterRegion<ValueType>> const& regions, SampleInformation<ValueType> const& samples, storm::api::MonotonicitySetting monotonicitySettings = storm::api::MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none, uint64_t monThresh = 0) {
             if (model->isSparseModel()) {
-                storm::pars::verifyWithSparseEngine<ValueType>(model->as<storm::models::sparse::Model<ValueType>>(), input, regions, samples);
+                storm::pars::verifyWithSparseEngine<ValueType>(model->as<storm::models::sparse::Model<ValueType>>(), input, regions, samples, monotonicitySettings, monotoneParameters, monThresh);
             } else {
+                assert (!monotonicitySettings.useMonotonicity);
+                assert (monotoneParameters == boost::none);
                 storm::pars::verifyWithDdEngine<DdType, ValueType>(model->as<storm::models::symbolic::Model<DdType, ValueType>>(), input, regions, samples);
             }
         }
@@ -582,40 +812,6 @@ namespace storm {
 
             STORM_LOG_THROW(model || input.properties.empty(), storm::exceptions::InvalidSettingsException, "No input model.");
 
-            if (monSettings.isMonotonicityAnalysisSet()) {
-                // Simplify the model
-                storm::utility::Stopwatch simplifyingWatch(true);
-                if (model->isOfType(storm::models::ModelType::Dtmc)) {
-                    auto consideredModel = (model->as<storm::models::sparse::Dtmc<ValueType>>());
-                    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<ValueType>>(*consideredModel);
-
-                    std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
-                    STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
-
-                    if (!simplifier.simplify(*(formulas[0]))) {
-                        STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
-                    }
-                    model = simplifier.getSimplifiedModel();
-                } else if (model->isOfType(storm::models::ModelType::Mdp)) {
-                    auto consideredModel = (model->as<storm::models::sparse::Mdp<ValueType>>());
-                    auto simplifier = storm::transformer::SparseParametricMdpSimplifier<storm::models::sparse::Mdp<ValueType>>(*consideredModel);
-
-                    std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
-                    STORM_LOG_THROW(formulas.begin()!=formulas.end(), storm::exceptions::NotSupportedException, "Only one formula at the time supported");
-
-                    if (!simplifier.simplify(*(formulas[0]))) {
-                        STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
-                    }
-                    model = simplifier.getSimplifiedModel();
-                } else {
-                    STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
-                }
-
-                simplifyingWatch.stop();
-                STORM_PRINT(std::endl << "Time for model simplification: " << simplifyingWatch << "." << std::endl << std::endl);
-                model->printModelInformationToStream(std::cout);
-            }
-
             if (model) {
                 auto preprocessingResult = storm::pars::preprocessModel<DdType, ValueType>(model, input, mpi);
                 if (preprocessingResult.changed) {
@@ -636,103 +832,13 @@ namespace storm {
                 }
             }
 
-
-            if (model && monSettings.isSccEliminationSet()) {
-                storm::utility::Stopwatch eliminationWatch(true);
-                if (model->isOfType(storm::models::ModelType::Dtmc)) {
-                    STORM_PRINT("Applying scc elimination" << std::endl);
-                    auto sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
-                    auto matrix = sparseModel->getTransitionMatrix();
-                    auto backwardsTransitionMatrix = matrix.transpose();
-
-                    storm::storage::StronglyConnectedComponentDecompositionOptions const options;
-                    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
-
-                    storm::storage::BitVector selectedStates(matrix.getRowCount());
-                    storm::storage::BitVector selfLoopStates(matrix.getRowCount());
-                    for (size_t i = 0; i < decomposition.size(); ++i) {
-                        auto scc = decomposition.getBlock(i);
-                        if (scc.size() > 1) {
-                            auto statesScc = scc.getStates();
-                            std::vector<uint_fast64_t> entryStates;
-                            for (auto state : statesScc) {
-                                auto row = backwardsTransitionMatrix.getRow(state);
-                                bool found = false;
-                                for (auto backState : row) {
-                                    if (!scc.containsState(backState.getColumn())) {
-                                        found = true;
-                                    }
-                                }
-                                if (found) {
-                                    entryStates.push_back(state);
-                                    selfLoopStates.set(state);
-                                } else {
-                                    selectedStates.set(state);
-                                }
-                            }
-
-                            if (entryStates.size() != 1) {
-                                STORM_LOG_THROW(entryStates.size() > 1, storm::exceptions::NotImplementedException,
-                                                "state elimination not implemented for scc with more than 1 entry points");
-                            }
-                        }
-                    }
-
-                    storm::storage::FlexibleSparseMatrix<ValueType> flexibleMatrix(matrix);
-                    storm::storage::FlexibleSparseMatrix<ValueType> flexibleBackwardTransitions(backwardsTransitionMatrix, true);
-                    auto actionRewards = std::vector<ValueType>(matrix.getRowCount(), storm::utility::zero<ValueType>());
-                    storm::solver::stateelimination::NondeterministicModelStateEliminator<ValueType> stateEliminator(flexibleMatrix, flexibleBackwardTransitions, actionRewards);
-                    for(auto state : selectedStates) {
-                        stateEliminator.eliminateState(state, true);
-                    }
-                    for (auto state : selfLoopStates) {
-                        auto row = flexibleMatrix.getRow(state);
-                        stateEliminator.eliminateLoop(state);
-                    }
-                    selectedStates.complement();
-                    auto keptRows = matrix.getRowFilter(selectedStates);
-                    storm::storage::SparseMatrix<ValueType> newTransitionMatrix = flexibleMatrix.createSparseMatrix(keptRows, selectedStates);
-                    // TODO: note that rewards get lost
-                    model = std::make_shared<storm::models::sparse::Dtmc<ValueType>>(std::move(newTransitionMatrix), sparseModel->getStateLabeling().getSubLabeling(selectedStates));
-
-                    eliminationWatch.stop();
-                    STORM_PRINT(std::endl << "Time for scc elimination: " << eliminationWatch << "." << std::endl << std::endl);
-                    model->printModelInformationToStream(std::cout);
-                } else if (model->isOfType(storm::models::ModelType::Mdp)) {
-                   STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Unable to perform SCC elimination for monotonicity analysis on MDP: Not mplemented");
-                } else {
-                    STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform monotonicity analysis on the provided model type.");
-                }
-
-            }
-
             std::vector<storm::storage::ParameterRegion<ValueType>> regions = parseRegions<ValueType>(model);
 
-            if (monSettings.isMonotonicityAnalysisSet()) {
-                std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::api::extractFormulasFromProperties(input.properties);
-                // Monotonicity
-                storm::utility::Stopwatch monotonicityWatch(true);
-
-                STORM_LOG_THROW(regions.size() <= 1, storm::exceptions::InvalidArgumentException, "Monotonicity analysis only allowed on single region");
-                storm::analysis::MonotonicityChecker<ValueType> monotonicityChecker = storm::analysis::MonotonicityChecker<ValueType>(model, formulas, regions, monSettings.isValidateAssumptionsSet(), monSettings.getNumberOfSamples(), monSettings.getMonotonicityAnalysisPrecision());
-                if (ioSettings.isExportMonotonicitySet()) {
-                    std::ofstream outfile;
-                    utility::openFile(ioSettings.getExportMonotonicityFilename(), outfile);
-                    monotonicityChecker.checkMonotonicity(outfile);
-                    utility::closeFile(outfile);
-                } else {
-                    monotonicityChecker.checkMonotonicity(std::cout);
-                }
-                monotonicityWatch.stop();
-                STORM_PRINT(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl
-                                    << std::endl);
-                return;
-            }
-
             std::string samplesAsString = parSettings.getSamples();
             SampleInformation<ValueType> samples;
             if (!samplesAsString.empty()) {
-                samples = parseSamples<ValueType>(model, samplesAsString, parSettings.isSamplesAreGraphPreservingSet());
+                samples = parseSamples<ValueType>(model, samplesAsString,
+                                                  parSettings.isSamplesAreGraphPreservingSet());
                 samples.exact = parSettings.isSampleExactSet();
             }
 
@@ -741,13 +847,24 @@ namespace storm {
             }
 
             if (parSettings.onlyObtainConstraints()) {
-                STORM_LOG_THROW(parSettings.exportResultToFile(), storm::exceptions::InvalidSettingsException, "When computing constraints, export path has to be specified.");
-                storm::api::exportParametricResultToFile<ValueType>(boost::none, storm::analysis::ConstraintCollector<ValueType>(*(model->as<storm::models::sparse::Model<ValueType>>())), parSettings.exportResultPath());
+                STORM_LOG_THROW(parSettings.exportResultToFile(), storm::exceptions::InvalidSettingsException,
+                                "When computing constraints, export path has to be specified.");
+                storm::api::exportParametricResultToFile<ValueType>(boost::none,
+                                                                    storm::analysis::ConstraintCollector<ValueType>(
+                                                                            *(model->as<storm::models::sparse::Model<ValueType>>())),
+                                                                    parSettings.exportResultPath());
                 return;
             }
 
             if (model) {
-                verifyParametricModel<DdType, ValueType>(model, input, regions, samples);
+                boost::optional<std::pair<std::set<storm::RationalFunctionVariable>, std::set<storm::RationalFunctionVariable>>> monotoneParameters;
+                if (monSettings.isMonotoneParametersSet()) {
+                    monotoneParameters = std::move(
+                            storm::api::parseMonotoneParameters<ValueType>(monSettings.getMonotoneParameterFilename(),
+                                    model->as<storm::models::sparse::Model<ValueType>>()));
+                }
+// TODO: is onlyGlobalSet was used here
+                verifyParametricModel<DdType, ValueType>(model, input, regions, samples, storm::api::MonotonicitySetting(parSettings.isUseMonotonicitySet(), false, monSettings.isUsePLABoundsSet()), monotoneParameters, monSettings.getMonotonicityThreshold());
             }
         }
 
diff --git a/src/storm-pars/analysis/AssumptionChecker.cpp b/src/storm-pars/analysis/AssumptionChecker.cpp
index fce4596ab..e83c323df 100644
--- a/src/storm-pars/analysis/AssumptionChecker.cpp
+++ b/src/storm-pars/analysis/AssumptionChecker.cpp
@@ -2,33 +2,30 @@
 #include "AssumptionChecker.h"
 
 #include "storm-pars/utility/ModelInstantiator.h"
-#include "storm-pars/analysis/MonotonicityChecker.h"
-
-#include "storm/environment/Environment.h"
 #include "storm/exceptions/NotSupportedException.h"
 #include "storm/modelchecker/CheckTask.h"
 #include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
-#include "storm/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
 #include "storm/modelchecker/results/CheckResult.h"
 #include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
 #include "storm/storage/expressions/SimpleValuation.h"
 #include "storm/storage/expressions/ExpressionManager.h"
 #include "storm/storage/expressions/VariableExpression.h"
 #include "storm/storage/expressions/RationalFunctionToExpression.h"
-#include "storm/utility/constants.h"
 
 namespace storm {
     namespace analysis {
-        template <typename ValueType>
-        AssumptionChecker<ValueType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storm::storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples) {
-            this->formula = formula;
-            this->matrix = model->getTransitionMatrix();
-            this->region = region;
+        template <typename ValueType, typename ConstantType>
+        AssumptionChecker<ValueType, ConstantType>::AssumptionChecker(storage::SparseMatrix<ValueType> matrix){
+            this->matrix = matrix;
+            useSamples = false;
+        }
 
+        template <typename ValueType, typename ConstantType>
+        void AssumptionChecker<ValueType, ConstantType>::initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples) {
             // Create sample points
-            auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<double>>(*model);
+            auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
             auto matrix = model->getTransitionMatrix();
-            std::set<typename utility::parametric::VariableType<ValueType>::type> variables =  models::sparse::getProbabilityParameters(*model);
+            std::set<VariableType> variables = models::sparse::getProbabilityParameters(*model);
 
             for (uint_fast64_t i = 0; i < numberOfSamples; ++i) {
                 auto valuation = utility::parametric::Valuation<ValueType>();
@@ -36,106 +33,77 @@ namespace storm {
                     auto lb = region.getLowerBoundary(var.name());
                     auto ub = region.getUpperBoundary(var.name());
                     // Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
-                    auto val =
-                            std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>
-                                    (var,utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1)));
+                    auto val = std::pair<VariableType, CoefficientType>(var, utility::convertNumber<CoefficientType>(lb + i * (ub - lb) / (numberOfSamples - 1)));
                     valuation.insert(val);
                 }
-                models::sparse::Dtmc<double> sampleModel = instantiator.instantiate(valuation);
-                auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<double>>(sampleModel);
+                models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
+                auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
                 std::unique_ptr<modelchecker::CheckResult> checkResult;
                 if (formula->isProbabilityOperatorFormula() &&
                     formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
-                    const modelchecker::CheckTask<logic::UntilFormula, double> checkTask = modelchecker::CheckTask<logic::UntilFormula, double>(
+                    const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::UntilFormula, ConstantType>(
                             (*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
                     checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
                 } else if (formula->isProbabilityOperatorFormula() &&
                            formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
-                    const modelchecker::CheckTask<logic::EventuallyFormula, double> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, double>(
+                    const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>(
                             (*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
                     checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
                 } else {
                     STORM_LOG_THROW(false, exceptions::NotSupportedException,
                                     "Expecting until or eventually formula");
                 }
-                auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
-                std::vector<double> values = quantitativeResult.getValueVector();
+                auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
+                std::vector<ConstantType> values = quantitativeResult.getValueVector();
                 samples.push_back(values);
             }
+            useSamples = true;
         }
 
-        template <typename ValueType>
-        AssumptionChecker<ValueType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
-            STORM_LOG_THROW(false, exceptions::NotImplementedException, "Assumption checking for mdps not yet implemented");
-            this->formula = formula;
-            this->matrix = model->getTransitionMatrix();
-
-            // Create sample points
-            auto instantiator = utility::ModelInstantiator<models::sparse::Mdp<ValueType>, models::sparse::Mdp<double>>(*model);
-            auto matrix = model->getTransitionMatrix();
-            std::set<typename utility::parametric::VariableType<ValueType>::type> variables =  models::sparse::getProbabilityParameters(*model);
-
-            for (auto i = 0; i < numberOfSamples; ++i) {
-                auto valuation = utility::parametric::Valuation<ValueType>();
-                for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
-                    auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type,
-                                        typename utility::parametric::CoefficientType<ValueType>::type>((*itr), utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(boost::lexical_cast<std::string>((i+1)/(double (numberOfSamples + 1)))));
-                    valuation.insert(val);
-                }
-                models::sparse::Mdp<double> sampleModel = instantiator.instantiate(valuation);
-                auto checker = modelchecker::SparseMdpPrctlModelChecker<models::sparse::Mdp<double>>(sampleModel);
-                std::unique_ptr<modelchecker::CheckResult> checkResult;
-                if (formula->isProbabilityOperatorFormula() &&
-                    formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
-                    const modelchecker::CheckTask<logic::UntilFormula, double> checkTask = modelchecker::CheckTask<logic::UntilFormula, double>(
-                            (*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
-                    checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
-                } else if (formula->isProbabilityOperatorFormula() &&
-                           formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
-                    const modelchecker::CheckTask<logic::EventuallyFormula, double> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, double>(
-                            (*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
-                    checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
-                } else {
-                    STORM_LOG_THROW(false, exceptions::NotSupportedException,
-                                    "Expecting until or eventually formula");
-                }
-                auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
-                std::vector<double> values = quantitativeResult.getValueVector();
-                samples.push_back(values);
-            }
+        template <typename ValueType, typename ConstantType>
+        void AssumptionChecker<ValueType, ConstantType>::setSampleValues(std::vector<std::vector<ConstantType>> samples) {
+            this->samples = samples;
+            useSamples = true;
         }
 
-        template <typename ValueType>
-        AssumptionStatus AssumptionChecker<ValueType>::checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) {
-            auto result = AssumptionStatus::UNKNOWN;
-            std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
-            assumption->gatherVariables(vars);
-            for (auto itr = samples.begin(); result == AssumptionStatus::UNKNOWN && itr != samples.end(); ++itr) {
-                std::shared_ptr<expressions::ExpressionManager const> manager = assumption->getManager().getSharedPointer();
-                auto valuation = expressions::SimpleValuation(manager);
-                auto values = (*itr);
-                for (auto var = vars.begin(); result == AssumptionStatus::UNKNOWN && var != vars.end(); ++var) {
-                    expressions::Variable par = *var;
-                    auto index = std::stoi(par.getName());
-                    valuation.setRationalValue(par, values[index]);
-                }
-                assert(assumption->hasBooleanType());
-                if (!assumption->evaluateAsBool(&valuation)) {
-                    result = AssumptionStatus::INVALID;
-                }
-            }
-            return result;
+        template <typename ValueType, typename ConstantType>
+        AssumptionChecker<ValueType, ConstantType>::AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
+            STORM_LOG_THROW(false, exceptions::NotSupportedException, "Assumption checking for mdps not yet implemented");
         }
 
-        template <typename ValueType>
-        AssumptionStatus AssumptionChecker<ValueType>::validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order) {
+        template <typename ValueType, typename ConstantType>
+        AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumption(uint_fast64_t val1, uint_fast64_t val2,std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValues) const {
             // First check if based on sample points the assumption can be discharged
-            auto result = checkOnSamples(assumption);
+            assert (val1 == std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName()));
+            assert (val2 == std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName()));
+            AssumptionStatus result = AssumptionStatus::UNKNOWN;
+            if (useSamples) {
+                result = checkOnSamples(assumption);
+            }
             assert (result != AssumptionStatus::VALID);
 
+            if (minValues.size() != 0) {
+                if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater) {
+                    if (minValues[val1] > maxValues[val2]) {
+                        return AssumptionStatus::VALID;
+                    } else  if (minValues[val1] == maxValues[val2] && minValues[val1] == maxValues[val1] && minValues[val2] == maxValues[val2]) {
+                        return AssumptionStatus::INVALID;
+                    }  else if (minValues[val2] > maxValues[val1]) {
+                        return AssumptionStatus::INVALID;
+                    }
+                } else {
+                    if (minValues[val1] == maxValues[val2] && minValues[val1] == maxValues[val1] && minValues[val2] == maxValues[val2]) {
+                        return AssumptionStatus::VALID;
+                    } else if (minValues[val1] > maxValues[val2]) {
+                        return AssumptionStatus::INVALID;
+                    }  else if (minValues[val2] > maxValues[val1]) {
+                        return AssumptionStatus::INVALID;
+                    }
+                }
+            }
+
             if (result == AssumptionStatus::UNKNOWN) {
-                // If result from sample checking was unknown, the assumption might hold, so we continue,
-                // otherwise we return INVALID
+                // If result from sample checking was unknown, the assumption might hold
                 std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
                 assumption->gatherVariables(vars);
 
@@ -145,141 +113,111 @@ namespace storm {
                                 expressions::BinaryRelationExpression::RelationType::Equal,
                                 exceptions::NotSupportedException,
                                 "Only Greater Or Equal assumptions supported");
-
-                // Row with successors of the first state
-                auto row1 = matrix.getRow(
-                        std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName()));
-                // Row with successors of the second state
-                auto row2 = matrix.getRow(
-                        std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName()));
-
-                if (row1.getNumberOfEntries() == 2 && row2.getNumberOfEntries() == 2) {
-                    // If the states have the same successors for which we know the position in the order
-                    // We can check with a function if the assumption holds
-
-                    auto state1succ1 = row1.begin();
-                    auto state1succ2 = (++row1.begin());
-                    auto state2succ1 = row2.begin();
-                    auto state2succ2 = (++row2.begin());
-
-                    if (state1succ1->getColumn() == state2succ2->getColumn()
-                        && state2succ1->getColumn() == state1succ2->getColumn()) {
-                        std::swap(state1succ1, state1succ2);
-                    }
-
-                    if (state1succ1->getColumn() == state2succ1->getColumn() && state1succ2->getColumn() == state2succ2->getColumn()) {
-                        if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater
-                            && order->compare(state1succ1->getColumn(), state1succ2->getColumn()) != Order::NodeComparison::UNKNOWN) {
-                            // The assumption should be the greater assumption
-                            // If the result is unknown, we cannot compare, also SMTSolver will not help
-                            result = validateAssumptionSMTSolver(assumption, order);
-
-//                            result = validateAssumptionFunction(order, state1succ1, state1succ2, state2succ1,
-//                                                                state2succ2);
-                        } else if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Equal) {
-                            // The assumption is equal, the successors are the same,
-                            // so if the probability of reaching the successors is the same, we have a valid assumption
-                            if (state1succ1->getValue() == state2succ1->getValue()) {
-                                result = AssumptionStatus::VALID;
-                            }
-                        } else {
-                            result = AssumptionStatus::UNKNOWN;
-                        }
-                    } else {
-                        result = validateAssumptionSMTSolver(assumption, order);
-                    }
-                } else {
-                    result = validateAssumptionSMTSolver(assumption, order);
-                }
+                result = validateAssumptionSMTSolver(val1, val2, assumption, order, region, minValues, maxValues);
             }
             return result;
         }
 
-        template <typename ValueType>
-        AssumptionStatus AssumptionChecker<ValueType>::validateAssumptionFunction(Order* order,
-                typename storage::SparseMatrix<ValueType>::iterator state1succ1,
-                typename storage::SparseMatrix<ValueType>::iterator state1succ2,
-                typename storage::SparseMatrix<ValueType>::iterator state2succ1,
-                typename storage::SparseMatrix<ValueType>::iterator state2succ2) {
-            assert((state1succ1->getColumn() == state2succ1->getColumn()
-                && state1succ2->getColumn() == state2succ2->getColumn())
-                || (state1succ1->getColumn() == state2succ2->getColumn()
-                && state1succ2->getColumn() == state2succ1->getColumn()));
-
-            AssumptionStatus result;
-
-            // Calculate the difference in probability for the "highest" successor state
-            ValueType prob;
-            auto comp = order->compare(state1succ1->getColumn(), state1succ2->getColumn());
-            assert (comp == Order::NodeComparison::ABOVE || comp == Order::NodeComparison::BELOW);
-            if (comp == Order::NodeComparison::ABOVE) {
-                prob = state1succ1->getValue() - state2succ1->getValue();
-            } else if (comp == Order::NodeComparison::BELOW) {
-                prob = state1succ2->getValue() - state2succ2->getValue();
-            }
-
-            auto vars = prob.gatherVariables();
-
-            // If the result in monotone increasing (decreasing), then choose 0 (1) for the substitutions
-            // This will give the smallest result
-            std::map<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type> substitutions;
-            for (auto var : vars) {
-                auto monotonicity = MonotonicityChecker<ValueType>::checkDerivative(prob.derivative(var), region);
-                if (monotonicity.first) {
-                    // monotone increasing
-                    substitutions[var] = 0;
-                } else if (monotonicity.second) {
-                    // monotone increasing
-                    substitutions[var] = 1;
-                } else {
-                    result = AssumptionStatus::UNKNOWN;
+        template <typename ValueType, typename ConstantType>
+        AssumptionStatus AssumptionChecker<ValueType, ConstantType>::checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) const {
+            auto result = AssumptionStatus::UNKNOWN;
+            std::set<expressions::Variable> vars = std::set<expressions::Variable>({});
+            assumption->gatherVariables(vars);
+            for (auto values : samples) {
+                auto valuation = expressions::SimpleValuation(assumption->getManager().getSharedPointer());
+                for (auto var : vars) {
+                    auto index = std::stoi(var.getName());
+                    valuation.setRationalValue(var, utility::convertNumber<double>(values[index]));
                 }
-            }
 
-            if (prob.evaluate(substitutions) >= 0) {
-                result = AssumptionStatus::VALID;
+                assert (assumption->hasBooleanType());
+                if (!assumption->evaluateAsBool(&valuation)) {
+                    result = AssumptionStatus::INVALID;
+                    break;
+                }
             }
             return result;
         }
 
-
-        template <typename ValueType>
-        AssumptionStatus AssumptionChecker<ValueType>::validateAssumptionSMTSolver(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order) {
+        template <typename ValueType, typename ConstantType>
+        AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumptionSMTSolver(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValues) const {
             std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
             std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
-
-            AssumptionStatus result;
+            AssumptionStatus result = AssumptionStatus::UNKNOWN;
             auto var1 = assumption->getFirstOperand()->asVariableExpression().getVariableName();
             auto var2 = assumption->getSecondOperand()->asVariableExpression().getVariableName();
-            auto row1 = matrix.getRow(std::stoi(var1));
-            auto row2 = matrix.getRow(std::stoi(var2));
+            auto row1 = matrix.getRow(val1);
+            auto row2 = matrix.getRow(val2);
 
             bool orderKnown = true;
+            // if the state with number var1 (var2) occurs in the successors of the state with number var2 (var1) we need to add var1 == expr1 (var2 == expr2) to the bounds
+            bool addVar1 = false;
+            bool addVar2 = false;
             // Check if the order between the different successors is known
             // Also start creating expression for order of states
-            expressions::Expression exprOrderSucc = manager->boolean(true);
+            auto exprOrderSucc = manager->boolean(true);
             std::set<expressions::Variable> stateVariables;
+            std::set<expressions::Variable> topVariables;
+            std::set<expressions::Variable> bottomVariables;
             for (auto itr1 = row1.begin(); orderKnown && itr1 != row1.end(); ++itr1) {
+                addVar2 |= std::to_string(itr1->getColumn()) == var2;
                 auto varname1 = "s" + std::to_string(itr1->getColumn());
                 if (!manager->hasVariable(varname1)) {
-                    stateVariables.insert(manager->declareRationalVariable(varname1));
+                    if (order->isTopState(itr1->getColumn())) {
+                        topVariables.insert(manager->declareRationalVariable(varname1));
+                    } else if (order->isBottomState(itr1->getColumn())) {
+                        bottomVariables.insert(manager->declareRationalVariable(varname1));
+                    } else {
+                        stateVariables.insert(manager->declareRationalVariable(varname1));
+                    }
                 }
+
                 for (auto itr2 = row2.begin(); orderKnown && itr2 != row2.end(); ++itr2) {
+                    addVar1 |= std::to_string(itr2->getColumn()) == var1;
                     if (itr1->getColumn() != itr2->getColumn()) {
                         auto varname2 = "s" + std::to_string(itr2->getColumn());
                         if (!manager->hasVariable(varname2)) {
-                            stateVariables.insert(manager->declareRationalVariable(varname2));
+                            if (order->isTopState(itr2->getColumn())) {
+                                topVariables.insert(manager->declareRationalVariable(varname2));
+                            } else if (order->isBottomState(itr2->getColumn())) {
+                                bottomVariables.insert(manager->declareRationalVariable(varname2));
+                            } else {
+                                stateVariables.insert(manager->declareRationalVariable(varname2));
+                            }
                         }
                         auto comp = order->compare(itr1->getColumn(), itr2->getColumn());
+                        if (minValues.size() > 0 && comp == Order::NodeComparison::UNKNOWN) {
+                            // Couldn't add relation between varname1 and varname2 but maybe we can based on min/max values;
+                            if (minValues[itr2->getColumn()] > maxValues[itr1->getColumn()]) {
+                                if (!order->contains(itr1->getColumn())) {
+                                    order->add(itr1->getColumn());
+                                    order->addStateToHandle(itr1->getColumn());
+                                }
+                                if (!order->contains(itr2->getColumn())) {
+                                    order->add(itr2->getColumn());
+                                    order->addStateToHandle(itr2->getColumn());
+                                }
+                                order->addRelation(itr2->getColumn(), itr1->getColumn());
+                                comp = Order::NodeComparison::BELOW;
+                            } else if (minValues[itr1->getColumn()] > maxValues[itr2->getColumn()]) {
+                                if (!order->contains(itr1->getColumn())) {
+                                    order->add(itr1->getColumn());
+                                    order->addStateToHandle(itr1->getColumn());
+                                }
+                                if (!order->contains(itr2->getColumn())) {
+                                    order->add(itr2->getColumn());
+                                    order->addStateToHandle(itr2->getColumn());
+                                }
+                                order->addRelation(itr1->getColumn(), itr2->getColumn());
+                                comp = Order::NodeComparison::ABOVE;
+                            }
+                        }
                         if (comp == Order::NodeComparison::ABOVE) {
-                            exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) <=
-                                                              manager->getVariable(varname2));
+                            exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) <= manager->getVariable(varname2));
                         } else if (comp == Order::NodeComparison::BELOW) {
-                            exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) >=
-                                                              manager->getVariable(varname2));
+                            exprOrderSucc = exprOrderSucc && !(manager->getVariable(varname1) >= manager->getVariable(varname2));
                         } else if (comp == Order::NodeComparison::SAME) {
-                            exprOrderSucc = exprOrderSucc &&
-                                            (manager->getVariable(varname1) = manager->getVariable(varname2));
+                            exprOrderSucc = exprOrderSucc && (manager->getVariable(varname1) >= manager->getVariable(varname2)) && (manager->getVariable(varname1) <= manager->getVariable(varname2));
                         } else {
                             orderKnown = false;
                         }
@@ -292,63 +230,96 @@ namespace storm {
                 auto valueTypeToExpression = expressions::RationalFunctionToExpression<ValueType>(manager);
                 expressions::Expression expr1 = manager->rational(0);
                 for (auto itr1 = row1.begin(); itr1 != row1.end(); ++itr1) {
-                    expr1 = expr1 + (valueTypeToExpression.toExpression(itr1->getValue()) * manager->getVariable("s" + std::to_string(itr1->getColumn())));
+                    expr1 = expr1 + (valueTypeToExpression.toExpression(itr1->getValue()) *
+                                     manager->getVariable("s" + std::to_string(itr1->getColumn())));
                 }
 
                 expressions::Expression expr2 = manager->rational(0);
                 for (auto itr2 = row2.begin(); itr2 != row2.end(); ++itr2) {
-                    expr2 = expr2 + (valueTypeToExpression.toExpression(itr2->getValue()) * manager->getVariable("s" + std::to_string(itr2->getColumn())));
+                    expr2 = expr2 + (valueTypeToExpression.toExpression(itr2->getValue()) *
+                                     manager->getVariable("s" + std::to_string(itr2->getColumn())));
                 }
 
                 // Create expression for the assumption based on the relation to successors
                 // It is the negation of actual assumption
-                expressions::Expression exprToCheck ;
-                if (assumption->getRelationType() ==
-                    expressions::BinaryRelationExpression::RelationType::Greater) {
+
+                expressions::Expression exprToCheck;
+                if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater) {
                     exprToCheck = expr1 <= expr2;
                 } else {
-                    assert (assumption->getRelationType() ==
-                        expressions::BinaryRelationExpression::RelationType::Equal);
-                    exprToCheck = expr1 != expr2 ;
+                    assert (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Equal);
+                    exprToCheck = expr1 != expr2;
                 }
 
                 auto variables = manager->getVariables();
                 // Bounds for the state probabilities and parameters
-                expressions::Expression exprBounds =  manager->boolean(true);
+                expressions::Expression exprBounds = manager->boolean(true);
+                if (addVar1) {
+                    exprBounds = exprBounds && (manager->getVariable("s" + var1) == expr1);
+                }
+                if (addVar2) {
+                    exprBounds = exprBounds && (manager->getVariable("s" + var2) == expr2);
+                }
                 for (auto var : variables) {
                     if (find(stateVariables.begin(), stateVariables.end(), var) != stateVariables.end()) {
                         // the var is a state
-                        exprBounds = exprBounds && manager->rational(0) <= var && var <= manager->rational(1);
+                        if (minValues.size() > 0) {
+                            std::string test = var.getName();
+                            auto val = std::stoi(test.substr(1,test.size()-1));
+                            exprBounds = exprBounds && manager->rational(minValues[val]) <= var &&
+                                         var <= manager->rational(maxValues[val]);
+                        } else {
+                            exprBounds = exprBounds && manager->rational(0) <= var &&
+                                         var <= manager->rational(1);
+                        }
+                    } else if (find(topVariables.begin(), topVariables.end(), var) != topVariables.end()) {
+                        // the var is =)
+                        exprBounds = exprBounds && var == manager->rational(1);
+                    } else if (find(bottomVariables.begin(), bottomVariables.end(), var) != bottomVariables.end()) {
+                        // the var is =(
+                        exprBounds = exprBounds && var == manager->rational(0);
                     } else {
                         // the var is a parameter
-                        auto lb = storm::utility::convertNumber<storm::RationalNumber>(region.getLowerBoundary(var.getName()));
-                        auto ub = storm::utility::convertNumber<storm::RationalNumber>(region.getUpperBoundary(var.getName()));
+                        auto lb = utility::convertNumber<RationalNumber>(region.getLowerBoundary(var.getName()));
+                        auto ub = utility::convertNumber<RationalNumber>(region.getUpperBoundary(var.getName()));
                         exprBounds = exprBounds && manager->rational(lb) < var && var < manager->rational(ub);
                     }
                 }
 
                 s.add(exprOrderSucc);
                 s.add(exprBounds);
+                s.setTimeout(100);
                 // assert that sorting of successors in the order and the bounds on the expression are at least satisfiable
-                assert (s.check() == solver::SmtSolver::CheckResult::Sat);
+                // when this is not the case, the order is invalid
+                // however, it could be that the sat solver didn't finish in time, in that case we just continue.
+                if (s.check() == solver::SmtSolver::CheckResult::Unsat) {
+                    return AssumptionStatus::INVALID;
+                }
+                assert (s.check() != solver::SmtSolver::CheckResult::Unsat);
+
                 s.add(exprToCheck);
                 auto smtRes = s.check();
                 if (smtRes == solver::SmtSolver::CheckResult::Unsat) {
                     // If there is no thing satisfying the negation we are safe.
                     result = AssumptionStatus::VALID;
                 } else if (smtRes == solver::SmtSolver::CheckResult::Sat) {
-                    assert (smtRes == solver::SmtSolver::CheckResult::Sat);
-                    result = AssumptionStatus::INVALID;
-                } else {
-                    result = AssumptionStatus::UNKNOWN;
+                        result = AssumptionStatus::INVALID;
                 }
-            } else {
-                result = AssumptionStatus::UNKNOWN;
             }
-
             return result;
         }
 
-        template class AssumptionChecker<RationalFunction>;
+        template<typename ValueType, typename ConstantType>
+        AssumptionStatus AssumptionChecker<ValueType, ConstantType>::validateAssumption(
+                std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order,
+                storage::ParameterRegion<ValueType> region) const {
+            auto var1 = std::stoi(assumption->getFirstOperand()->asVariableExpression().getVariableName());
+            auto var2 = std::stoi(assumption->getSecondOperand()->asVariableExpression().getVariableName());
+            std::vector<ConstantType> vals;
+            return validateAssumption(var1, var2, assumption, order, region, vals, vals);
+        }
+
+        template class AssumptionChecker<RationalFunction, double>;
+        template class AssumptionChecker<RationalFunction, RationalNumber>;
     }
 }
diff --git a/src/storm-pars/analysis/AssumptionChecker.h b/src/storm-pars/analysis/AssumptionChecker.h
index 802291291..73e0e708d 100644
--- a/src/storm-pars/analysis/AssumptionChecker.h
+++ b/src/storm-pars/analysis/AssumptionChecker.h
@@ -8,6 +8,8 @@
 #include "storm/storage/expressions/BinaryRelationExpression.h"
 #include "storm-pars/storage/ParameterRegion.h"
 #include "Order.h"
+#include "storm/storage/SparseMatrix.h"
+
 
 namespace storm {
     namespace analysis {
@@ -19,18 +21,19 @@ namespace storm {
             INVALID,
             UNKNOWN,
         };
-        template<typename ValueType>
+
+        template<typename ValueType, typename ConstantType>
         class AssumptionChecker {
         public:
+            typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
+            typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
 
             /*!
-             * Constructs an AssumptionChecker based on the number of samples, for the given formula and model.
+             * Constructs an AssumptionChecker.
              *
-             * @param formula The formula to check.
-             * @param model The dtmc model to check the formula on.
-             * @param numberOfSamples Number of sample points.
+             * @param matrix The matrix of the considered model.
              */
-            AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storm::storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
+            AssumptionChecker(storage::SparseMatrix<ValueType> matrix);
 
             /*!
              * Constructs an AssumptionChecker based on the number of samples, for the given formula and model.
@@ -39,51 +42,46 @@ namespace storm {
              * @param model The mdp model to check the formula on.
              * @param numberOfSamples Number of sample points.
              */
-            AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
+            AssumptionChecker(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t const numberOfSamples);
 
             /*!
-             * Checks if the assumption holds at the sample points of the AssumptionChecker.
+             * Initializes the given number of sample points for a given model, formula and region.
              *
-             * @param assumption The assumption to check.
-             * @return AssumptionStatus::UNKNOWN or AssumptionStatus::INVALID
+             * @param formula The formula to compute the samples for.
+             * @param model The considered model.
+             * @param region The region of the model's parameters.
+             * @param numberOfSamples Number of sample points.
              */
-            AssumptionStatus checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption);
+            void initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
 
             /*!
-             * Tries to validate an assumption based on the order and underlying transition matrix.
+             * Sets the sample values to the given vector and useSamples to true.
              *
-             * @param assumption The assumption to validate.
-             * @param order The order.
-             * @return AssumptionStatus::VALID, or AssumptionStatus::UNKNOWN, or AssumptionStatus::INVALID
+             * @param samples The new value for samples.
              */
-            AssumptionStatus validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order);
+            void setSampleValues(std::vector<std::vector<ConstantType>> samples);
 
             /*!
-             * Tries to validate an assumption based on the order, and SMT solving techniques
+             * Tries to validate an assumption based on the order and underlying transition matrix.
              *
              * @param assumption The assumption to validate.
              * @param order The order.
+             * @param region The region of the considered model.
              * @return AssumptionStatus::VALID, or AssumptionStatus::UNKNOWN, or AssumptionStatus::INVALID
              */
-            AssumptionStatus validateAssumptionSMTSolver(std::shared_ptr<expressions::BinaryRelationExpression> assumption, Order* order);
+            AssumptionStatus validateAssumption(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region,  std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
+            AssumptionStatus validateAssumption(std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const;
 
         private:
-            std::shared_ptr<logic::Formula const> formula;
+            bool useSamples;
 
-            storage::SparseMatrix<ValueType> matrix;
+            std::vector<std::vector<ConstantType>> samples;
 
-            std::vector<std::vector<double>> samples;
-
-            void createSamples();
-
-            AssumptionStatus validateAssumptionFunction(Order* order,
-                    typename storage::SparseMatrix<ValueType>::iterator state1succ1,
-                    typename storage::SparseMatrix<ValueType>::iterator state1succ2,
-                    typename storage::SparseMatrix<ValueType>::iterator state2succ1,
-                    typename storage::SparseMatrix<ValueType>::iterator state2succ2);
+            storage::SparseMatrix<ValueType> matrix;
 
-            storm::storage::ParameterRegion<ValueType> region;
+            AssumptionStatus validateAssumptionSMTSolver(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<expressions::BinaryRelationExpression> assumption, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType>const minValues, std::vector<ConstantType>const maxValue) const;
 
+            AssumptionStatus checkOnSamples(std::shared_ptr<expressions::BinaryRelationExpression> assumption) const;
         };
     }
 }
diff --git a/src/storm-pars/analysis/AssumptionMaker.cpp b/src/storm-pars/analysis/AssumptionMaker.cpp
index d889e3f13..479ebcdee 100644
--- a/src/storm-pars/analysis/AssumptionMaker.cpp
+++ b/src/storm-pars/analysis/AssumptionMaker.cpp
@@ -2,66 +2,86 @@
 
 namespace storm {
     namespace analysis {
-        typedef std::shared_ptr<expressions::BinaryRelationExpression> AssumptionType;
-        template<typename ValueType>
-        AssumptionMaker<ValueType>::AssumptionMaker(AssumptionChecker<ValueType>* assumptionChecker, uint_fast64_t numberOfStates, bool validate) {
-            this->numberOfStates = numberOfStates;
-            this->assumptionChecker = assumptionChecker;
-            this->validate = validate;
-            this->expressionManager = std::make_shared<expressions::ExpressionManager>(expressions::ExpressionManager());
+        template<typename ValueType, typename ConstantType>
+        AssumptionMaker<ValueType, ConstantType>::AssumptionMaker(storage::SparseMatrix<ValueType> matrix) : assumptionChecker(matrix){
+            numberOfStates = matrix.getColumnCount();
+            expressionManager = std::make_shared<expressions::ExpressionManager>(expressions::ExpressionManager());
             for (uint_fast64_t i = 0; i < this->numberOfStates; ++i) {
                 expressionManager->declareRationalVariable(std::to_string(i));
             }
         }
 
+        template <typename ValueType, typename ConstantType>
+        std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const {
+            auto vec1 = std::vector<ConstantType>();
+            auto vec2 = std::vector<ConstantType>();
+            return createAndCheckAssumptions(val1, val2, order, region, vec1, vec2);
+        }
 
-        template <typename ValueType>
-        std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType>::createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, Order* order) {
+        template <typename ValueType, typename ConstantType>
+        std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValues) const {
             std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> result;
-
-            expressions::Variable var1 = expressionManager->getVariable(std::to_string(val1));
-            expressions::Variable var2 = expressionManager->getVariable(std::to_string(val2));
-            std::shared_ptr<expressions::BinaryRelationExpression> assumption1
-                    = std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
-                                                                                                                                  var1.getExpression().getBaseExpressionPointer(), var2.getExpression().getBaseExpressionPointer(),
-                                                                                                                                  expressions::BinaryRelationExpression::RelationType::Greater));
-            AssumptionStatus result1;
-            AssumptionStatus result2;
-            AssumptionStatus result3;
-            if (validate) {
-                result1 = assumptionChecker->validateAssumption(assumption1, order);
-            } else {
-                result1 = AssumptionStatus::UNKNOWN;
+            STORM_LOG_INFO("Creating assumptions for " << val1 << " and " << val2);
+            assert (order->compare(val1, val2) == Order::UNKNOWN);
+            auto assumption = createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues);
+            if (assumption.second != AssumptionStatus::INVALID) {
+                result.insert(assumption);
+                if (assumption.second == AssumptionStatus::VALID) {
+                    assert (createAndCheckAssumption(val2, val1, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues).second != AssumptionStatus::VALID
+                            && createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues).second != AssumptionStatus::VALID);
+                    STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
+                    return result;
+                }
             }
-            result[assumption1] = result1;
-
-
-            std::shared_ptr<expressions::BinaryRelationExpression> assumption2
-                    = std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
-                                                                                                                                  var2.getExpression().getBaseExpressionPointer(), var1.getExpression().getBaseExpressionPointer(),
-                                                                                                                                  expressions::BinaryRelationExpression::RelationType::Greater));
-
-            if (validate) {
-                result2 = assumptionChecker->validateAssumption(assumption2, order);
-            } else {
-                result2 = AssumptionStatus::UNKNOWN;
+            assert (order->compare(val1, val2) == Order::UNKNOWN);
+            assumption = createAndCheckAssumption(val2, val1, expressions::BinaryRelationExpression::RelationType::Greater, order, region, minValues, maxValues);
+            if (assumption.second != AssumptionStatus::INVALID) {
+                if (assumption.second == AssumptionStatus::VALID) {
+                    result.clear();
+                    result.insert(assumption);
+                    assert (createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues).second != AssumptionStatus::VALID);
+                    STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
+                    return result;
+                }
+                result.insert(assumption);
             }
-            result[assumption2] = result2;
-
-            std::shared_ptr<expressions::BinaryRelationExpression> assumption3
-                    = std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
-                                                                                                                                  var2.getExpression().getBaseExpressionPointer(), var1.getExpression().getBaseExpressionPointer(),
-                                                                                                                                  expressions::BinaryRelationExpression::RelationType::Equal));
-            if (validate) {
-                result3 = assumptionChecker->validateAssumption(assumption3, order);
-            } else {
-                result3 = AssumptionStatus::UNKNOWN;
+                assert (order->compare(val1, val2) == Order::UNKNOWN);
+            assumption = createAndCheckAssumption(val1, val2, expressions::BinaryRelationExpression::RelationType::Equal, order, region, minValues, maxValues);
+            if (assumption.second != AssumptionStatus::INVALID) {
+                if (assumption.second == AssumptionStatus::VALID) {
+                    result.clear();
+                    result.insert(assumption);
+                    STORM_LOG_INFO("Assumption " << assumption.first << "is valid" << std::endl);
+                    return result;
+                }
+                result.insert(assumption);
             }
-            result[assumption3] = result3;
-
+                assert (order->compare(val1, val2) == Order::UNKNOWN);
+            STORM_LOG_INFO("None of the assumptions is valid, number of possible assumptions:  " << result.size() << std::endl);
             return result;
         }
 
-        template class AssumptionMaker<RationalFunction>;
+        template <typename ValueType, typename ConstantType>
+        void AssumptionMaker<ValueType, ConstantType>::initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples){
+            assumptionChecker.initializeCheckingOnSamples(formula, model, region, numberOfSamples);
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void AssumptionMaker<ValueType, ConstantType>::setSampleValues(std::vector<std::vector<ConstantType>>const & samples) {
+            assumptionChecker.setSampleValues(samples);
+        }
+
+        template <typename ValueType, typename ConstantType>
+        std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> AssumptionMaker<ValueType, ConstantType>::createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, expressions::BinaryRelationExpression::RelationType relationType, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region,  std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValues) const {
+            assert (val1 != val2);
+            expressions::Variable var1 = expressionManager->getVariable(std::to_string(val1));
+            expressions::Variable var2 = expressionManager->getVariable(std::to_string(val2));
+            auto assumption = std::make_shared<expressions::BinaryRelationExpression>(expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(), var1.getExpression().getBaseExpressionPointer(), var2.getExpression().getBaseExpressionPointer(), relationType));
+            AssumptionStatus validationResult = assumptionChecker.validateAssumption(val1, val2, assumption, order, region, minValues, maxValues);
+            return std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus>(assumption, validationResult);
+        }
+
+        template class AssumptionMaker<RationalFunction, double>;
+        template class AssumptionMaker<RationalFunction, RationalNumber>;
     }
 }
diff --git a/src/storm-pars/analysis/AssumptionMaker.h b/src/storm-pars/analysis/AssumptionMaker.h
index 41643fb5a..97fa6f66f 100644
--- a/src/storm-pars/analysis/AssumptionMaker.h
+++ b/src/storm-pars/analysis/AssumptionMaker.h
@@ -3,47 +3,65 @@
 
 #include "AssumptionChecker.h"
 #include "Order.h"
-#include "OrderExtender.h"
-#include "storm/storage/expressions/BinaryRelationExpression.h"
-#include "storm-pars/utility/ModelInstantiator.h"
 
+#include "storm/storage/expressions/BinaryRelationExpression.h"
+#include "storm/storage/expressions/ExpressionManager.h"
+#include "storm/storage/SparseMatrix.h"
 
 namespace storm {
     namespace analysis {
 
-        template<typename ValueType>
+        template<typename ValueType, typename ConstantType>
         class AssumptionMaker {
             typedef std::shared_ptr<expressions::BinaryRelationExpression> AssumptionType;
         public:
             /*!
-             * Constructs AssumptionMaker based on the order extender, the assumption checker and number of states of the mode
+             * Constructs AssumptionMaker based on the matrix of the model.
+             *
+             * @param matrix The matrix of the model.
+             */
+            AssumptionMaker(storage::SparseMatrix<ValueType> matrix);
+
+            /*!
+             * Creates assumptions, and checks them, only VALID and UNKNOWN assumptions are returned.
+             * If one assumption is VALID, this assumption will be returned as only assumption.
+             * Possible results: AssumptionStatus::VALID, AssumptionStatus::UNKNOWN.
              *
-             * @param orderExtender The OrderExtender which needs the assumptions made by the AssumptionMaker.
-             * @param checker The AssumptionChecker which checks the assumptions at sample points.
-             * @param numberOfStates The number of states of the model.
+             * @param val1 First state number.
+             * @param val2 Second state number.
+             * @param order The order on which the assumptions are checked.
+             * @param region The region for the parameters.
+             * @return Map with at most three assumptions, and the validation.
              */
-            AssumptionMaker(AssumptionChecker<ValueType>* checker, uint_fast64_t numberOfStates, bool validate);
+            std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) const;
+            std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumptions(uint_fast64_t val1, uint_fast64_t val2, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
 
             /*!
-             * Creates assumptions, and checks them if validate in constructor is true.
-             * Possible results: AssumptionStatus::VALID, AssumptionStatus::INVALID, AssumptionStatus::UNKNOWN
-             * If validate is false result is always AssumptionStatus::UNKNOWN
+             * Initializes the given number of sample points for a given model, formula and region.
              *
-             * @param val1 First state number
-             * @param val2 Second state number
-             * @param order The order on which the assumptions are checked
-             * @return Map with three assumptions, and the validation
+             * @param formula The formula to compute the samples for.
+             * @param model The considered model.
+             * @param region The region of the model's parameters.
+             * @param numberOfSamples Number of sample points.
              */
-            std::map<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, Order* order);
+            void initializeCheckingOnSamples(std::shared_ptr<logic::Formula const> formula, std::shared_ptr<models::sparse::Dtmc<ValueType>> model, storage::ParameterRegion<ValueType> region, uint_fast64_t numberOfSamples);
+
+            /*!
+             * Sets the sample values to the given vector.
+             *
+             * @param samples The new value for samples.
+             */
+            void setSampleValues(std::vector<std::vector<ConstantType>>const & samples);
 
         private:
-            AssumptionChecker<ValueType>* assumptionChecker;
+            std::pair<std::shared_ptr<expressions::BinaryRelationExpression>, AssumptionStatus> createAndCheckAssumption(uint_fast64_t val1, uint_fast64_t val2, expressions::BinaryRelationExpression::RelationType relationType, std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region, std::vector<ConstantType> const minValues, std::vector<ConstantType> const maxValue) const;
+
+            AssumptionChecker<ValueType, ConstantType> assumptionChecker;
 
             std::shared_ptr<expressions::ExpressionManager> expressionManager;
 
             uint_fast64_t numberOfStates;
 
-            bool validate;
         };
     }
 }
diff --git a/src/storm-pars/analysis/LocalMonotonicityResult.cpp b/src/storm-pars/analysis/LocalMonotonicityResult.cpp
new file mode 100644
index 000000000..8a5bb7f18
--- /dev/null
+++ b/src/storm-pars/analysis/LocalMonotonicityResult.cpp
@@ -0,0 +1,175 @@
+#include "LocalMonotonicityResult.h"
+
+namespace storm {
+    namespace analysis {
+
+        template <typename VariableType>
+        LocalMonotonicityResult<VariableType>::LocalMonotonicityResult(uint_fast64_t numberOfStates) {
+            stateMonRes = std::vector<std::shared_ptr<MonotonicityResult<VariableType>>>(numberOfStates, nullptr);
+            globalMonotonicityResult = std::make_shared<MonotonicityResult<VariableType>>();
+            statesMonotone = storm::storage::BitVector(numberOfStates, false);
+            dummyPointer = std::make_shared<MonotonicityResult<VariableType>>();
+            done = false;
+        }
+
+        template <typename VariableType>
+        typename LocalMonotonicityResult<VariableType>::Monotonicity LocalMonotonicityResult<VariableType>::getMonotonicity(uint_fast64_t state, VariableType var) const {
+            if (stateMonRes[state] == dummyPointer) {
+                return Monotonicity::Constant;
+            } else if (stateMonRes[state] != nullptr) {
+                auto res = stateMonRes[state]->getMonotonicity(var);
+                if (res == Monotonicity::Unknown && globalMonotonicityResult->isDoneForVar(var)) {
+                    return globalMonotonicityResult->getMonotonicity(var);
+                }
+                return res;
+            } else {
+                return globalMonotonicityResult->isDoneForVar(var) ? globalMonotonicityResult->getMonotonicity(var) : Monotonicity::Unknown;
+            }
+        }
+
+        template <typename VariableType>
+        std::shared_ptr<MonotonicityResult<VariableType>> LocalMonotonicityResult<VariableType>::getGlobalMonotonicityResult() const {
+            return globalMonotonicityResult;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setMonotonicity(uint_fast64_t state, VariableType var, typename LocalMonotonicityResult<VariableType>::Monotonicity mon) {
+            assert (stateMonRes[state] != dummyPointer);
+            if (stateMonRes[state] == nullptr) {
+                stateMonRes[state] = std::make_shared<MonotonicityResult<VariableType>>();
+            }
+            stateMonRes[state]->addMonotonicityResult(var, mon);
+            globalMonotonicityResult->updateMonotonicityResult(var, mon);
+            if (mon == Monotonicity::Unknown || mon == Monotonicity::Not) {
+                statesMonotone.set(state, false);
+            } else {
+                bool stateMonotone = stateMonRes[state]->isAllMonotonicity();
+                if (stateMonotone) {
+                    statesMonotone.set(state);
+                    done |= statesMonotone.full();
+                }
+                if (isDone()) {
+                    globalMonotonicityResult->setDone();
+                }
+            }
+        }
+
+        template <typename VariableType>
+        std::shared_ptr<LocalMonotonicityResult<VariableType>> LocalMonotonicityResult<VariableType>::copy() {
+            std::shared_ptr<LocalMonotonicityResult<VariableType>> copy = std::make_shared<LocalMonotonicityResult<VariableType>>(stateMonRes.size());
+            for (auto state = 0; state < stateMonRes.size(); state++) {
+                if (stateMonRes[state] != nullptr) {
+                    copy->setMonotonicityResult(state, stateMonRes[state]->copy());
+                }
+            }
+            copy->setGlobalMonotonicityResult(this->getGlobalMonotonicityResult()->copy());
+            copy->setStatesMonotone(statesMonotone);
+            return copy;
+        }
+
+        template <typename VariableType>
+        bool LocalMonotonicityResult<VariableType>::isDone() const {
+            return done;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setIndexMinimize(int i) {
+            assert (indexMinimize == -1);
+            this->indexMinimize = i;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setIndexMaximize(int i) {
+            this->indexMaximize = i;
+        }
+
+        template <typename VariableType>
+        int LocalMonotonicityResult<VariableType>::getIndexMinimize() const {
+            return indexMinimize;
+        }
+
+        template <typename VariableType>
+        int LocalMonotonicityResult<VariableType>::getIndexMaximize() const {
+            return indexMaximize;
+        }
+
+        template <typename VariableType>
+        bool LocalMonotonicityResult<VariableType>::isNoMonotonicity() const {
+            return statesMonotone.empty();
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setMonotonicityResult(uint_fast64_t state, std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
+            this->stateMonRes[state] = monRes;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setGlobalMonotonicityResult(std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
+            this->globalMonotonicityResult = monRes;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setStatesMonotone(storm::storage::BitVector statesMonotone) {
+            this->statesMonotone = statesMonotone;
+        }
+
+        template <typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setConstant(uint_fast64_t state) {
+            if (stateMonRes[state] == nullptr) {
+                stateMonRes[state] = dummyPointer;
+            }
+            this->statesMonotone.set(state);
+        }
+
+        template<typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setMonotoneIncreasing(VariableType var) {
+            globalMonotonicityResult->updateMonotonicityResult(var, Monotonicity::Incr);
+            globalMonotonicityResult->setDoneForVar(var);
+            setFixedParameters = true;
+        }
+
+        template<typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setMonotoneDecreasing(VariableType var) {
+            globalMonotonicityResult->updateMonotonicityResult(var, Monotonicity::Decr);
+            globalMonotonicityResult->setDoneForVar(var);
+            setFixedParameters = true;
+        }
+
+        template <typename VariableType>
+        std::string LocalMonotonicityResult<VariableType>::toString() const {
+            std::string result = "Local Monotonicity Result: \n";
+            for (auto i = 0; i < stateMonRes.size(); ++i) {
+                result += "state ";
+                result += std::to_string(i);
+                if (stateMonRes[i] != nullptr) {
+                    result += stateMonRes[i]->toString();
+                } else if (statesMonotone[i]) {
+                    result += "constant";
+                } else {
+                    result += "not analyzed";
+                }
+                result += "\n";
+            }
+            return result;
+        }
+
+        template<typename VariableType>
+        bool LocalMonotonicityResult<VariableType>::isFixedParametersSet() const {
+            return setFixedParameters;
+        }
+
+        template<typename VariableType>
+        void LocalMonotonicityResult<VariableType>::setDone(bool done) {
+            this->done = done;
+        }
+
+        template<typename VariableType>
+        std::shared_ptr<MonotonicityResult<VariableType>>
+        LocalMonotonicityResult<VariableType>::getMonotonicity(uint_fast64_t state) const {
+            return stateMonRes[state];
+        }
+
+
+        template class LocalMonotonicityResult<storm::RationalFunctionVariable>;
+    }
+}
diff --git a/src/storm-pars/analysis/LocalMonotonicityResult.h b/src/storm-pars/analysis/LocalMonotonicityResult.h
new file mode 100644
index 000000000..4caa8fba9
--- /dev/null
+++ b/src/storm-pars/analysis/LocalMonotonicityResult.h
@@ -0,0 +1,113 @@
+#ifndef STORM_LOCALMONOTONICITYRESULT_H
+#define STORM_LOCALMONOTONICITYRESULT_H
+
+#include <vector>
+#include "storm-pars/analysis/MonotonicityResult.h"
+#include "storm/adapters/RationalFunctionAdapter.h"
+#include "storm/storage/BitVector.h"
+
+namespace storm {
+    namespace analysis {
+        template <typename VariableType>
+        class LocalMonotonicityResult {
+
+        public:
+            typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+
+            /*!
+             * Constructs a new LocalMonotonicityResult object.
+             *
+             * @param numberOfStates The number of states in the considered model.
+             * @return The new object.
+             */
+            LocalMonotonicityResult(uint_fast64_t numberOfStates);
+
+            /*!
+             * Returns the local Monotonicity of a parameter at a given state.
+             *
+             * @param state The state.
+             * @param var The parameter.
+             * @return The local Monotonicity.
+             */
+            Monotonicity getMonotonicity(uint_fast64_t state, VariableType var) const;
+            std::shared_ptr<MonotonicityResult<VariableType>> getMonotonicity(uint_fast64_t state) const;
+
+            /*!
+             * Sets the local Monotonicity of a parameter at a given state.
+             *
+             * @param mon The Monotonicity.
+             * @param state The state.
+             * @param var The parameter.
+             */
+            void setMonotonicity(uint_fast64_t state, VariableType var, Monotonicity mon);
+
+            /*!
+             * Returns the Global MonotonicityResult object that corresponds to this object.
+             *
+             * @return The pointer to the global MonotonicityResult.
+             */
+            std::shared_ptr<MonotonicityResult<VariableType>> getGlobalMonotonicityResult() const;
+
+            /*!
+             * Constructs a new LocalMonotonicityResult object that is a copy of the current one.
+             *
+             * @return Pointer to the copy.
+             */
+            std::shared_ptr<LocalMonotonicityResult<VariableType>> copy();
+
+            /*!
+             * Checks if the LocalMonotonicity is done yet.
+             *
+             * @return true if done is set to true, false otherwise.
+             */
+            bool isDone() const;
+            void setDone(bool done = true);
+
+            bool isNoMonotonicity() const;
+
+            void setConstant(uint_fast64_t state);
+
+            void setIndexMinimize(int index);
+            void setIndexMaximize(int index);
+            int getIndexMinimize() const;
+            int getIndexMaximize() const;
+
+            /*!
+             * Constructs a string output of all variables and their corresponding Monotonicity
+             * @return Results so far
+             */
+            std::string toString() const;
+
+            void setMonotoneIncreasing(VariableType var);
+            void setMonotoneDecreasing(VariableType var);
+
+            bool isFixedParametersSet() const;
+
+
+
+
+        private:
+            std::vector<std::shared_ptr<MonotonicityResult<VariableType>>> stateMonRes;
+
+            std::shared_ptr<MonotonicityResult<VariableType>> globalMonotonicityResult;
+
+            void setMonotonicityResult(uint_fast64_t state, std::shared_ptr<MonotonicityResult<VariableType>> monRes);
+
+            void setGlobalMonotonicityResult(std::shared_ptr<MonotonicityResult<VariableType>> monRes);
+
+            void setStatesMonotone(storm::storage::BitVector statesMonotone);
+
+            storm::storage::BitVector statesMonotone;
+            bool done;
+
+            int indexMinimize = -1;
+            int indexMaximize = -1;
+            std::shared_ptr<MonotonicityResult<VariableType>> dummyPointer;
+
+            bool setFixedParameters = false;
+
+        };
+    }
+}
+
+#endif //STORM_LOCALMONOTONICITYRESULT_H
diff --git a/src/storm-pars/analysis/MonotonicityChecker.cpp b/src/storm-pars/analysis/MonotonicityChecker.cpp
index f8ba84c7e..61e125e68 100644
--- a/src/storm-pars/analysis/MonotonicityChecker.cpp
+++ b/src/storm-pars/analysis/MonotonicityChecker.cpp
@@ -1,690 +1,143 @@
 #include "MonotonicityChecker.h"
-#include "storm-pars/analysis/AssumptionMaker.h"
-#include "storm-pars/analysis/AssumptionChecker.h"
-#include "storm-pars/analysis/Order.h"
-#include "storm-pars/analysis/OrderExtender.h"
-
-#include "storm/exceptions/NotSupportedException.h"
-#include "storm/exceptions/UnexpectedException.h"
-#include "storm/exceptions/InvalidOperationException.h"
-
-#include "storm/utility/Stopwatch.h"
-#include "storm/models/ModelType.h"
-
-#include "storm/api/verification.h"
-#include "storm-pars/api/storm-pars.h"
-
-#include "storm/modelchecker/results/CheckResult.h"
-#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
-#include "storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h"
-
-
 
 namespace storm {
     namespace analysis {
+        /*** Constructor ***/
         template <typename ValueType>
-        MonotonicityChecker<ValueType>::MonotonicityChecker(std::shared_ptr<storm::models::ModelBase> model, std::vector<std::shared_ptr<storm::logic::Formula const>> formulas,  std::vector<storm::storage::ParameterRegion<ValueType>> regions, bool validate, uint_fast64_t numberOfSamples, double const& precision) {
-            assert (model != nullptr);
-            this->model = model;
-            this->formulas = formulas;
-            this->validate = validate;
-            this->precision = precision;
-            std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
-
-            if (regions.size() == 1) {
-                this->region = *(regions.begin());
-            } else {
-                assert (regions.size() == 0);
-                typename storm::storage::ParameterRegion<ValueType>::Valuation lowerBoundaries;
-                typename storm::storage::ParameterRegion<ValueType>::Valuation upperBoundaries;
-                std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> vars;
-                vars = storm::models::sparse::getProbabilityParameters(*sparseModel);
-                for (auto var : vars) {
-                    typename storm::storage::ParameterRegion<ValueType>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<ValueType>::CoefficientType>(0 + precision);
-                    typename storm::storage::ParameterRegion<ValueType>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<ValueType>::CoefficientType>(1 - precision);
-                    lowerBoundaries.insert(std::make_pair(var, lb));
-                    upperBoundaries.insert(std::make_pair(var, ub));
-                }
-                this->region =  storm::storage::ParameterRegion<ValueType>(std::move(lowerBoundaries), std::move(upperBoundaries));
-            }
-
-            if (numberOfSamples > 0) {
-                // sampling
-                if (model->isOfType(storm::models::ModelType::Dtmc)) {
-                    this->resultCheckOnSamples = std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(
-                            checkOnSamples(model->as<storm::models::sparse::Dtmc<ValueType>>(), numberOfSamples));
-                } else if (model->isOfType(storm::models::ModelType::Mdp)) {
-                    this->resultCheckOnSamples = std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(
-                            checkOnSamples(model->as<storm::models::sparse::Mdp<ValueType>>(), numberOfSamples));
-
-                }
-                checkSamples= true;
-            } else {
-                checkSamples= false;
-            }
-
-            this->extender = new storm::analysis::OrderExtender<ValueType>(sparseModel);
+        MonotonicityChecker<ValueType>::MonotonicityChecker(storage::SparseMatrix<ValueType> matrix) {
+            this->matrix = matrix;
         }
 
+        /*** Public methods ***/
         template <typename ValueType>
-        std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> MonotonicityChecker<ValueType>::checkMonotonicity(std::ostream& outfile) {
-            auto map = createOrder();
-            std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
-            auto matrix = sparseModel->getTransitionMatrix();
-            return checkMonotonicity(outfile, map, matrix);
-        }
-
-        template <typename ValueType>
-        std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> MonotonicityChecker<ValueType>::checkMonotonicity(std::ostream& outfile, std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> map, storm::storage::SparseMatrix<ValueType> matrix) {
-            storm::utility::Stopwatch monotonicityCheckWatch(true);
-            std::map<storm::analysis::Order *, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> result;
-
-
-            if (map.size() == 0) {
-                // Nothing is known
-                outfile << " No assumptions -";
-                STORM_PRINT("No valid assumptions, couldn't build a sufficient order");
-                if (resultCheckOnSamples.size() != 0) {
-                    STORM_PRINT("\n" << "Based results on samples");
-                } else {
-                    outfile << " ?";
+        typename MonotonicityChecker<ValueType>::Monotonicity MonotonicityChecker<ValueType>::checkLocalMonotonicity(std::shared_ptr<Order> const& order, uint_fast64_t state, VariableType const& var, storage::ParameterRegion<ValueType> const& region) {
+            // Create + fill Vector containing the Monotonicity of the transitions to the succs
+            auto row = matrix.getRow(state);
+            // Ignore if all entries are constant
+            bool ignore = true;
+
+            std::vector<uint_fast64_t> succs;
+            std::vector<Monotonicity> succsMonUnsorted;
+            std::vector<uint_fast64_t> statesIncr;
+            std::vector<uint_fast64_t>  statesDecr;
+            bool checkAllow = true;
+            for (auto entry : row) {
+                auto succState = entry.getColumn();
+                auto mon = checkTransitionMonRes(entry.getValue(), var, region);
+                succsMonUnsorted.push_back(mon);
+                succs.push_back(succState);
+                ignore &= entry.getValue().isConstant();
+                if (mon == Monotonicity::Incr) {
+                    statesIncr.push_back(succState);
+                } else if (mon == Monotonicity::Decr) {
+                    statesDecr.push_back(succState);
+                } else if (mon == Monotonicity::Not) {
+                    checkAllow = false;
                 }
-
-                for (auto entry : resultCheckOnSamples) {
-                    if (!entry.second.first && ! entry.second.second) {
-                        outfile << " SX " << entry.first << " ";
-                    } else if (entry.second.first && ! entry.second.second) {
-                        outfile << " SI " << entry.first << " ";
-                    } else if (entry.second.first && entry.second.second) {
-                        outfile << " SC " << entry.first << " ";
-                    } else {
-                        outfile << " SD " << entry.first << " ";
-                    }
-                }
-
-            } else {
-                size_t i = 0;
-                for (auto itr = map.begin(); i < map.size() && itr != map.end(); ++itr) {
-                    auto order = itr->first;
-
-                    auto addedStates = order->getAddedStates()->getNumberOfSetBits();
-                    assert (addedStates == order->getAddedStates()->size());
-                    std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone = analyseMonotonicity(i, order,
-                                                                                                      matrix);
-
-                    auto assumptions = itr->second;
-                    if (assumptions.size() > 0) {
-                        bool first = true;
-                        for (auto itr2 = assumptions.begin(); itr2 != assumptions.end(); ++itr2) {
-                            if (!first) {
-                                outfile << (" ^ ");
-                            } else {
-                                first = false;
-                            }
-                            outfile << (*(*itr2));
+            }
+            if (ignore) {
+                return Monotonicity::Constant;
+            }
+            auto succsSorted = order->sortStates(&succs);
+
+            uint_fast64_t succSize = succs.size();
+            if (succsSorted[succSize - 1] == matrix.getColumnCount()) {
+                // Maybe we can still do something
+                // If one is decreasing and all others increasing, and this one is above all others or vice versa
+                if (checkAllow) {
+                    if (statesIncr.size() == 1 && statesDecr.size() > 1) {
+                        auto comp = order->allAboveBelow(statesDecr, statesIncr.back());
+                        if (comp.first) {
+                            // All decreasing states are above the increasing state, therefore decreasing
+                            return Monotonicity::Decr;
+                        } else if (comp.second) {
+                            // All decreasing states are below the increasing state, therefore increasing
+                            return Monotonicity::Incr;
                         }
-                        outfile << " - ";
-                    } else if (assumptions.size() == 0) {
-                        outfile << "No assumptions - ";
-                    }
-
-                    if (varsMonotone.size() == 0) {
-                        outfile << "No params";
-                    } else {
-                        auto itr2 = varsMonotone.begin();
-                        while (itr2 != varsMonotone.end()) {
-                            if (checkSamples &&
-                                resultCheckOnSamples.find(itr2->first) != resultCheckOnSamples.end() &&
-                                (!resultCheckOnSamples[itr2->first].first &&
-                                 !resultCheckOnSamples[itr2->first].second)) {
-                                outfile << "X " << itr2->first;
-                            } else {
-                                if (itr2->second.first && itr2->second.second) {
-                                    outfile << "C " << itr2->first;
-                                } else if (itr2->second.first) {
-                                    outfile << "I " << itr2->first;
-                                } else if (itr2->second.second) {
-                                    outfile << "D " << itr2->first;
-                                } else {
-                                    outfile << "? " << itr2->first;
-                                }
-                            }
-                            ++itr2;
-                            if (itr2 != varsMonotone.end()) {
-                                outfile << " ";
-                            }
+                    } else if (statesDecr.size() == 1 && statesIncr.size() > 1) {
+                        auto comp = order->allAboveBelow(statesDecr, statesIncr.back());
+                        if (comp.first) {
+                            // All increasing states are below the decreasing state, therefore increasing
+                            return Monotonicity::Incr;
+                        } else if (comp.second) {
+                            // All increasing states are above the decreasing state, therefore decreasing
+                            return Monotonicity::Decr;
                         }
-                        result.insert(
-                                std::pair<storm::analysis::Order *, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>>(
-                                        order, varsMonotone));
                     }
-                    ++i;
-                    outfile << ";";
                 }
-            }
-            outfile << ", ";
-
-            monotonicityCheckWatch.stop();
-            return result;
-        }
 
-        template <typename ValueType>
-        std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> MonotonicityChecker<ValueType>::createOrder() {
-            // Transform to Orders
-            storm::utility::Stopwatch orderWatch(true);
-
-            // Use parameter lifting modelchecker to get initial min/max values for order creation
-            storm::modelchecker::SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<ValueType>, double> plaModelChecker;
-            std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
-            auto env = Environment();
-
-            auto formula = formulas[0];
-            const storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> checkTask
-                = storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>(*formula);
-            STORM_LOG_THROW(plaModelChecker.canHandle(model, checkTask), storm::exceptions::NotSupportedException,
-                           "Cannot handle this formula");
-            plaModelChecker.specify(env, model, checkTask);
-
-            std::unique_ptr<storm::modelchecker::CheckResult> minCheck = plaModelChecker.check(env, region,storm::solver::OptimizationDirection::Minimize);
-            std::unique_ptr<storm::modelchecker::CheckResult> maxCheck = plaModelChecker.check(env, region,storm::solver::OptimizationDirection::Maximize);
-            auto minRes = minCheck->asExplicitQuantitativeCheckResult<double>();
-            auto maxRes = maxCheck->asExplicitQuantitativeCheckResult<double>();
-
-            std::vector<double> minValues = minRes.getValueVector();
-            std::vector<double> maxValues = maxRes.getValueVector();
-            // Create initial order
-            std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> criticalTuple = extender->toOrder(formulas, minValues, maxValues);
-            // Continue based on not (yet) sorted states
-            std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> result;
-
-            auto val1 = std::get<1>(criticalTuple);
-            auto val2 = std::get<2>(criticalTuple);
-            auto numberOfStates = model->getNumberOfStates();
-            std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions;
-
-            if (val1 == numberOfStates && val2 == numberOfStates) {
-                result.insert(std::pair<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>>(std::get<0>(criticalTuple), assumptions));
-            } else if (val1 != numberOfStates && val2 != numberOfStates) {
-
-                storm::analysis::AssumptionChecker<ValueType> *assumptionChecker;
-                if (model->isOfType(storm::models::ModelType::Dtmc)) {
-                    auto dtmc = model->as<storm::models::sparse::Dtmc<ValueType>>();
-                    assumptionChecker = new storm::analysis::AssumptionChecker<ValueType>(formulas[0], dtmc, region, 3);
-                } else if (model->isOfType(storm::models::ModelType::Mdp)) {
-                    auto mdp = model->as<storm::models::sparse::Mdp<ValueType>>();
-                    assumptionChecker = new storm::analysis::AssumptionChecker<ValueType>(formulas[0], mdp, 3);
-                } else {
-                    STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException,
-                                    "Unable to perform monotonicity analysis on the provided model type.");
-                }
-                auto assumptionMaker = new storm::analysis::AssumptionMaker<ValueType>(assumptionChecker, numberOfStates, validate);
-                result = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker, val1, val2, assumptions);
-            } else {
-                assert(false);
+                return Monotonicity::Unknown;
             }
-            orderWatch.stop();
-            return result;
-        }
-
-        template <typename ValueType>
-        std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> MonotonicityChecker<ValueType>::extendOrderWithAssumptions(storm::analysis::Order* order, storm::analysis::AssumptionMaker<ValueType>* assumptionMaker, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions) {
-            std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> result;
-
-            auto numberOfStates = model->getNumberOfStates();
-            if (val1 == numberOfStates || val2 == numberOfStates) {
-                assert (val1 == val2);
-                assert (order->getAddedStates()->size() == order->getAddedStates()->getNumberOfSetBits());
-                result.insert(std::pair<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>>(order, assumptions));
-            } else {
-                // Make the three assumptions
-                auto assumptionTriple = assumptionMaker->createAndCheckAssumption(val1, val2, order);
-                assert (assumptionTriple.size() == 3);
-                auto itr = assumptionTriple.begin();
-                auto assumption1 = *itr;
-                ++itr;
-                auto assumption2 = *itr;
-                ++itr;
-                auto assumption3 = *itr;
-
-                if (assumption1.second != AssumptionStatus::INVALID) {
-                    auto orderCopy = new Order(order);
-                    auto assumptionsCopy = std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>(assumptions);
-
-                    if (assumption1.second == AssumptionStatus::UNKNOWN) {
-                        // only add assumption to the set of assumptions if it is unknown if it is valid
-                        assumptionsCopy.push_back(assumption1.first);
-                    }
 
-                    auto criticalTuple = extender->extendOrder(orderCopy, assumption1.first);
-                    if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
-                        auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
-                                                                std::get<1>(criticalTuple), std::get<2>(criticalTuple),
-                                                                assumptionsCopy);
-                        result.insert(map.begin(), map.end());
-                    }
-                }
-
-                if (assumption2.second != AssumptionStatus::INVALID) {
-                    auto orderCopy = new Order(order);
-                    auto assumptionsCopy = std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>(assumptions);
-
-                    if (assumption2.second == AssumptionStatus::UNKNOWN) {
-                        assumptionsCopy.push_back(assumption2.first);
-                    }
-
-                    auto criticalTuple = extender->extendOrder(orderCopy, assumption2.first);
-                    if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
-                        auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
-                                                                std::get<1>(criticalTuple), std::get<2>(criticalTuple),
-                                                                assumptionsCopy);
-                        result.insert(map.begin(), map.end());
-                    }
-                }
-
-                if (assumption3.second != AssumptionStatus::INVALID) {
-                    // Here we can use the original order and assumptions set
-                    if (assumption3.second == AssumptionStatus::UNKNOWN) {
-                        assumptions.push_back(assumption3.first);
-                    }
-
-                    auto criticalTuple = extender->extendOrder(order, assumption3.first);
-                    if (somewhereMonotonicity(std::get<0>(criticalTuple))) {
-                        auto map = extendOrderWithAssumptions(std::get<0>(criticalTuple), assumptionMaker,
-                                                                std::get<1>(criticalTuple), std::get<2>(criticalTuple),
-                                                                assumptions);
-                        result.insert(map.begin(), map.end());
-                    }
-                }
+            if (succSize == 2) {
+                // In this case we can ignore the last entry, as this will have a probability of 1 - the other
+                succSize = 1;
             }
-            return result;
-        }
 
-        template <typename ValueType>
-        ValueType MonotonicityChecker<ValueType>::getDerivative(ValueType function, typename utility::parametric::VariableType<ValueType>::type var) {
-            if (function.isConstant()) {
-                return storm::utility::zero<ValueType>();
-            }
-            if ((derivatives[function]).find(var) == (derivatives[function]).end()) {
-                (derivatives[function])[var] = function.derivative(var);
-            }
-
-            return (derivatives[function])[var];
-        }
-
-        template <typename ValueType>
-        std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::analyseMonotonicity(uint_fast64_t j, storm::analysis::Order* order, storm::storage::SparseMatrix<ValueType> matrix) {
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone;
-
-            // go over all rows, check for each row local monotonicity
-            for (uint_fast64_t i = 0; i < matrix.getColumnCount(); ++i) {
-                auto row = matrix.getRow(i);
-                // only enter if you are in a state with at least two successors (so there must be successors,
-                // and first prob shouldn't be 1)
-                if (row.begin() != row.end() && !row.begin()->getValue().isOne()) {
-                    std::map<uint_fast64_t, ValueType> transitions;
-
-                    // Gather all states which are reached with a non constant probability
-                    auto states = new storm::storage::BitVector(matrix.getColumnCount());
-                    std::set<typename utility::parametric::VariableType<ValueType>::type> vars;
-                    for (auto const& entry : row) {
-                        if (!entry.getValue().isConstant()) {
-                            // only analyse take non constant transitions
-                            transitions.insert(std::pair<uint_fast64_t, ValueType>(entry.getColumn(), entry.getValue()));
-                            for (auto const& var:entry.getValue().gatherVariables()) {
-                                vars.insert(var);
-                                states->set(entry.getColumn());
-                            }
-                        }
-                    }
-
-                    // Copy info from checkOnSamples
-                    if (checkSamples) {
-                        for (auto var : vars) {
-                            assert (resultCheckOnSamples.find(var) != resultCheckOnSamples.end());
-                            if (varsMonotone.find(var) == varsMonotone.end()) {
-                                varsMonotone[var].first = resultCheckOnSamples[var].first;
-                                varsMonotone[var].second = resultCheckOnSamples[var].second;
-                            } else {
-                                varsMonotone[var].first &= resultCheckOnSamples[var].first;
-                                varsMonotone[var].second &= resultCheckOnSamples[var].second;
-                            }
-                        }
-                    } else {
-                        for (auto var : vars) {
-                            if (varsMonotone.find(var) == varsMonotone.end()) {
-                                varsMonotone[var].first = true;
-                                varsMonotone[var].second = true;
-                            }
-                        }
-                    }
-
-
-
-                    // Sort the states based on the order
-                    auto sortedStates = order->sortStates(states);
-                    if (sortedStates[sortedStates.size() - 1] == matrix.getColumnCount()) {
-                    // If the states are not all sorted, we still might obtain some monotonicity
-                        for (auto var: vars) {
-                            // current value of monotonicity
-                            std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
-
-                            // Go over all transitions to successor states, compare all of them
-                            for (auto itr2 = transitions.begin(); (value->first || value->second)
-                                    && itr2 != transitions.end(); ++itr2) {
-                                for (auto itr3 = transitions.begin(); (value->first || value->second)
-                                        && itr3 != transitions.end(); ++itr3) {
-                                    if (itr2->first < itr3->first) {
-
-                                        auto derivative2 = getDerivative(itr2->second, var);
-                                        auto derivative3 = getDerivative(itr3->second, var);
-
-                                        auto compare = order->compare(itr2->first, itr3->first);
-
-                                        if (compare == Order::ABOVE) {
-                                            // As the first state (itr2) is above the second state (itr3) it
-                                            // is sufficient to look at the derivative of itr2.
-                                            std::pair<bool, bool> mon2;
-                                            mon2 = checkDerivative(derivative2, region);
-                                            value->first &= mon2.first;
-                                            value->second &= mon2.second;
-                                        } else if (compare == Order::BELOW) {
-                                            // As the second state (itr3) is above the first state (itr2) it
-                                            // is sufficient to look at the derivative of itr3.
-                                            std::pair<bool, bool> mon3;
-
-                                            mon3 = checkDerivative(derivative3, region);
-                                            value->first &= mon3.first;
-                                            value->second &= mon3.second;
-                                        } else if (compare == Order::SAME) {
-                                            // Behaviour doesn't matter, as the states are at the same level.
-                                        } else {
-                                            // only if derivatives are the same we can continue
-                                            if (derivative2 != derivative3) {
-                                                // As the relation between the states is unknown, we can't claim
-                                                // anything about the monotonicity.
-                                                value->first = false;
-                                                value->second = false;
-                                            }
-
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    } else {
-                        // The states are all sorted
-                        for (auto var : vars) {
-                            std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
-                            bool change = false;
-                            for (auto const &i : sortedStates) {
-                                auto res = checkDerivative(getDerivative(transitions[i], var), region);
-                                change = change || (!(value->first && value->second) // they do not hold both
-                                                    && ((value->first && !res.first)
-                                                        || (value->second && !res.second)));
-
-                                if (change) {
-                                    value->first &= res.second;
-                                    value->second &= res.first;
-                                } else {
-                                    value->first &= res.first;
-                                    value->second &= res.second;
-                                }
-                                if (!value->first && !value->second) {
-                                    break;
-                                }
-                            }
-
-                        }
-                    }
+            // First check as long as it stays constant and either incr or decr
+            bool allowedToSwap = true;
+            Monotonicity localMonotonicity = Monotonicity::Constant;
+            uint_fast64_t index = 0;
+            while (index < succSize && localMonotonicity == Monotonicity::Constant) {
+                auto itr = std::find(succs.begin(), succs.end(), succsSorted[index]);
+                auto newIndex = std::distance(succs.begin(), itr);
+                auto transitionMon = succsMonUnsorted[newIndex];
+                localMonotonicity = transitionMon;
+                if (transitionMon == Monotonicity::Not && succSize != 1) {
+                    localMonotonicity = Monotonicity::Unknown;
                 }
+                index++;
             }
-            return varsMonotone;
-        }
 
-        template <typename ValueType>
-        bool MonotonicityChecker<ValueType>::somewhereMonotonicity(Order* order) {
-            std::shared_ptr<storm::models::sparse::Model<ValueType>> sparseModel = model->as<storm::models::sparse::Model<ValueType>>();
-            auto matrix = sparseModel->getTransitionMatrix();
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> varsMonotone;
+            while (index < succSize && localMonotonicity != Monotonicity::Not && localMonotonicity != Monotonicity::Unknown) {
+                // We get here as soon as we have seen incr/decr once
+                auto itr = std::find(succs.begin(), succs.end(), succsSorted[index]);
+                auto newIndex = std::distance(succs.begin(), itr);
+                auto transitionMon = succsMonUnsorted[newIndex];
 
-            for (uint_fast64_t i = 0; i < matrix.getColumnCount(); ++i) {
-                // go over all rows
-                auto row = matrix.getRow(i);
-                auto first = (*row.begin());
-                if (first.getValue() != ValueType(1)) {
-                    std::map<uint_fast64_t, ValueType> transitions;
-
-                    for (auto itr = row.begin(); itr != row.end(); ++itr) {
-                        transitions.insert(std::pair<uint_fast64_t, ValueType>((*itr).getColumn(), (*itr).getValue()));
+                if (transitionMon == Monotonicity::Not || transitionMon == Monotonicity::Unknown) {
+                    return Monotonicity::Unknown;
+                }
+                if (allowedToSwap) {
+                    // So far we have only seen constant and either incr or decr, but not both
+                    if (transitionMon != Monotonicity::Constant && transitionMon != localMonotonicity) {
+                        allowedToSwap = false;
                     }
-
-                    auto val = first.getValue();
-                    auto vars = val.gatherVariables();
-                    // Copy info from checkOnSamples
-                    if (checkSamples) {
-                        for (auto var : vars) {
-                            assert (resultCheckOnSamples.find(var) != resultCheckOnSamples.end());
-                            if (varsMonotone.find(var) == varsMonotone.end()) {
-                                varsMonotone[var].first = resultCheckOnSamples[var].first;
-                                varsMonotone[var].second = resultCheckOnSamples[var].second;
-                            } else {
-                                varsMonotone[var].first &= resultCheckOnSamples[var].first;
-                                varsMonotone[var].second &= resultCheckOnSamples[var].second;
-                            }
-                        }
-                    } else {
-                        for (auto var : vars) {
-                            if (varsMonotone.find(var) == varsMonotone.end()) {
-                                varsMonotone[var].first = true;
-                                varsMonotone[var].second = true;
-                            }
-                        }
-                    }
-
-                    for (auto var: vars) {
-                        // current value of monotonicity
-                        std::pair<bool, bool> *value = &varsMonotone.find(var)->second;
-
-                        // Go over all transitions to successor states, compare all of them
-                        for (auto itr2 = transitions.begin(); (value->first || value->second)
-                                                              && itr2 != transitions.end(); ++itr2) {
-                            for (auto itr3 = transitions.begin(); (value->first || value->second)
-                                                                  && itr3 != transitions.end(); ++itr3) {
-                                if (itr2->first < itr3->first) {
-
-                                    auto derivative2 = getDerivative(itr2->second, var);
-                                    auto derivative3 = getDerivative(itr3->second, var);
-
-                                    auto compare = order->compare(itr2->first, itr3->first);
-
-                                    if (compare == Order::ABOVE) {
-                                        // As the first state (itr2) is above the second state (itr3) it
-                                        // is sufficient to look at the derivative of itr2.
-                                        std::pair<bool, bool> mon2;
-                                        mon2 = checkDerivative(derivative2, region);
-                                        value->first &= mon2.first;
-                                        value->second &= mon2.second;
-                                    } else if (compare == Order::BELOW) {
-                                        // As the second state (itr3) is above the first state (itr2) it
-                                        // is sufficient to look at the derivative of itr3.
-                                        std::pair<bool, bool> mon3;
-
-                                        mon3 = checkDerivative(derivative3, region);
-                                        value->first &= mon3.first;
-                                        value->second &= mon3.second;
-                                    } else if (compare == Order::SAME) {
-                                        // Behaviour doesn't matter, as the states are at the same level.
-                                    } else {
-                                        // As the relation between the states is unknown, we don't do anything
-                                    }
-                                }
-                            }
-                        }
+                } else if (!allowedToSwap) {
+                    // So we have been at the point where we changed from incr to decr (or decr to incr)
+                    if (transitionMon == localMonotonicity || transitionMon == Monotonicity::Not || transitionMon == Monotonicity::Unknown) {
+                        localMonotonicity = Monotonicity::Unknown;
                     }
                 }
+                index++;
             }
-
-            bool result = false;
-
-            for (auto itr = varsMonotone.begin(); !result && itr != varsMonotone.end(); ++itr) {
-                result = itr->second.first || itr->second.second;
-            }
-            return result;
+            return localMonotonicity;
         }
 
+        /*** Private methods ***/
         template <typename ValueType>
-        std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::checkOnSamples(std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples) {
-            storm::utility::Stopwatch samplesWatch(true);
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> result;
-
-            auto instantiator = storm::utility::ModelInstantiator<storm::models::sparse::Dtmc<ValueType>, storm::models::sparse::Dtmc<double>>(*model);
-            auto matrix = model->getTransitionMatrix();
-            std::set<typename utility::parametric::VariableType<ValueType>::type> variables =  storm::models::sparse::getProbabilityParameters(*model);
-
-            // For each of the variables create a model in which we only change the value for this specific variable
-            for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
-                double previous = -1;
-                bool monDecr = true;
-                bool monIncr = true;
-
-                // Check monotonicity in variable (*itr) by instantiating the model
-                // all other variables fixed on lb, only increasing (*itr)
-                for (uint_fast64_t i = 0; (monDecr || monIncr) && i < numberOfSamples; ++i) {
-                    // Create valuation
-                    auto valuation = storm::utility::parametric::Valuation<ValueType>();
-                    for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
-                        // Only change value for current variable
-                        if ((*itr) == (*itr2)) {
-                            auto lb = region.getLowerBoundary(itr->name());
-                            auto ub = region.getUpperBoundary(itr->name());
-                            // Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
-                            valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1));
-                        } else {
-                            auto lb = region.getLowerBoundary(itr->name());
-                            valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
-                        }
-                    }
-
-                    // Instantiate model and get result
-                    storm::models::sparse::Dtmc<double> sampleModel = instantiator.instantiate(valuation);
-                    auto checker = storm::modelchecker::SparseDtmcPrctlModelChecker<storm::models::sparse::Dtmc<double>>(sampleModel);
-                    std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
-                    auto formula = formulas[0];
-                    if (formula->isProbabilityOperatorFormula() &&
-                        formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
-                        const storm::modelchecker::CheckTask<storm::logic::UntilFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::UntilFormula, double>(
-                                (*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
-                        checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
-                    } else if (formula->isProbabilityOperatorFormula() &&
-                               formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
-                        const storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double>(
-                                (*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
-                        checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
-                    } else {
-                        STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
-                                        "Expecting until or eventually formula");
-                    }
-                    auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
-                    std::vector<double> values = quantitativeResult.getValueVector();
-                    auto initialStates = model->getInitialStates();
-                    double initial = 0;
-                    // Get total probability from initial states
-                    for (auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j+1)) {
-                        initial += values[j];
-                    }
-                    // Calculate difference with result for previous valuation
-                    assert (initial >= 0-precision && initial <= 1+precision);
-                    double diff = previous - initial;
-                    assert (previous == -1 || diff >= -1-precision && diff <= 1 + precision);
-                    if (previous != -1 && (diff > precision || diff < -precision)) {
-                        monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
-                        monIncr &= diff < -precision;
-                    }
-                    previous = initial;
-                }
-                result.insert(std::pair<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(*itr, std::pair<bool,bool>(monIncr, monDecr)));
+        typename MonotonicityChecker<ValueType>::Monotonicity MonotonicityChecker<ValueType>::checkTransitionMonRes(ValueType function, typename MonotonicityChecker<ValueType>::VariableType param, typename MonotonicityChecker<ValueType>::Region region) {
+            std::pair<bool, bool> res = MonotonicityChecker<ValueType>::checkDerivative(getDerivative(function, param), region);
+            if (res.first && !res.second) {
+                return Monotonicity::Incr;
+            } else if (!res.first && res.second) {
+                return Monotonicity::Decr;
+            } else if (res.first && res.second) {
+                return Monotonicity::Constant;
+            } else {
+                return Monotonicity::Not;
             }
-
-            samplesWatch.stop();
-            resultCheckOnSamples = result;
-            return result;
         }
 
         template <typename ValueType>
-        std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> MonotonicityChecker<ValueType>::checkOnSamples(std::shared_ptr<storm::models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
-            storm::utility::Stopwatch samplesWatch(true);
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> result;
-
-            auto instantiator = storm::utility::ModelInstantiator<storm::models::sparse::Mdp<ValueType>, storm::models::sparse::Mdp<double>>(*model);
-            auto matrix = model->getTransitionMatrix();
-            std::set<typename utility::parametric::VariableType<ValueType>::type> variables =  storm::models::sparse::getProbabilityParameters(*model);
-
-            for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
-                double previous = -1;
-                bool monDecr = true;
-                bool monIncr = true;
-
-                for (uint_fast64_t i = 0; i < numberOfSamples; ++i) {
-                    auto valuation = storm::utility::parametric::Valuation<ValueType>();
-                    for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
-                        // Only change value for current variable
-                        if ((*itr) == (*itr2)) {
-                            auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>(
-                                    (*itr2), storm::utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(
-                                            boost::lexical_cast<std::string>((i + 1) / (double(numberOfSamples + 1)))));
-                            valuation.insert(val);
-                        } else {
-                            auto val = std::pair<typename utility::parametric::VariableType<ValueType>::type, typename utility::parametric::CoefficientType<ValueType>::type>(
-                                    (*itr2), storm::utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(
-                                            boost::lexical_cast<std::string>((1) / (double(numberOfSamples + 1)))));
-                            valuation.insert(val);
-                        }
-                    }
-                    storm::models::sparse::Mdp<double> sampleModel = instantiator.instantiate(valuation);
-                    auto checker = storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<double>>(sampleModel);
-                    std::unique_ptr<storm::modelchecker::CheckResult> checkResult;
-                    auto formula = formulas[0];
-                    if (formula->isProbabilityOperatorFormula() &&
-                        formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
-                        const storm::modelchecker::CheckTask<storm::logic::UntilFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::UntilFormula, double>(
-                                (*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
-                        checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
-                    } else if (formula->isProbabilityOperatorFormula() &&
-                               formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
-                        const storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double> checkTask = storm::modelchecker::CheckTask<storm::logic::EventuallyFormula, double>(
-                                (*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
-                        checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
-                    } else {
-                        STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
-                                        "Expecting until or eventually formula");
-                    }
-                    auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<double>();
-                    std::vector<double> values = quantitativeResult.getValueVector();
-                    auto initialStates = model->getInitialStates();
-                    double initial = 0;
-                    for (auto i = initialStates.getNextSetIndex(0); i < model->getNumberOfStates(); i = initialStates.getNextSetIndex(i+1)) {
-                        initial += values[i];
-                    }
-                    assert (initial >= precision && initial <= 1+precision);
-                    double diff = previous - initial;
-                    assert (previous == -1 || diff >= -1-precision && diff <= 1 + precision);
-                    if (previous != -1 && (diff > precision || diff < -precision)) {
-                        monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
-                        monIncr &= diff < -precision;
-                    }
-                    previous = initial;
-                }
-                result.insert(std::pair<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>(*itr, std::pair<bool,bool>(monIncr, monDecr)));
+        ValueType& MonotonicityChecker<ValueType>::getDerivative(ValueType function, typename MonotonicityChecker<ValueType>::VariableType var) {
+            auto& derivativeMap = derivatives[function];
+            if (derivativeMap.find(var) == derivativeMap.end()) {
+                derivativeMap[var] = function.derivative(var);
             }
-
-            samplesWatch.stop();
-            resultCheckOnSamples = result;
-            return result;
+            return derivativeMap[var];
         }
 
-        template class MonotonicityChecker<storm::RationalFunction>;
+        template class MonotonicityChecker<RationalFunction>;
     }
 }
diff --git a/src/storm-pars/analysis/MonotonicityChecker.h b/src/storm-pars/analysis/MonotonicityChecker.h
index 31fc0e193..9f6dc1ddd 100644
--- a/src/storm-pars/analysis/MonotonicityChecker.h
+++ b/src/storm-pars/analysis/MonotonicityChecker.h
@@ -2,22 +2,22 @@
 #define STORM_MONOTONICITYCHECKER_H
 
 #include <map>
+#include <boost/container/flat_map.hpp>
 #include "Order.h"
-#include "OrderExtender.h"
-#include "AssumptionMaker.h"
+#include "LocalMonotonicityResult.h"
+#include "MonotonicityResult.h"
+#include "storm-pars/storage/ParameterRegion.h"
+
+
+#include "storm/solver/Z3SmtSolver.h"
+
+#include "storm/storage/SparseMatrix.h"
 #include "storm/storage/expressions/BinaryRelationExpression.h"
 #include "storm/storage/expressions/ExpressionManager.h"
-
 #include "storm/storage/expressions/RationalFunctionToExpression.h"
-#include "storm/utility/constants.h"
-#include "storm/models/ModelBase.h"
-#include "storm/models/sparse/Dtmc.h"
-#include "storm/models/sparse/Mdp.h"
-#include "storm/logic/Formula.h"
-#include "storm/storage/SparseMatrix.h"
-#include "storm-pars/api/region.h"
-#include "storm/solver/Z3SmtSolver.h"
 
+#include "storm/utility/constants.h"
+#include "storm/utility/solver.h"
 
 namespace storm {
     namespace analysis {
@@ -26,34 +26,26 @@ namespace storm {
         class MonotonicityChecker {
 
         public:
-            /*!
-             * Constructor of MonotonicityChecker
-             * @param model the model considered
-             * @param formula the formula considered
-             * @param regions the regions to consider
-             * @param validate whether or not assumptions are to be validated
-             * @param numberOfSamples number of samples taken for monotonicity checking, default 0,
-             *          if 0 then no check on samples is executed
-             * @param precision precision on which the samples are compared
-             */
-            MonotonicityChecker(std::shared_ptr<storm::models::ModelBase> model, std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<storm::storage::ParameterRegion<ValueType>> regions, bool validate, uint_fast64_t numberOfSamples=0, double const& precision=0.000001);
-
-            /*!
-             * Checks for model and formula as provided in constructor for monotonicity
-             */
-            std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> checkMonotonicity(std::ostream& outfile);
+            typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
+            typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
+            typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+            typedef typename storage::ParameterRegion<ValueType> Region;
 
             /*!
-             * Checks if monotonicity can be found in this order. Unordered states are not checked
+             * Constructs a new MonotonicityChecker object.
+             *
+             * @param matrix The Matrix of the model.
              */
-            bool somewhereMonotonicity(storm::analysis::Order* order) ;
+            MonotonicityChecker(storage::SparseMatrix<ValueType> matrix);
 
             /*!
-             * Checks if a derivative >=0 or/and <=0
-             * @param derivative The derivative you want to check
-             * @return pair of bools, >= 0 and <= 0
+             * Checks if a derivative >=0 or/and <=0.
+             *
+             * @param derivative The derivative you want to check.
+             * @param reg The region of the parameters.
+             * @return Pair of bools, >= 0 and <= 0.
              */
-            static std::pair<bool, bool> checkDerivative(ValueType derivative, storm::storage::ParameterRegion<ValueType> reg) {
+            static std::pair<bool, bool> checkDerivative(ValueType derivative, storage::ParameterRegion<ValueType> reg) {
                 bool monIncr = false;
                 bool monDecr = false;
 
@@ -64,86 +56,58 @@ namespace storm {
                     monIncr = derivative.constantPart() >= 0;
                     monDecr = derivative.constantPart() <= 0;
                 } else {
+                    std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
+                    std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
+                    solver::Z3SmtSolver s(*manager);
+                    std::set<VariableType> variables = derivative.gatherVariables();
 
-                    std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<storm::utility::solver::MathsatSmtSolverFactory>();
-                    std::shared_ptr<storm::expressions::ExpressionManager> manager(
-                            new storm::expressions::ExpressionManager());
-
-                    storm::solver::Z3SmtSolver s(*manager);
-
-                    std::set<typename utility::parametric::VariableType<ValueType>::type> variables = derivative.gatherVariables();
-
-
+                    expressions::Expression exprBounds = manager->boolean(true);
                     for (auto variable : variables) {
-                        manager->declareRationalVariable(variable.name());
-
-                    }
-                    storm::expressions::Expression exprBounds = manager->boolean(true);
-                    auto managervars = manager->getVariables();
-                    for (auto var : managervars) {
-                        auto lb = storm::utility::convertNumber<storm::RationalNumber>(reg.getLowerBoundary(var.getName()));
-                        auto ub = storm::utility::convertNumber<storm::RationalNumber>(reg.getUpperBoundary(var.getName()));
-                        exprBounds = exprBounds && manager->rational(lb) < var && var < manager->rational(ub);
+                        auto managerVariable = manager->declareRationalVariable(variable.name());
+                        auto lb = utility::convertNumber<RationalNumber>(reg.getLowerBoundary(variable));
+                        auto ub = utility::convertNumber<RationalNumber>(reg.getUpperBoundary(variable));
+                        exprBounds = exprBounds && manager->rational(lb) < managerVariable && managerVariable < manager->rational(ub);
                     }
-                    assert (s.check() == storm::solver::SmtSolver::CheckResult::Sat);
 
-                    auto converter = storm::expressions::RationalFunctionToExpression<ValueType>(manager);
+                    auto converter = expressions::RationalFunctionToExpression<ValueType>(manager);
 
-                    // < 0 so not monotone increasing
-                    storm::expressions::Expression exprToCheck =
-                            converter.toExpression(derivative) < manager->rational(0);
+                    // < 0, so not monotone increasing. If this is unsat, then it should be monotone increasing.
+                    expressions::Expression exprToCheck = converter.toExpression(derivative) < manager->rational(0);
                     s.add(exprBounds);
                     s.add(exprToCheck);
-                    // If it is unsatisfiable then it should be monotone increasing
-                    monIncr = s.check() == storm::solver::SmtSolver::CheckResult::Unsat;
-
-                    // > 0 so not monotone decreasing
-                    exprToCheck =
-                            converter.toExpression(derivative) > manager->rational(0);
+                    monIncr = s.check() == solver::SmtSolver::CheckResult::Unsat;
 
+                    // > 0, so not monotone decreasing. If this is unsat it should be monotone decreasing.
+                    exprToCheck = converter.toExpression(derivative) > manager->rational(0);
                     s.reset();
                     s.add(exprBounds);
-                    assert (s.check() == storm::solver::SmtSolver::CheckResult::Sat);
                     s.add(exprToCheck);
-                    monDecr = s.check() == storm::solver::SmtSolver::CheckResult::Unsat;
+                    monDecr = s.check() == solver::SmtSolver::CheckResult::Unsat;
                 }
                 assert (!(monIncr && monDecr) || derivative.isZero());
 
                 return std::pair<bool, bool>(monIncr, monDecr);
             }
 
-        private:
-            std::map<storm::analysis::Order*, std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>>> checkMonotonicity(std::ostream& outfile, std::map<storm::analysis::Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> map, storm::storage::SparseMatrix<ValueType> matrix);
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> analyseMonotonicity(uint_fast64_t i, Order* order, storm::storage::SparseMatrix<ValueType> matrix) ;
-
-            std::map<Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> createOrder();
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> checkOnSamples(std::shared_ptr<storm::models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples);
-
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> checkOnSamples(std::shared_ptr<storm::models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
-
-            std::unordered_map<ValueType, std::unordered_map<typename utility::parametric::VariableType<ValueType>::type, ValueType>> derivatives;
-
-            ValueType getDerivative(ValueType function, typename utility::parametric::VariableType<ValueType>::type var);
-
-            std::map<Order*, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>>> extendOrderWithAssumptions(Order* order, AssumptionMaker<ValueType>* assumptionMaker, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<storm::expressions::BinaryRelationExpression>> assumptions);
-
-            std::shared_ptr<storm::models::ModelBase> model;
-
-            std::vector<std::shared_ptr<storm::logic::Formula const>> formulas;
-
-            bool validate;
-
-            bool checkSamples;
+            /*!
+             * Checks for local monotonicity at the given state.
+             *
+             * @param order The order on which the monotonicity should be checked.
+             * @param state The considerd state.
+             * @param var The variable in which we check for monotonicity.
+             * @param region The region on which we check the monotonicity.
+             * @return Incr, Decr, Constant, Unknown or Not
+             */
+            Monotonicity checkLocalMonotonicity(std::shared_ptr<Order> const & order, uint_fast64_t state, VariableType const& var, storage::ParameterRegion<ValueType> const& region);
 
-            std::map<typename utility::parametric::VariableType<ValueType>::type, std::pair<bool, bool>> resultCheckOnSamples;
+        private:
+            Monotonicity checkTransitionMonRes(ValueType function, VariableType param, Region region);
 
-            OrderExtender<ValueType> *extender;
+            ValueType& getDerivative(ValueType function, VariableType var);
 
-            double precision;
+            storage::SparseMatrix<ValueType> matrix;
 
-            storm::storage::ParameterRegion<ValueType> region;
+            boost::container::flat_map<ValueType, boost::container::flat_map<VariableType, ValueType>> derivatives;
         };
     }
 }
diff --git a/src/storm-pars/analysis/MonotonicityHelper.cpp b/src/storm-pars/analysis/MonotonicityHelper.cpp
new file mode 100644
index 000000000..07cf5eb04
--- /dev/null
+++ b/src/storm-pars/analysis/MonotonicityHelper.cpp
@@ -0,0 +1,325 @@
+#include "MonotonicityHelper.h"
+
+#include "storm/exceptions/NotSupportedException.h"
+#include "storm/exceptions/InvalidOperationException.h"
+
+#include "storm/models/ModelType.h"
+
+#include "storm/modelchecker/results/CheckResult.h"
+
+#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+
+#include "storm-pars/analysis/AssumptionChecker.h"
+
+
+namespace storm {
+    namespace analysis {
+        /*** Constructor ***/
+        template <typename ValueType, typename ConstantType>
+        MonotonicityHelper<ValueType, ConstantType>::MonotonicityHelper(std::shared_ptr<models::sparse::Model<ValueType>> model, std::vector<std::shared_ptr<logic::Formula const>> formulas, std::vector<storage::ParameterRegion<ValueType>> regions, uint_fast64_t numberOfSamples, double const& precision, bool dotOutput) : assumptionMaker(model->getTransitionMatrix()){
+            assert (model != nullptr);
+            STORM_LOG_THROW(regions.size() <= 1, exceptions::NotSupportedException, "Monotonicity checking is not (yet) supported for multiple regions");
+            STORM_LOG_THROW(formulas.size() <= 1, exceptions::NotSupportedException, "Monotonicity checking is not (yet) supported for multiple formulas");
+
+            this->model = model;
+            this->formulas = formulas;
+            this->precision = utility::convertNumber<ConstantType>(precision);
+            this->matrix = model->getTransitionMatrix();
+            this->dotOutput = dotOutput;
+
+            if (regions.size() == 1) {
+                this->region = *(regions.begin());
+            } else {
+                typename storage::ParameterRegion<ValueType>::Valuation lowerBoundaries;
+                typename storage::ParameterRegion<ValueType>::Valuation upperBoundaries;
+                std::set<VariableType> vars;
+                vars = models::sparse::getProbabilityParameters(*model);
+                for (auto var : vars) {
+                    typename storage::ParameterRegion<ValueType>::CoefficientType lb = utility::convertNumber<CoefficientType>(0 + precision) ;
+                    typename storage::ParameterRegion<ValueType>::CoefficientType ub = utility::convertNumber<CoefficientType>(1 - precision) ;
+                    lowerBoundaries.insert(std::make_pair(var, lb));
+                    upperBoundaries.insert(std::make_pair(var, ub));
+                }
+                this->region = storage::ParameterRegion<ValueType>(std::move(lowerBoundaries), std::move(upperBoundaries));
+            }
+
+            if (numberOfSamples > 2) {
+                // sampling
+                if (model->isOfType(models::ModelType::Dtmc)) {
+                            checkMonotonicityOnSamples(model->template as<models::sparse::Dtmc<ValueType>>(), numberOfSamples);
+                } else if (model->isOfType(models::ModelType::Mdp)) {
+                            checkMonotonicityOnSamples(model->template as<models::sparse::Mdp<ValueType>>(), numberOfSamples);
+                }
+                checkSamples = true;
+            } else {
+                if (numberOfSamples > 0) {
+                    STORM_LOG_WARN("At least 3 sample points are needed to check for monotonicity on samples, not using samples for now");
+                }
+                checkSamples = false;
+            }
+
+            this->extender = new analysis::OrderExtender<ValueType, ConstantType>(model, formulas[0]);
+
+            for (auto i = 0; i < matrix.getRowCount(); ++i) {
+                std::set<VariableType> occurringVariables;
+
+                for (auto &entry : matrix.getRow(i)) {
+                    storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
+                }
+                for (auto& var : occurringVariables) {
+                    occuringStatesAtVariable[var].push_back(i);
+                }
+            }
+        }
+
+
+        /*** Public methods ***/
+        template <typename ValueType, typename ConstantType>
+        std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<typename MonotonicityHelper<ValueType, ConstantType>::VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityInBuild(std::ostream& outfile, bool usePLA, std::string dotOutfileName) {
+            if (usePLA) {
+                storm::utility::Stopwatch plaWatch(true);
+                this->extender->initializeMinMaxValues(region);
+                plaWatch.stop();
+                STORM_PRINT(std::endl << "Total time for pla checking: " << plaWatch << "." << std::endl << std::endl);
+            }
+            createOrder();
+
+            //output of results
+            for (auto itr : monResults) {
+                if (itr.first != nullptr) {
+                    std::cout << "Number of done states: " << itr.first->getNumberOfDoneStates() << std::endl;
+                }
+                if (checkSamples) {
+                    for (auto & entry : resultCheckOnSamples.getMonotonicityResult()) {
+                        if (entry.second == Monotonicity::Not) {
+                            itr.second.first->updateMonotonicityResult(entry.first, entry.second, true);
+                        }
+                    }
+                }
+                std::string temp = itr.second.first->toString();
+                bool first = true;
+                for (auto& assumption : itr.second.second) {
+                    if (!first) {
+                        outfile << " & ";
+                    } else {
+                        outfile << "Assumptions: " << std::endl << "    ";
+                        first = false;
+                    }
+                    outfile << *assumption;
+                }
+                if (!first) {
+                    outfile << std::endl;
+                } else {
+                    outfile << "No Assumptions" << std::endl;
+                }
+                outfile << "Monotonicity Result: " << std::endl << "    " << temp << std::endl << std::endl;
+            }
+
+            if (monResults.size() == 0) {
+                outfile << "No monotonicity found, as the order is insufficient" << std::endl;
+                if (checkSamples) {
+                        outfile << "Monotonicity Result on samples: " << resultCheckOnSamples.toString() << std::endl;
+                }
+            }
+
+            //dotoutput
+            if (dotOutput) {
+                STORM_LOG_WARN_COND(monResults.size() <= 10, "Too many Reachability Orders. Dot Output will only be created for 10.");
+                int i = 0;
+                auto orderItr = monResults.begin();
+                while (i < 10 && orderItr != monResults.end()) {
+                    std::ofstream dotOutfile;
+                    std::string name = dotOutfileName + std::to_string(i);
+                    utility::openFile(name, dotOutfile);
+                    dotOutfile << "Assumptions:" << std::endl;
+                    auto assumptionItr = orderItr->second.second.begin();
+                    while (assumptionItr != orderItr->second.second.end()) {
+                        dotOutfile << *assumptionItr << std::endl;
+                        dotOutfile << std::endl;
+                        assumptionItr++;
+                    }
+                    dotOutfile << std::endl;
+                    orderItr->first->dotOutputToFile(dotOutfile);
+                    utility::closeFile(dotOutfile);
+                    i++;
+                    orderItr++;
+                }
+            }
+            return monResults;
+        }
+
+        /*** Private methods ***/
+        template <typename ValueType, typename ConstantType>
+        void MonotonicityHelper<ValueType, ConstantType>::createOrder() {
+            // Transform to Orders
+            std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> criticalTuple;
+
+            // Create initial order
+            auto monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
+            criticalTuple = extender->toOrder(region, monRes);
+            // Continue based on not (yet) sorted states
+            std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
+
+            auto val1 = std::get<1>(criticalTuple);
+            auto val2 = std::get<2>(criticalTuple);
+            auto numberOfStates = model->getNumberOfStates();
+            std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions;
+
+            if (val1 == numberOfStates && val2 == numberOfStates) {
+                auto resAssumptionPair = std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes, assumptions);
+                monResults.insert(std::pair<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(std::get<0>(criticalTuple), resAssumptionPair));
+            } else if (val1 != numberOfStates && val2 != numberOfStates) {
+                extendOrderWithAssumptions(std::get<0>(criticalTuple), val1, val2, assumptions, monRes);
+            } else {
+                assert (false);
+            }
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void MonotonicityHelper<ValueType, ConstantType>::extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions, std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
+            std::map<std::shared_ptr<Order>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>> result;
+            if (order->isInvalid()) {
+                // We don't add anything as the order we created with assumptions turns out to be invalid
+                STORM_LOG_INFO("    The order was invalid, so we stop here");
+                return;
+            }
+            auto numberOfStates = model->getNumberOfStates();
+            if (val1 == numberOfStates || val2 == numberOfStates) {
+                assert (val1 == val2);
+                assert (order->getNumberOfAddedStates() == order->getNumberOfStates());
+                auto resAssumptionPair = std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>(monRes, assumptions);
+                monResults.insert(std::pair<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>>(std::move(order), std::move(resAssumptionPair)));
+            } else {
+                // Make the three assumptions
+                STORM_LOG_INFO("Creating assumptions for " << val1 << " and " << val2 << ". ");
+                auto newAssumptions = assumptionMaker.createAndCheckAssumptions(val1, val2, order, region);
+                assert (newAssumptions.size() <= 3);
+                auto itr = newAssumptions.begin();
+                if (newAssumptions.size() == 0) {
+                    monRes = std::make_shared<MonotonicityResult<VariableType>>(MonotonicityResult<VariableType>());
+                    for (auto& entry : occuringStatesAtVariable) {
+                        for (auto & state : entry.second) {
+                            extender->checkParOnStateMonRes(state, order, entry.first, monRes);
+                            if (monRes->getMonotonicity(entry.first) == Monotonicity::Unknown) {
+                                break;
+                            }
+                        }
+                        monRes->setDoneForVar(entry.first);
+                    }
+                    monResults.insert({order, {monRes, assumptions}});
+                    STORM_LOG_INFO("    None of the assumptions were valid, we stop exploring the current order");
+                } else {
+                    STORM_LOG_INFO("    Created " << newAssumptions.size() << " assumptions, we continue extending the current order");
+                }
+
+                while (itr != newAssumptions.end()) {
+                    auto assumption = *itr;
+                    ++itr;
+                    if (assumption.second != AssumptionStatus::INVALID) {
+                        if (itr != newAssumptions.end()) {
+                            // We make a copy of the order and the assumptions
+                            auto orderCopy = order->copy();
+                            auto assumptionsCopy = std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>(assumptions);
+                            auto monResCopy = monRes->copy();
+
+                            if (assumption.second == AssumptionStatus::UNKNOWN) {
+                                // only add assumption to the set of assumptions if it is unknown whether it holds or not
+                                assumptionsCopy.push_back(std::move(assumption.first));
+                            }
+                            auto criticalTuple = extender->extendOrder(orderCopy, region, monResCopy, assumption.first);
+                            extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptionsCopy, monResCopy);
+                        } else {
+                            // It is the last one, so we don't need to create a copy.
+                            if (assumption.second == AssumptionStatus::UNKNOWN) {
+                                // only add assumption to the set of assumptions if it is unknown whether it holds or not
+                                assumptions.push_back(std::move(assumption.first));
+                            }
+                            auto criticalTuple = extender->extendOrder(order, region, monRes, assumption.first);
+                            extendOrderWithAssumptions(std::get<0>(criticalTuple), std::get<1>(criticalTuple), std::get<2>(criticalTuple), assumptions, monRes);
+                        }
+                    }
+                }
+            }
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples) {
+            assert (numberOfSamples > 2);
+
+            auto instantiator = utility::ModelInstantiator<models::sparse::Dtmc<ValueType>, models::sparse::Dtmc<ConstantType>>(*model);
+            std::set<VariableType> variables = models::sparse::getProbabilityParameters(*model);
+            std::vector<std::vector<ConstantType>> samples;
+            // For each of the variables create a model in which we only change the value for this specific variable
+            for (auto itr = variables.begin(); itr != variables.end(); ++itr) {
+                ConstantType previous = -1;
+                bool monDecr = true;
+                bool monIncr = true;
+
+                // Check monotonicity in variable (*itr) by instantiating the model
+                // all other variables fixed on lb, only increasing (*itr)
+                for (uint_fast64_t i = 0; (monDecr || monIncr) && i < numberOfSamples; ++i) {
+                    // Create valuation
+                    auto valuation = utility::parametric::Valuation<ValueType>();
+                    for (auto itr2 = variables.begin(); itr2 != variables.end(); ++itr2) {
+                        // Only change value for current variable
+                        if ((*itr) == (*itr2)) {
+                            auto lb = region.getLowerBoundary(itr->name());
+                            auto ub = region.getUpperBoundary(itr->name());
+                            // Creates samples between lb and ub, that is: lb, lb + (ub-lb)/(#samples -1), lb + 2* (ub-lb)/(#samples -1), ..., ub
+                            valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb + i*(ub-lb)/(numberOfSamples-1));
+                        } else {
+                            auto lb = region.getLowerBoundary(itr2->name());
+                            valuation[*itr2] = utility::convertNumber<typename utility::parametric::CoefficientType<ValueType>::type>(lb);
+                        }
+                    }
+
+                    // Instantiate model and get result
+                    models::sparse::Dtmc<ConstantType> sampleModel = instantiator.instantiate(valuation);
+                    auto checker = modelchecker::SparseDtmcPrctlModelChecker<models::sparse::Dtmc<ConstantType>>(sampleModel);
+                    std::unique_ptr<modelchecker::CheckResult> checkResult;
+                    auto formula = formulas[0];
+                    if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
+                        const modelchecker::CheckTask<logic::UntilFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::UntilFormula, ConstantType>((*formula).asProbabilityOperatorFormula().getSubformula().asUntilFormula());
+                        checkResult = checker.computeUntilProbabilities(Environment(), checkTask);
+                    } else if (formula->isProbabilityOperatorFormula() && formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()) {
+                        const modelchecker::CheckTask<logic::EventuallyFormula, ConstantType> checkTask = modelchecker::CheckTask<logic::EventuallyFormula, ConstantType>((*formula).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula());
+                        checkResult = checker.computeReachabilityProbabilities(Environment(), checkTask);
+                    } else {
+                        STORM_LOG_THROW(false, exceptions::NotSupportedException, "Expecting until or eventually formula");
+                    }
+
+                    auto quantitativeResult = checkResult->asExplicitQuantitativeCheckResult<ConstantType>();
+                    std::vector<ConstantType> values = quantitativeResult.getValueVector();
+                    auto initialStates = model->getInitialStates();
+                    ConstantType initial = 0;
+                    // Get total probability from initial states
+                    for (auto j = initialStates.getNextSetIndex(0); j < model->getNumberOfStates(); j = initialStates.getNextSetIndex(j + 1)) {
+                        initial += values[j];
+                    }
+                    // Calculate difference with result for previous valuation
+                    assert (initial >= 0 - precision && initial <= 1 + precision);
+                    ConstantType diff = previous - initial;
+                    assert (previous == -1 || diff >= -1 - precision && diff <= 1 + precision);
+
+                    if (previous != -1 && (diff > precision || diff < -precision)) {
+                        monDecr &= diff > precision; // then previous value is larger than the current value from the initial states
+                        monIncr &= diff < -precision;
+                    }
+                    previous = initial;
+                    samples.push_back(std::move(values));
+                }
+                auto res = (!monIncr && !monDecr) ? MonotonicityResult<VariableType>::Monotonicity::Not : MonotonicityResult<VariableType>::Monotonicity::Unknown;
+                resultCheckOnSamples.addMonotonicityResult(*itr, res);
+            }
+            assumptionMaker.setSampleValues(std::move(samples));
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void MonotonicityHelper<ValueType, ConstantType>::checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples) {
+           STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Checking monotonicity on samples not implemented for mdps");
+        }
+
+        template class MonotonicityHelper<RationalFunction, double>;
+        template class MonotonicityHelper<RationalFunction, RationalNumber>;
+    }
+}
diff --git a/src/storm-pars/analysis/MonotonicityHelper.h b/src/storm-pars/analysis/MonotonicityHelper.h
new file mode 100644
index 000000000..2ebfcf594
--- /dev/null
+++ b/src/storm-pars/analysis/MonotonicityHelper.h
@@ -0,0 +1,171 @@
+#ifndef STORM_MONOTONICITYHELPER_H
+#define STORM_MONOTONICITYHELPER_H
+
+#include <map>
+#include "Order.h"
+#include "LocalMonotonicityResult.h"
+#include "OrderExtender.h"
+#include "AssumptionMaker.h"
+#include "MonotonicityResult.h"
+
+
+#include "storm/logic/Formula.h"
+
+#include "storm/models/ModelBase.h"
+#include "storm/models/sparse/Dtmc.h"
+#include "storm/models/sparse/Mdp.h"
+
+#include "storm/solver/Z3SmtSolver.h"
+
+#include "storm/storage/SparseMatrix.h"
+#include "storm/storage/expressions/BinaryRelationExpression.h"
+#include "storm/storage/expressions/ExpressionManager.h"
+#include "storm/storage/expressions/RationalFunctionToExpression.h"
+
+#include "storm/utility/constants.h"
+
+#include "storm-pars/api/region.h"
+
+namespace storm {
+    namespace analysis {
+
+        template <typename ValueType, typename ConstantType>
+        class MonotonicityHelper {
+
+        public:
+            typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
+            typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
+            typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+            typedef typename storage::ParameterRegion<ValueType> Region;
+
+            /*!
+             * Constructor of MonotonicityHelper.
+             *
+             * @param model The model considered.
+             * @param formulas The formulas considered.
+             * @param regions The regions to consider.
+             * @param numberOfSamples Number of samples taken for monotonicity checking, default 0,
+             *          if 0 then no check on samples is executed.
+             * @param precision Precision on which the samples are compared
+             * @param dotOutput Whether or not dot output should be generated for the ROs.
+             */
+            MonotonicityHelper(std::shared_ptr<models::sparse::Model<ValueType>> model, std::vector<std::shared_ptr<logic::Formula const>> formulas, std::vector<storage::ParameterRegion<ValueType>> regions, uint_fast64_t numberOfSamples=0, double const& precision=0.000001, bool dotOutput = false);
+
+            /*!
+             * Checks if a derivative >=0 or/and <=0
+             *
+             * @param derivative The derivative you want to check
+             * @return pair of bools, >= 0 and <= 0
+             */
+            static std::pair<bool, bool> checkDerivative(ValueType derivative, storage::ParameterRegion<ValueType> reg) {
+                bool monIncr = false;
+                bool monDecr = false;
+
+                if (derivative.isZero()) {
+                    monIncr = true;
+                    monDecr = true;
+                } else if (derivative.isConstant()) {
+                    monIncr = derivative.constantPart() >= 0;
+                    monDecr = derivative.constantPart() <= 0;
+                } else {
+
+                    std::shared_ptr<utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<utility::solver::MathsatSmtSolverFactory>();
+                    std::shared_ptr<expressions::ExpressionManager> manager(new expressions::ExpressionManager());
+                    solver::Z3SmtSolver s(*manager);
+                    std::set<VariableType> variables = derivative.gatherVariables();
+
+                    expressions::Expression exprBounds = manager->boolean(true);
+                    for (auto variable : variables) {
+                        auto managerVariable = manager->declareRationalVariable(variable.name());
+                        auto lb = utility::convertNumber<RationalNumber>(reg.getLowerBoundary(variable));
+                        auto ub = utility::convertNumber<RationalNumber>(reg.getUpperBoundary(variable));
+                        exprBounds = exprBounds && manager->rational(lb) < managerVariable && managerVariable < manager->rational(ub);
+                    }
+
+                    auto converter = expressions::RationalFunctionToExpression<ValueType>(manager);
+
+                    // < 0, so not monotone increasing. If this is unsat, then it should be monotone increasing.
+                    expressions::Expression exprToCheck = converter.toExpression(derivative) < manager->rational(0);
+                    s.add(exprBounds);
+                    s.add(exprToCheck);
+                    monIncr = s.check() == solver::SmtSolver::CheckResult::Unsat;
+
+                    // > 0, so not monotone decreasing. If this is unsat it should be monotone decreasing.
+                    exprToCheck = converter.toExpression(derivative) > manager->rational(0);
+                    s.reset();
+                    s.add(exprBounds);
+                    s.add(exprToCheck);
+                    monDecr = s.check() == solver::SmtSolver::CheckResult::Unsat;
+                }
+                assert (!(monIncr && monDecr) || derivative.isZero());
+
+                return std::pair<bool, bool>(monIncr, monDecr);
+            }
+
+            /*!
+             * Builds Reachability Orders for the given model and simultaneously uses them to check for Monotonicity.
+             *
+             * @param outfile Outfile to which results are written.
+             * @param dotOutfileName Name for the files of the dot outputs should they be generated
+             * @return Map which maps each order to its Reachability Order and used assumptions.
+             */
+            std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> checkMonotonicityInBuild(std::ostream& outfile, bool usePLA = false, std::string dotOutfileName = "dotOutput");
+
+            /*!
+             * Checks for local monotonicity at the given state.
+             *
+             * @param order the order on which the monotonicity should be checked
+             * @param state the considerd state
+             * @param var the variable in which we check for monotonicity
+             * @param region the region on which we check the monotonicity
+             * @return Incr, Decr, Constant, Unknown or Not
+             */
+            Monotonicity checkLocalMonotonicity(std::shared_ptr<Order> order, uint_fast64_t state, VariableType var, storage::ParameterRegion<ValueType> region);
+
+        private:
+            void createOrder();
+
+            void checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Dtmc<ValueType>> model, uint_fast64_t numberOfSamples);
+
+            void checkMonotonicityOnSamples(std::shared_ptr<models::sparse::Mdp<ValueType>> model, uint_fast64_t numberOfSamples);
+
+            void extendOrderWithAssumptions(std::shared_ptr<Order> order, uint_fast64_t val1, uint_fast64_t val2, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>> assumptions, std::shared_ptr<MonotonicityResult<VariableType>> monRes);
+
+            Monotonicity checkTransitionMonRes(ValueType function, VariableType param, Region region);
+
+            ValueType getDerivative(ValueType function, VariableType var);
+
+
+            std::shared_ptr<models::ModelBase> model;
+
+            std::vector<std::shared_ptr<logic::Formula const>> formulas;
+
+            bool dotOutput;
+
+            bool checkSamples;
+
+            bool onlyCheckOnOrder;
+
+            MonotonicityResult<VariableType> resultCheckOnSamples;
+
+            std::map<VariableType, std::vector<uint_fast64_t>> occuringStatesAtVariable;
+
+            std::map<std::shared_ptr<Order>, std::pair<std::shared_ptr<MonotonicityResult<VariableType>>, std::vector<std::shared_ptr<expressions::BinaryRelationExpression>>>> monResults;
+
+            OrderExtender<ValueType, ConstantType> *extender;
+
+            ConstantType precision;
+
+            Region region;
+
+            storage::SparseMatrix<ValueType> matrix;
+
+            std::unordered_map<ValueType, std::unordered_map<VariableType, ValueType>> derivatives;
+
+            AssumptionMaker<ValueType, ConstantType> assumptionMaker;
+
+
+        };
+    }
+}
+#endif //STORM_MONOTONICITYHELPER_H
diff --git a/src/storm-pars/analysis/MonotonicityResult.cpp b/src/storm-pars/analysis/MonotonicityResult.cpp
new file mode 100644
index 000000000..5a7e678c5
--- /dev/null
+++ b/src/storm-pars/analysis/MonotonicityResult.cpp
@@ -0,0 +1,231 @@
+#include "MonotonicityResult.h"
+
+#include "storm/utility/macros.h"
+#include "storm/exceptions/NotImplementedException.h"
+#include "storm/models/sparse/Dtmc.h"
+#include "storm/models/sparse/Mdp.h"
+
+namespace storm {
+    namespace analysis {
+
+        template <typename VariableType>
+        MonotonicityResult<VariableType>::MonotonicityResult() {
+            this->done = false;
+            this->somewhereMonotonicity = true;
+            this->allMonotonicity = true;
+        }
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::addMonotonicityResult(VariableType var,  MonotonicityResult<VariableType>::Monotonicity mon) {
+            monotonicityResult.insert(std::pair<VariableType, MonotonicityResult<VariableType>::Monotonicity>(std::move(var), std::move(mon)));
+        }
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::updateMonotonicityResult(VariableType var, MonotonicityResult<VariableType>::Monotonicity mon, bool force) {
+            assert (!isDoneForVar(var));
+            if (force) {
+                assert (mon == MonotonicityResult<VariableType>::Monotonicity::Not);
+                if (monotonicityResult.find(var) == monotonicityResult.end()) {
+                    addMonotonicityResult(std::move(var), std::move(mon));
+                } else {
+                    monotonicityResult[var] = mon;
+                }
+            } else {
+                if (mon == MonotonicityResult<VariableType>::Monotonicity::Not) {
+                    mon = MonotonicityResult<VariableType>::Monotonicity::Unknown;
+                }
+
+                if (monotonicityResult.find(var) == monotonicityResult.end()) {
+                    addMonotonicityResult(std::move(var), std::move(mon));
+                } else {
+                    auto monRes = monotonicityResult[var];
+                    if (monRes == MonotonicityResult<VariableType>::Monotonicity::Unknown || monRes == mon ||
+                        mon == MonotonicityResult<VariableType>::Monotonicity::Constant) {
+                        return;
+                    } else if (mon == MonotonicityResult<VariableType>::Monotonicity::Unknown ||
+                               monRes == MonotonicityResult<VariableType>::Monotonicity::Constant) {
+                        monotonicityResult[var] = mon;
+                    } else {
+                        monotonicityResult[var] = MonotonicityResult<VariableType>::Monotonicity::Unknown;
+                    }
+                }
+                if (monotonicityResult[var] == MonotonicityResult<VariableType>::Monotonicity::Unknown) {
+                    setAllMonotonicity(false);
+                    setSomewhereMonotonicity(false);
+                } else {
+                    setSomewhereMonotonicity(true);
+                }
+            }
+        }
+
+        template <typename VariableType>
+        typename MonotonicityResult<VariableType>::Monotonicity MonotonicityResult<VariableType>::getMonotonicity(VariableType var) const {
+            auto itr = monotonicityResult.find(var);
+            if (itr != monotonicityResult.end()) {
+                return itr->second;
+            }
+            return Monotonicity::Unknown;
+        }
+
+        template <typename VariableType>
+        std::map<VariableType, typename MonotonicityResult<VariableType>::Monotonicity> MonotonicityResult<VariableType>::getMonotonicityResult() const {
+            return monotonicityResult;
+        }
+
+        template <typename VariableType>
+        std::pair<std::set<VariableType>, std::set<VariableType>> MonotonicityResult<VariableType>::splitVariables(std::set<VariableType> const& consideredVariables) const {
+            std::set<VariableType> nonMonotoneVariables;
+            std::set<VariableType> monotoneVariables;
+            for (auto var : consideredVariables) {
+                if (isDoneForVar(var)) {
+                    auto res = getMonotonicity(var);
+                    if (res == Monotonicity::Not || res == Monotonicity::Unknown) {
+                        nonMonotoneVariables.insert(var);
+                    } else {
+                        monotoneVariables.insert(var);
+                    }
+                } else {
+                    nonMonotoneVariables.insert(var);
+                }
+            }
+            return std::make_pair(std::move(monotoneVariables), std::move(nonMonotoneVariables));
+        }
+
+        template <typename VariableType>
+        std::string MonotonicityResult<VariableType>::toString() const {
+            std::string result;
+            auto countIncr = 0;
+            auto countDecr = 0;
+            for (auto res : getMonotonicityResult()) {
+                result += res.first.name();
+                switch (res.second) {
+                    case MonotonicityResult<VariableType>::Monotonicity::Incr:
+                        countIncr++;
+                        result += " MonIncr; ";
+                        break;
+                    case MonotonicityResult<VariableType>::Monotonicity::Decr:
+                        countDecr++;
+                        result += " MonDecr; ";
+                        break;
+                    case MonotonicityResult<VariableType>::Monotonicity::Constant:
+                        result += " Constant; ";
+                        break;
+                    case MonotonicityResult<VariableType>::Monotonicity::Not:
+                        result += " NotMon; ";
+                        break;
+                    case MonotonicityResult<VariableType>::Monotonicity::Unknown:
+                        result += " Unknown; ";
+                        break;
+                    default:
+                        STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Could not get a string from the region monotonicity check result. The case has not been implemented");
+                }
+            }
+            result = "#Incr: " + std::to_string(countIncr) + " #Decr: " + std::to_string(countDecr) + "\n" + result;
+            return result;
+        }
+
+
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::setDone(bool done) {
+            this->done = done;
+        }
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::setDoneForVar(VariableType variable) {
+            doneVariables.insert(variable);
+        }
+
+        template <typename VariableType>
+        bool MonotonicityResult<VariableType>::isDone() const {
+            return done;
+        }
+
+        template <typename VariableType>
+        bool MonotonicityResult<VariableType>::isDoneForVar(VariableType var) const {
+            return doneVariables.find(var) != doneVariables.end();
+        }
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::setSomewhereMonotonicity(bool somewhereMonotonicity) {
+            this->somewhereMonotonicity = somewhereMonotonicity;
+        }
+
+        template <typename VariableType>
+        bool MonotonicityResult<VariableType>::existsMonotonicity() {
+            if (!somewhereMonotonicity) {
+
+                for (auto itr : monotonicityResult) {
+                    if (isDoneForVar(itr.first) && itr.second != MonotonicityResult<VariableType>::Monotonicity::Unknown && itr.second != MonotonicityResult<VariableType>::Monotonicity::Not) {
+                        setSomewhereMonotonicity(true);
+                        break;
+                    }
+                }
+            }
+            return monotonicityResult.size() > 0 && somewhereMonotonicity;
+        }
+
+        template <typename VariableType>
+        void MonotonicityResult<VariableType>::setAllMonotonicity(bool allMonotonicity) {
+            this->allMonotonicity = allMonotonicity;
+        }
+
+        template <typename VariableType>
+        bool MonotonicityResult<VariableType>::isAllMonotonicity() const {
+            return allMonotonicity;
+        }
+
+        template <typename VariableType>
+        std::shared_ptr<MonotonicityResult<VariableType>> MonotonicityResult<VariableType>::copy() const {
+            std::shared_ptr<MonotonicityResult<VariableType>> copy = std::make_shared<MonotonicityResult<VariableType>>();
+            copy->monotonicityResult = std::map<VariableType, Monotonicity>(monotonicityResult);
+            copy->setAllMonotonicity(allMonotonicity);
+            copy->setSomewhereMonotonicity(somewhereMonotonicity);
+            copy->setDone(done);
+            copy->setDoneVariables(doneVariables);
+            return copy;
+        }
+
+        template<typename VariableType>
+        void MonotonicityResult<VariableType>::setDoneVariables(std::set<VariableType> doneVariables) {
+            this->doneVariables = doneVariables;
+        }
+
+        template<typename VariableType>
+        void
+        MonotonicityResult<VariableType>::splitBasedOnMonotonicity(const std::set<VariableType> &consideredVariables,
+                                                                   std::set<VariableType> & monotoneIncr,
+                                                                   std::set<VariableType> & monotoneDecr,
+                                                                   std::set<VariableType> & notMonotone) const {
+            for (auto& var : consideredVariables) {
+                if (!isDoneForVar(var)) {
+                    notMonotone.insert(var);
+                } else {
+                    auto mon = getMonotonicity(var);
+                    if (mon == Monotonicity::Unknown || mon == Monotonicity::Not) {
+                        notMonotone.insert(var);
+                    } else if (mon == Monotonicity::Incr) {
+                        monotoneIncr.insert(var);
+                    } else {
+                        monotoneDecr.insert(var);
+                    }
+                }
+            }
+
+        }
+
+        template<typename VariableType>
+        bool MonotonicityResult<VariableType>::isMonotone(VariableType var) const {
+            if (monotonicityResult.find(var) == monotonicityResult.end()) {
+                return false;
+            } else {
+                auto monRes = monotonicityResult.at(var);
+                return isDoneForVar(var) && (monRes == Monotonicity::Incr
+                                             || monRes == Monotonicity::Decr
+                                             || monRes == Monotonicity::Constant);
+            }
+        }
+
+        template class MonotonicityResult<storm::RationalFunctionVariable>;
+    }
+}
diff --git a/src/storm-pars/analysis/MonotonicityResult.h b/src/storm-pars/analysis/MonotonicityResult.h
new file mode 100644
index 000000000..6ec70945c
--- /dev/null
+++ b/src/storm-pars/analysis/MonotonicityResult.h
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <ostream>
+#include <map>
+#include <set>
+
+#include <memory>
+#include <storm/storage/BitVector.h>
+
+namespace storm {
+    namespace analysis {
+        template <typename VariableType>
+        class MonotonicityResult {
+        public:
+
+            /*!
+             * The results of monotonicity checking for a single Parameter Region
+             */
+            enum class Monotonicity {
+                Incr, /*!< the result is monotonically increasing */
+                Decr, /*!< the result is monotonically decreasing */
+                Constant, /*!< the result is constant */
+                Not, /*!< the result is not monotonic */
+                Unknown /*!< the monotonicity result is unknown */
+            };
+
+            /*!
+             * Constructs a new MonotonicityResult object.
+             */
+            MonotonicityResult();
+
+            /*!
+             * Adds a new variable with a given Monotonicity to the map.
+             *
+             * @param var The variable that is to be added.
+             * @param mon The Monotonicity of the variable.
+             */
+            void addMonotonicityResult(VariableType var, Monotonicity mon);
+
+
+            /*!
+             * Updates the Monotonicity of a variable based on its value so far and a new value.
+             *
+             * @param var The variable.
+             * @param mon The new Monotonicity to be considered.
+             */
+            void updateMonotonicityResult(VariableType var, Monotonicity mon, bool force = false);
+
+            /*!
+             * Returns the  current monotonicity of a given parameter.
+             *
+             * @param var The parameter.
+             * @return Incr, Decr, Constant, Not or Unknown.
+             */
+            Monotonicity getMonotonicity(VariableType var) const;
+            bool isMonotone(VariableType var) const;
+
+            /*!
+             * Returns the results so far.
+             *
+             * @return The parameter / Monotonicity map
+             */
+            std::map<VariableType, Monotonicity> getMonotonicityResult() const;
+
+
+            void splitBasedOnMonotonicity(std::set<VariableType> const& consideredVariables, std::set<VariableType>& monotoneIncr, std::set<VariableType>& monotoneDecr, std::set<VariableType> & notMontone) const;
+
+
+            std::pair<std::set<VariableType>, std::set<VariableType>> splitVariables(std::set<VariableType> const& consideredVariables) const;
+            /*!
+             * Constructs a string output of all variables and their corresponding Monotonicity
+             *
+             * @return Results so far
+             */
+            std::string toString() const;
+
+            /*!
+             * Checks if the result is complete
+             */
+            bool isDone() const;
+
+            bool isDoneForVar(VariableType) const;
+
+            /*!
+             * Checks if there is any variable that is monotone
+             */
+            bool existsMonotonicity();
+
+            /*!
+             * Returns if all Variables are monotone
+             */
+            bool isAllMonotonicity() const;
+
+            /*!
+             * Sets the done bool to the given truth value
+             */
+            void setDone(bool done = true);
+
+            /*!
+             * Sets the somewhereMonotonicity bool to the given truth value
+             */
+            void setSomewhereMonotonicity(bool done = true);
+
+            /*!
+             * Sets the allMonotonicity bool to the given truth value
+             */
+            void setAllMonotonicity(bool done = true);
+
+            void setDoneForVar(VariableType);
+
+            void setDoneVariables(std::set<VariableType> doneVariables);
+
+            /*!
+             * Constructs a new MonotonicityResult object that is a copy of the current one
+             *
+             * @return Pointer to the copy
+             */
+            std::shared_ptr<MonotonicityResult<VariableType>> copy() const;
+
+        private:
+            std::map<VariableType, Monotonicity> monotonicityResult;
+            std::set<VariableType> doneVariables;
+            bool done;
+            bool somewhereMonotonicity;
+            bool allMonotonicity;
+        };
+
+    }
+}
+
diff --git a/src/storm-pars/analysis/Order.cpp b/src/storm-pars/analysis/Order.cpp
index 150d2711d..b5e87244d 100644
--- a/src/storm-pars/analysis/Order.cpp
+++ b/src/storm-pars/analysis/Order.cpp
@@ -1,313 +1,681 @@
+#include "Order.h"
+
 #include <iostream>
-#include <fstream>
-#include <algorithm>
+#include <storm/utility/macros.h>
+#include <queue>
 
-#include "Order.h"
 
 namespace storm {
     namespace analysis {
-        Order::Order(storm::storage::BitVector* topStates,
-                         storm::storage::BitVector* bottomStates,
-                         storm::storage::BitVector* initialMiddleStates,
-                         uint_fast64_t numberOfStates,
-                         std::vector<uint_fast64_t>* statesSorted) {
-            nodes = std::vector<Node *>(numberOfStates);
-
-            this->numberOfStates = numberOfStates;
-            this->addedStates = new storm::storage::BitVector(numberOfStates);
-            this->doneBuilding = false;
-            assert (statesSorted != nullptr);
-            this->statesSorted = *statesSorted;
-            this->statesToHandle = initialMiddleStates;
-
-            top = new Node();
-            bottom = new Node();
-
-            top->statesAbove = storm::storage::BitVector(numberOfStates);
-            bottom->statesAbove = storm::storage::BitVector(numberOfStates);
-
+        Order::Order(storm::storage::BitVector* topStates, storm::storage::BitVector* bottomStates, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> decomposition, std::vector<uint_fast64_t> statesSorted) {
+            init(numberOfStates, decomposition);
+            this->numberOfAddedStates = 0;
+            this->onlyBottomTopOrder = true;
             for (auto const& i : *topStates) {
-                addedStates->set(i);
-                bottom->statesAbove.set(i);
-                top->states.insert(i);
-                nodes[i] = top;
+                this->doneStates.set(i);
+                this->bottom->statesAbove.set(i);
+                this->top->states.insert(i);
+                this->nodes[i] = top;
+                numberOfAddedStates++;
             }
+            this->statesSorted = statesSorted;
 
             for (auto const& i : *bottomStates) {
-                addedStates->set(i);
-                bottom->states.insert(i);
-                nodes[i] = bottom;
+                this->doneStates.set(i);
+                this->bottom->states.insert(i);
+                this->nodes[i] = bottom;
+                numberOfAddedStates++;
             }
-
-            for (auto const &state : *initialMiddleStates) {
-                add(state);
+            assert (numberOfAddedStates <= numberOfStates);
+            assert (doneStates.getNumberOfSetBits() == (topStates->getNumberOfSetBits() + bottomStates->getNumberOfSetBits()));
+            if (numberOfAddedStates == numberOfStates) {
+                doneBuilding = doneStates.full();
             }
         }
 
-        Order::Order(uint_fast64_t topState, uint_fast64_t bottomState, uint_fast64_t numberOfStates, std::vector<uint_fast64_t>* statesSorted) {
-            nodes = std::vector<Node *>(numberOfStates);
+        Order::Order(uint_fast64_t topState, uint_fast64_t bottomState, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> decomposition, std::vector<uint_fast64_t> statesSorted) {
+            init(numberOfStates, decomposition);
 
-            this->numberOfStates = numberOfStates;
-            this->addedStates = new storm::storage::BitVector(numberOfStates);
-            this->doneBuilding = false;
-            this->statesSorted = *statesSorted;
-            this->statesToHandle = new storm::storage::BitVector(numberOfStates);
-
-            top = new Node();
-            bottom = new Node();
-
-            top->statesAbove = storm::storage::BitVector(numberOfStates);
-            bottom->statesAbove = storm::storage::BitVector(numberOfStates);
-
-            addedStates->set(topState);
-            bottom->statesAbove.set(topState);
-            top->states.insert(topState);
-            nodes[topState] = top;
-
-            addedStates->set(bottomState);
-            bottom->states.insert(bottomState);
-            nodes[bottomState] = bottom;
-            assert (addedStates->getNumberOfSetBits() == 2);
-        }
-
-        Order::Order(Order* order) {
-            numberOfStates = order->getAddedStates()->size();
-            nodes = std::vector<Node *>(numberOfStates);
-            addedStates = new storm::storage::BitVector(numberOfStates);
-            this->doneBuilding = order->getDoneBuilding();
-
-            auto oldNodes = order->getNodes();
-            // Create nodes
-            for (auto itr = oldNodes.begin(); itr != oldNodes.end(); ++itr) {
-                Node *oldNode = (*itr);
-                if (oldNode != nullptr) {
-                    Node *newNode = new Node();
-                    newNode->states = oldNode->states;
-                    for (auto const& i : newNode->states) {
-                        addedStates->set(i);
-                        nodes[i] = newNode;
-                    }
-                    if (oldNode == order->getTop()) {
-                        top = newNode;
-                    } else if (oldNode == order->getBottom()) {
-                        bottom = newNode;
-                    }
-                }
-            }
-            assert(*addedStates == *(order->getAddedStates()));
+            this->onlyBottomTopOrder = true;
+            this->doneStates.set(topState);
 
-            // set all states above and below
-            for (auto itr = oldNodes.begin(); itr != oldNodes.end(); ++itr) {
-                Node *oldNode = (*itr);
-                if (oldNode != nullptr) {
-                    Node *newNode = getNode(*(oldNode->states.begin()));
-                    newNode->statesAbove = storm::storage::BitVector((oldNode->statesAbove));
-                }
-            }
+            this->bottom->statesAbove.set(topState);
+            this->top->states.insert(topState);
+            this->nodes[topState] = top;
+
+            this->doneStates.set(bottomState);
+
+            this->bottom->states.insert(bottomState);
+            this->nodes[bottomState] = bottom;
+            this->numberOfAddedStates = 2;
+            assert (numberOfAddedStates <= numberOfStates);
 
-            auto statesSortedOrder = order->getStatesSorted();
-            for (auto itr = statesSortedOrder.begin(); itr != statesSortedOrder.end(); ++itr) {
-                this->statesSorted.push_back(*itr);
+            this->statesSorted = statesSorted;
+            assert (doneStates.getNumberOfSetBits() == 2);
+            if (numberOfAddedStates == numberOfStates) {
+                doneBuilding = doneStates.full();
             }
-            this->statesToHandle = new storm::storage::BitVector(*(order->statesToHandle));
         }
 
-        void Order::addBetween(uint_fast64_t state, Node *above, Node *below) {
-            assert(!(*addedStates)[state]);
-            assert(compare(above, below) == ABOVE);
+        Order::Order(){
+            this->invalid = false;
+        }
+
+        /*** Modifying the order ***/
+
+        void Order::add(uint_fast64_t state) {
+            assert (nodes[state] == nullptr);
+            addBetween(state, top, bottom);
+            addStateToHandle(state);
+        }
+
+        void Order::addAbove(uint_fast64_t state, Node *node) {
+            STORM_LOG_INFO("Add " << state << " above " << *node->states.begin() << std::endl);
+
+            assert (nodes[state] == nullptr);
             Node *newNode = new Node();
             nodes[state] = newNode;
 
             newNode->states.insert(state);
-            newNode->statesAbove = storm::storage::BitVector((above->statesAbove));
-            for (auto const& state : above->states) {
+            newNode->statesAbove = storm::storage::BitVector(numberOfStates, false);
+            for (auto const &state : top->states) {
                 newNode->statesAbove.set(state);
             }
-            below->statesAbove.set(state);
-            addedStates->set(state);
+            node->statesAbove.set(state);
+            numberOfAddedStates++;
+            onlyBottomTopOrder = false;
+            if (numberOfAddedStates == numberOfStates) {
+                doneBuilding = doneStates.full();
+            }
+
         }
 
-        void Order::addBetween(uint_fast64_t state, uint_fast64_t above, uint_fast64_t below) {
-            assert(!(*addedStates)[state]);
+        void Order::addBelow(uint_fast64_t state, Node *node) {
+            STORM_LOG_INFO("Add " << state << " below " << *node->states.begin()<< std::endl);
+
+            assert (nodes[state] == nullptr);
+            Node *newNode = new Node();
+            nodes[state] = newNode;
+            newNode->states.insert(state);
+            newNode->statesAbove = storm::storage::BitVector((node->statesAbove));
+            for (auto statesAbove : node->states) {
+                newNode->statesAbove.set(statesAbove);
+            }
+            bottom->statesAbove.set(state);
+            numberOfAddedStates++;
+            onlyBottomTopOrder = false;
+            if (numberOfAddedStates == numberOfStates) {
+                doneBuilding = doneStates.full();
+            }
+            assert (numberOfAddedStates <= numberOfStates);
+
+        }
+
+        void Order::addBetween(uint_fast64_t state, Node *above, Node *below) {
+            STORM_LOG_INFO("Add " << state << " between (above) " << *above->states.begin() << " and " << *below->states.begin() << std::endl);
+
             assert(compare(above, below) == ABOVE);
+            assert (above != nullptr && below != nullptr);
+            if (nodes[state] == nullptr) {
+                // State is not in the order yet
+                Node *newNode = new Node();
+                nodes[state] = newNode;
+
+                newNode->states.insert(state);
+                newNode->statesAbove = storm::storage::BitVector(above->statesAbove);
+                for (auto aboveStates : above->states) {
+                    newNode->statesAbove.set(aboveStates);
+                }
+                below->statesAbove.set(state);
+                numberOfAddedStates++;
+                onlyBottomTopOrder = false;
+                if (numberOfAddedStates == numberOfStates) {
+                    doneBuilding = doneStates.full();
+                }
+                assert (numberOfAddedStates <= numberOfStates);
+            } else {
+                // State is in the order already, so we add the new relations
+                addRelationNodes(above, nodes[state]);
+                addRelationNodes(nodes[state], below);
+            }
+        }
 
+        void Order::addBetween(uint_fast64_t state, uint_fast64_t above, uint_fast64_t below) {
+            assert (compare(above, below) == ABOVE);
             assert (getNode(below)->states.find(below) != getNode(below)->states.end());
             assert (getNode(above)->states.find(above) != getNode(above)->states.end());
-            addBetween(state, getNode(above), getNode(below));
 
+            addBetween(state, getNode(above), getNode(below));
         }
 
-        void Order::addToNode(uint_fast64_t state, Node *node) {
-            assert(!(*addedStates)[state]);
-            node->states.insert(state);
-            nodes[state] = node;
-            addedStates->set(state);
+        void Order::addRelation(uint_fast64_t above, uint_fast64_t below, bool allowMerge) {
+            assert (getNode(above) != nullptr && getNode(below) != nullptr);
+            addRelationNodes(getNode(above), getNode(below), allowMerge);
         }
 
-        void Order::add(uint_fast64_t state) {
-            assert(!(*addedStates)[state]);
-            addBetween(state, top, bottom);
-        }
+        void Order::addRelationNodes(Order::Node *above, Order::Node * below, bool allowMerge) {
+            assert (allowMerge || compare(above, below) != BELOW);
 
-        void Order::addRelationNodes(Order::Node *above, Order::Node * below) {
-            assert (compare(above, below) == UNKNOWN);
-            for (auto const& state : above->states) {
+            STORM_LOG_INFO("Add relation between (above) " << *above->states.begin() << " and " << *below->states.begin() << std::endl);
+
+            if (allowMerge) {
+                if (compare(below, above) == ABOVE) {
+                    mergeNodes(above, below);
+                    return;
+                }
+            }
+            below->statesAbove |= ((above->statesAbove));
+            for (auto const &state : above->states) {
                 below->statesAbove.set(state);
             }
-            below->statesAbove|=((above->statesAbove));
             assert (compare(above, below) == ABOVE);
         }
 
-        void Order::addRelation(uint_fast64_t above, uint_fast64_t below) {
-            addRelationNodes(getNode(above), getNode(below));
+        void Order::addToNode(uint_fast64_t state, Node *node) {
+            STORM_LOG_INFO("Add "<< state << " to between (above) " << *node->states.begin() << std::endl);
+
+            if (nodes[state] == nullptr) {
+                // State is not in the order yet
+                node->states.insert(state);
+                nodes[state] = node;
+                numberOfAddedStates++;
+                if (numberOfAddedStates == numberOfStates) {
+                    doneBuilding = doneStates.full();
+                }
+                assert (numberOfAddedStates <= numberOfStates);
+
+            } else {
+                // State is in the order already, so we merge the nodes
+                mergeNodes(nodes[state], node);
+            }
+        }
+
+        bool Order::mergeNodes(storm::analysis::Order::Node *node1, storm::analysis::Order::Node *node2) {
+            STORM_LOG_INFO("Merge " << *node1->states.begin() << " and " << *node2->states.begin() << std::endl);
+
+            // Merges node2 into node 1
+            // everything above n2 also above n1
+            node1->statesAbove |= ((node2->statesAbove));
+            // add states of node 2 to node 1
+            node1->states.insert(node2->states.begin(), node2->states.end());
+
+            for(auto const& i : node2->states) {
+                nodes[i] = node1;
+            }
+
+            for (auto const& node : nodes) {
+                if (node != nullptr) {
+                    for (auto state2 : node2->states) {
+                        if (node->statesAbove[state2]) {
+                            for (auto state1 : node1->states) {
+                                node->statesAbove.set(state1);
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+            for (auto i = 0; i < numberOfStates; ++i) {
+                for (auto j= i + 1; j < numberOfStates; ++j) {
+                    auto comp1 = compare(i,j);
+                    auto comp2 = compare(j,i);
+                    if (!((comp1 == BELOW && comp2 == ABOVE ) ||
+                                                (comp1 == ABOVE && comp2 == BELOW) ||
+                                                (comp1 == UNKNOWN && comp2 == UNKNOWN) ||
+                                                (comp1 == SAME && comp2 == SAME))) {
+                        invalid =  true;
+                        return false;
+                    }
+
+                }
+            }
+            return !invalid;
+        }
+
+        bool Order::merge(uint_fast64_t var1, uint_fast64_t var2) {
+            return mergeNodes(getNode(var1), getNode(var2));
+        }
+
+        /*** Checking on the order ***/
+
+        Order::NodeComparison Order::compare(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis){
+            return  compare(getNode(state1), getNode(state2), hypothesis);
         }
 
-        Order::NodeComparison Order::compare(uint_fast64_t state1, uint_fast64_t state2) {
-            return compare(getNode(state1), getNode(state2));
+        Order::NodeComparison Order::compareFast(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis) const {
+            auto res = compareFast(getNode(state1), getNode(state2), hypothesis);
+            return res;
         }
 
-        Order::NodeComparison Order::compare(Node* node1, Node* node2) {
+        Order::NodeComparison Order::compareFast(Node* node1, Node* node2, NodeComparison hypothesis) const {
             if (node1 != nullptr && node2 != nullptr) {
                 if (node1 == node2) {
                     return SAME;
                 }
 
-                if (above(node1, node2)) {
-                    assert(!above(node2, node1));
+                if ((hypothesis == UNKNOWN || hypothesis == ABOVE) && ((node1 == top || node2 == bottom) || aboveFast(node1, node2))) {
                     return ABOVE;
                 }
 
-                if (above(node2, node1)) {
+                if ((hypothesis == UNKNOWN || hypothesis == BELOW) && ((node2 == top || node1 == bottom) || aboveFast(node2, node1))) {
                     return BELOW;
                 }
+            }  else if (node1 == top || node2 == bottom) {
+                return ABOVE;
+            } else if (node2 == top || node1 == bottom) {
+                return BELOW;
+            }
+            return UNKNOWN;
+        }
 
-                // tweak for cyclic pmcs
-                if (doneBuilding) {
-                    doneBuilding = false;
-                    if (above(node1, node2)) {
-                        assert(!above(node2, node1));
-                        doneBuilding = true;
-                        return ABOVE;
-                    }
-                    if (above(node2, node1)) {
-                        doneBuilding = true;
-                        return BELOW;
-                    }
+        Order::NodeComparison Order::compare(Node* node1, Node* node2, NodeComparison hypothesis) {
+            if (node1 != nullptr && node2 != nullptr) {
+                auto comp = compareFast(node1, node2, hypothesis);
+                if (comp != UNKNOWN) {
+                    return comp;
+                }
+                if ((hypothesis == UNKNOWN || hypothesis == ABOVE) && above(node1, node2)) {
+                    assert(!above(node2, node1));
+                    return ABOVE;
+                }
+
+                if ((hypothesis == UNKNOWN || hypothesis == BELOW) && above(node2, node1)) {
+                    return BELOW;
                 }
+            } else if (node1 == top || node2 == bottom) {
+                return ABOVE;
+            } else if (node2 == top || node1 == bottom) {
+                return BELOW;
             }
             return UNKNOWN;
         }
 
+        bool Order::contains(uint_fast64_t state) const {
+            return state < numberOfStates && nodes[state] != nullptr;
+        }
+
+        Order::Node *Order::getBottom() const {
+            return bottom;
+        }
+
+        bool Order::getDoneBuilding() const {
+            assert (!doneStates.full() || numberOfAddedStates == numberOfStates);
+            return doneStates.full();
+        }
 
-        bool Order::contains(uint_fast64_t state) {
-            return state < addedStates->size() && addedStates->get(state);
+        uint_fast64_t Order::getNextDoneState(uint_fast64_t state) const {
+            return doneStates.getNextSetIndex(state + 1);
         }
 
-        Order::Node *Order::getNode(uint_fast64_t stateNumber) {
+        Order::Node *Order::getNode(uint_fast64_t stateNumber) const {
             assert (stateNumber < numberOfStates);
-            return nodes.at(stateNumber);
+            return nodes[stateNumber];
         }
 
-        Order::Node *Order::getTop() {
+        std::vector<Order::Node*> Order::getNodes() const {
+            return nodes;
+        }
+
+        std::vector<uint_fast64_t>& Order::getStatesSorted() {
+            return statesSorted;
+        }
+
+        Order::Node *Order::getTop() const {
             return top;
         }
 
-        Order::Node *Order::getBottom() {
-            return bottom;
+        uint_fast64_t Order::getNumberOfAddedStates() const {
+            return numberOfAddedStates;
         }
 
-        std::vector<Order::Node*> Order::getNodes() {
-            return nodes;
+        uint_fast64_t Order::getNumberOfStates() const {
+            return numberOfStates;
         }
 
-        storm::storage::BitVector* Order::getAddedStates() {
-            return addedStates;
+        bool Order::isBottomState(uint_fast64_t state) const {
+            auto states = bottom->states;
+            return states.find(state) != states.end();
         }
 
-        bool Order::getDoneBuilding() {
-            return doneBuilding;
+        bool Order::isTopState(uint_fast64_t state) const {
+            auto states = top->states;
+            return states.find(state) != states.end();
         }
 
-        void Order::setDoneBuilding(bool done) {
-            doneBuilding = done;
+        bool Order::isOnlyBottomTopOrder() const {
+            return onlyBottomTopOrder;
         }
 
-        std::vector<uint_fast64_t> Order::sortStates(storm::storage::BitVector* states) {
-            uint_fast64_t numberOfSetBits = states->getNumberOfSetBits();
-            auto stateSize = states->size();
-            auto result = std::vector<uint_fast64_t>(numberOfSetBits, stateSize);
+        std::vector<uint_fast64_t> Order::sortStates(std::vector<uint_fast64_t>* states) {
+            assert (states != nullptr);
+            uint_fast64_t numberOfStatesToSort = states->size();
+            std::vector<uint_fast64_t> result;
+            // Go over all states
             for (auto state : *states) {
-                if (result[0] == stateSize) {
-                    result[0] = state;
+                bool unknown = false;
+                if (result.size() == 0) {
+                    result.push_back(state);
                 } else {
-                    uint_fast64_t i = 0;
                     bool added = false;
-                    while (i < numberOfSetBits && !added) {
-                        if (result[i] == stateSize) {
-                            result[i] = state;
+                    for (auto itr = result.begin();  itr != result.end(); ++itr) {
+                        auto compareRes = compare(state, (*itr));
+                        if (compareRes == ABOVE || compareRes == SAME) {
+                            // insert at current pointer (while keeping other values)
+                            result.insert(itr, state);
                             added = true;
-                        } else {
-                            auto compareRes = compare(state, result[i]);
-                            if (compareRes == ABOVE) {
-                                auto temp = result[i];
-                                result[i] = state;
-                                for (uint_fast64_t j = i + 1; j < numberOfSetBits && result[j + 1] != stateSize; j++) {
-                                    auto temp2 = result[j];
-                                    result[j] = temp;
-                                    temp = temp2;
-                                }
-                                added = true;
-                            } else if (compareRes == UNKNOWN) {
-                                break;
-                            } else if (compareRes == SAME) {
-                                ++i;
-                                auto temp = result[i];
-                                result[i] = state;
-                                for (uint_fast64_t j = i + 1; j < numberOfSetBits && result[j + 1] != stateSize; j++) {
-                                    auto temp2 = result[j];
-                                    result[j] = temp;
-                                    temp = temp2;
-                                }
-                                added = true;
-                            }
+                            break;
+                        } else if (compareRes == UNKNOWN) {
+                            unknown = true;
+                            break;
                         }
-                        ++i;
+                    }
+                    if (unknown) {
+                        break;
+                    }
+                    if (!added) {
+                        result.push_back(state);
                     }
                 }
             }
-
+            while (result.size() < numberOfStatesToSort) {
+                result.push_back(numberOfStates);
+            }
+            assert (result.size() == numberOfStatesToSort);
             return result;
         }
 
-        bool Order::above(Node *node1, Node *node2) {
-            bool found = false;
-            for (auto const& state : node1->states) {
-                found = ((node2->statesAbove))[state];
-                if (found) {
+        std::pair<std::pair<uint_fast64_t,uint_fast64_t>, std::vector<uint_fast64_t>> Order::sortStatesForForward(uint_fast64_t currentState, std::vector<uint_fast64_t> const& states) {
+            std::vector<uint_fast64_t> statesSorted;
+            statesSorted.push_back(currentState);
+            // Go over all states
+            bool oneUnknown = false;
+            bool unknown = false;
+            uint_fast64_t s1 = numberOfStates;
+            uint_fast64_t s2 = numberOfStates;
+            for (auto & state : states) {
+                unknown = false;
+                bool added = false;
+                for (auto itr = statesSorted.begin(); itr != statesSorted.end(); ++itr) {
+                    auto compareRes = compare(state, (*itr));
+                    if (compareRes == ABOVE || compareRes == SAME) {
+                        if (!contains(state) && compareRes == ABOVE) {
+                            add(state);
+                        }
+                        added = true;
+                        // insert at current pointer (while keeping other values)
+                        statesSorted.insert(itr, state);
+                        break;
+                    } else if (compareRes == UNKNOWN && !oneUnknown) {
+                        // We miss state in the result.
+                        s1 = state;
+                        s2 = *itr;
+                        oneUnknown = true;
+                        added = true;
+                        break;
+                    } else if (compareRes == UNKNOWN && oneUnknown) {
+                        unknown = true;
+                        added = true;
+                        break;
+                    }
+                }
+                if (!added ) {
+                    // State will be last in the list
+                    statesSorted.push_back(state);
+                }
+                if (unknown && oneUnknown) {
                     break;
                 }
             }
+            if (!unknown && oneUnknown) {
+                assert (statesSorted.size() == states.size());
+                s2 = numberOfStates;
+            }
+            assert (s1 == numberOfStates || (s1 != numberOfStates && s2 == numberOfStates && statesSorted.size() == states.size()) || (s1 !=numberOfStates && s2 != numberOfStates && statesSorted.size() < states.size()));
 
-            if (!found && !doneBuilding) {
-                storm::storage::BitVector statesSeen((node2->statesAbove));
-                for (auto const &i: (node2->statesAbove)) {
-                    auto nodeI = getNode(i);
-                    if (((nodeI->statesAbove) & statesSeen) != (nodeI->statesAbove)) {
-                        found = above(node1, nodeI, node2, &statesSeen);
+            return {{s1, s2}, statesSorted};
+        }
+
+        std::pair<std::pair<uint_fast64_t ,uint_fast64_t>,std::vector<uint_fast64_t>> Order::sortStatesUnorderedPair(const std::vector<uint_fast64_t>* states) {
+            assert (states != nullptr);
+            uint_fast64_t numberOfStatesToSort = states->size();
+            std::vector<uint_fast64_t> result;
+            // Go over all states
+            for (auto state : *states) {
+                bool unknown = false;
+                if (result.size() == 0) {
+                    result.push_back(state);
+                } else {
+                    bool added = false;
+                    for (auto itr = result.begin();  itr != result.end(); ++itr) {
+                        auto compareRes = compare(state, (*itr));
+                        if (compareRes == ABOVE || compareRes == SAME) {
+                            // insert at current pointer (while keeping other values)
+                            result.insert(itr, state);
+                            added = true;
+                            break;
+                        } else if (compareRes == UNKNOWN) {
+                            return {{(*itr), state}, std::move(result)};
+                        }
+                    }
+                    if (unknown) {
+                        break;
+                    }
+                    if (!added) {
+                        result.push_back(state);
                     }
-                    if (found) {
-                        for (auto const& state:node1->states) {
-                            node2->statesAbove.set(state);
+                }
+            }
+
+            assert (result.size() == numberOfStatesToSort);
+            return {{numberOfStates, numberOfStates}, std::move(result)};
+        }
+
+        std::vector<uint_fast64_t> Order::sortStates(storm::storage::BitVector* states) {
+            uint_fast64_t numberOfStatesToSort = states->getNumberOfSetBits();
+            std::vector<uint_fast64_t> result;
+            // Go over all states
+            for (auto state : *states) {
+                bool unknown = false;
+                if (result.size() == 0) {
+                    result.push_back(state);
+                } else {
+                    bool added = false;
+                    for (auto itr = result.begin();  itr != result.end(); ++itr) {
+                        auto compareRes = compare(state, (*itr));
+                        if (compareRes == ABOVE || compareRes == SAME) {
+                            // insert at current pointer (while keeping other values)
+                            result.insert(itr, state);
+                            added = true;
+                            break;
+                        } else if (compareRes == UNKNOWN) {
+                            unknown = true;
+                            break;
                         }
+                    }
+                    if (unknown) {
                         break;
                     }
+                    if (!added) {
+                        result.push_back(state);
+                    }
                 }
             }
-            return found;
+            auto i = 0;
+            while (result.size() < numberOfStatesToSort) {
+                result.push_back(numberOfStates);
+                ++i;
+            }
+            assert (result.size() == numberOfStatesToSort);
+            return result;
+        }
+
+        /*** Checking on helpfunctionality for building of order ***/
+
+        std::shared_ptr<Order> Order::copy() const {
+            assert (!isInvalid());
+            std::shared_ptr<Order> copiedOrder = std::make_shared<Order>();
+            copiedOrder->nodes = std::vector<Node *>(numberOfStates, nullptr);
+            copiedOrder->onlyBottomTopOrder = this->isOnlyBottomTopOrder();
+            copiedOrder->numberOfStates = this->getNumberOfStates();
+            copiedOrder->statesSorted = std::vector<uint_fast64_t>(this->statesSorted);
+            copiedOrder->statesToHandle = std::vector<uint_fast64_t>(this->statesToHandle);
+            copiedOrder->trivialStates = storm::storage::BitVector(trivialStates);
+            copiedOrder->doneStates = storm::storage::BitVector(doneStates);
+            copiedOrder->numberOfAddedStates = this->numberOfAddedStates;
+            copiedOrder->doneBuilding = this->doneBuilding;
+
+            auto seenStates = storm::storage::BitVector(numberOfStates, false);
+            //copy nodes
+            for (auto state = 0; state < numberOfStates; ++state) {
+                Node *oldNode = nodes.at(state);
+                if (oldNode != nullptr) {
+                    if (!seenStates[*(oldNode->states.begin())]) {
+                        Node *newNode = new Node();
+                        if (oldNode == this->getTop()) {
+                            copiedOrder->top = newNode;
+                        } else if (oldNode == this->getBottom()) {
+                            copiedOrder->bottom = newNode;
+                        }
+                        newNode->statesAbove = storm::storage::BitVector(oldNode->statesAbove);
+                        for (auto i = 0; i < oldNode->statesAbove.size(); ++i) {
+                            assert (newNode->statesAbove[i] == oldNode->statesAbove[i]);
+                        }
+                        for (auto const &i : oldNode->states) {
+                            assert (!seenStates[i]);
+                            newNode->states.insert(i);
+                            seenStates.set(i);
+                            copiedOrder->nodes[i] = newNode;
+                        }
+                    }
+                } else {
+                    assert(copiedOrder->nodes[state] == nullptr);
+                }
+            }
+
+            return copiedOrder;
+        }
+
+        /*** Setters ***/
+        void Order::setDoneState(uint_fast64_t stateNumber) {
+            doneStates.set(stateNumber);
+        }
+
+        /*** Output ***/
+
+        void Order::toDotOutput() const {
+            // Graphviz Output start
+            STORM_PRINT("Dot Output:" << std::endl << "digraph model {" << std::endl);
+
+            // Vertices of the digraph
+            storm::storage::BitVector stateCoverage = storm::storage::BitVector(doneStates);
+            for (auto i = 0; i < numberOfStates; ++i) {
+                if (nodes[i] != nullptr) {
+                    stateCoverage.set(i);
+                }
+            }
+            for (uint_fast64_t i = stateCoverage.getNextSetIndex(0); i!= numberOfStates; i= stateCoverage.getNextSetIndex(i+1)) {
+                for (uint_fast64_t j = i + 1; j < numberOfStates; j++) {
+                    if (getNode(j) == getNode(i)) stateCoverage.set(j, false);
+                }
+                STORM_PRINT("\t" << nodeName(*getNode(i)) << " [ label = \"" << nodeLabel(*getNode(i)) << "\" ];" << std::endl);
+            }
+
+            // Edges of the digraph
+            for (uint_fast64_t i = stateCoverage.getNextSetIndex(0); i!= numberOfStates; i= stateCoverage.getNextSetIndex(i+1)) {
+                storm::storage::BitVector v = storm::storage::BitVector(numberOfStates, false);
+                Node* currentNode = getNode(i);
+                for (uint_fast64_t s1 : getNode(i)->statesAbove) {
+                    v |= (currentNode->statesAbove & getNode(s1)->statesAbove);
+                }
+
+                std::set<Node*> seenNodes;
+                for (uint_fast64_t state : currentNode->statesAbove) {
+                    Node* n = getNode(state);
+                    if (std::find(seenNodes.begin(), seenNodes.end(), n) == seenNodes.end()) {
+                        seenNodes.insert(n);
+                        if (!v[state]) {
+                            STORM_PRINT("\t" << nodeName(*currentNode) << " ->  " << nodeName(*getNode(state)) << ";" << std::endl);
+                        }
+                    }
+                }
+            }
+            // Graphviz Output end
+            STORM_PRINT("}" << std::endl);
+        }
+
+        void Order::dotOutputToFile(std::ofstream& dotOutfile) const {
+            // Graphviz Output start
+            dotOutfile << "Dot Output:" << std::endl << "digraph model {" << std::endl;
+
+            // Vertices of the digraph
+            storm::storage::BitVector stateCoverage = storm::storage::BitVector(numberOfStates, true);
+            for (uint_fast64_t i = stateCoverage.getNextSetIndex(0); i!= numberOfStates; i= stateCoverage.getNextSetIndex(i+1)) {
+                if (getNode(i) == NULL) {
+                    continue;
+                }
+                for (uint_fast64_t j = i + 1; j < numberOfStates; j++) {
+                    if (getNode(j) == getNode(i)) stateCoverage.set(j, false);
+                }
+
+                dotOutfile << "\t" << nodeName(*getNode(i)) << " [ label = \"" << nodeLabel(*getNode(i)) << "\" ];" << std::endl;
+            }
+
+            // Edges of the digraph
+            for (uint_fast64_t i = stateCoverage.getNextSetIndex(0); i!= numberOfStates; i= stateCoverage.getNextSetIndex(i+1)) {
+                storm::storage::BitVector v = storm::storage::BitVector(numberOfStates, false);
+                Node* currentNode = getNode(i);
+                if (currentNode == NULL){
+                    continue;
+                }
+
+                for (uint_fast64_t s1 : getNode(i)->statesAbove) {
+                    v |= (currentNode->statesAbove & getNode(s1)->statesAbove);
+                }
+
+                std::set<Node*> seenNodes;
+                for (uint_fast64_t state : currentNode->statesAbove) {
+                    Node* n = getNode(state);
+                    if (std::find(seenNodes.begin(), seenNodes.end(), n) == seenNodes.end()) {
+                        seenNodes.insert(n);
+                        if (!v[state]) {
+                            dotOutfile << "\t" << nodeName(*currentNode) << " ->  " << nodeName(*getNode(state)) << ";" << std::endl;
+                        }
+
+                    }
+                }
+
+            }
+
+            // Graphviz Output end
+            dotOutfile << "}" << std::endl;
+        }
+
+        /*** Private methods ***/
+
+        void Order::init(uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> decomposition, bool doneBuilding) {
+            this->numberOfStates = numberOfStates;
+            this->invalid = false;
+            this->nodes = std::vector<Node *>(numberOfStates, nullptr);
+            this->doneStates = storm::storage::BitVector(numberOfStates, false);
+            assert (doneStates.getNumberOfSetBits() == 0);
+            if (decomposition.size() == 0) {
+                this->trivialStates = storm::storage::BitVector(numberOfStates, true);
+            } else {
+                this->trivialStates = storm::storage::BitVector(numberOfStates, false);
+                for (auto& scc : decomposition) {
+                    if (scc.size() == 1) {
+                        trivialStates.set(*(scc.begin()));
+                    }
+                }
+            }
+            this->top = new Node();
+            this->bottom = new Node();
+            this->top->statesAbove = storm::storage::BitVector(numberOfStates, false);
+            this->bottom->statesAbove = storm::storage::BitVector(numberOfStates, false);
+            this->doneBuilding = doneBuilding;
         }
 
-        bool Order::above(storm::analysis::Order::Node *node1, storm::analysis::Order::Node *node2,
-                            storm::analysis::Order::Node *nodePrev, storm::storage::BitVector *statesSeen) {
+        bool Order::aboveFast(Node* node1, Node* node2) const {
             bool found = false;
             for (auto const& state : node1->states) {
                 found = ((node2->statesAbove))[state];
@@ -315,67 +683,123 @@ namespace storm {
                     break;
                 }
             }
-            if (!found) {
-                nodePrev->statesAbove|=((node2->statesAbove));
-                statesSeen->operator|=(((node2->statesAbove)));
+            return found;
+        }
 
-                for (auto const &i: node2->statesAbove) {
-                    if (!(*statesSeen)[i]) {
-                        auto nodeI = getNode(i);
-                        if (((nodeI->statesAbove) & *statesSeen) != (nodeI->statesAbove)) {
-                            found = above(node1, nodeI, node2, statesSeen);
-                        }
+        bool Order::above(Node *node1, Node *node2) {
+            assert (!aboveFast(node1, node2));
+            // Check whether node1 is above node2 by going over all states that are above state 2
+            bool above = false;
+            // Only do this when we have to deal with forward reasoning or we are not yet done with the building of the order
+            if (!trivialStates.full() || !doneBuilding) {
+                // First gather all states that are above node 2;
+
+                storm::storage::BitVector statesSeen((node2->statesAbove));
+                std::queue<uint_fast64_t> statesToHandle;
+                for (auto state : statesSeen) {
+                    statesToHandle.push(state);
+                }
+                while (!above && !statesToHandle.empty()) {
+                    // Get first item from the queue
+                    auto state = statesToHandle.front();
+                    statesToHandle.pop();
+                    auto node = getNode(state);
+                    if (aboveFast(node1, node)) {
+                        above = true;
+                        continue;
                     }
-                    if (found) {
-                        break;
+                    for (auto newState: node->statesAbove) {
+                        if (!statesSeen[newState]) {
+                            statesToHandle.push(newState);
+                            statesSeen.set(newState);
+                        }
                     }
-
                 }
             }
-            return found;
+            if (above) {
+                for (auto state : node1->states) {
+                    node2->statesAbove.set(state);
+                }
+            }
+            return above;
         }
 
-        void Order::mergeNodes(storm::analysis::Order::Node *node1, storm::analysis::Order::Node *node2) {
-            // Merges node2 into node 1
-            // everything above n2 also above n1
-            node1->statesAbove|=((node2->statesAbove));
-            // everything below node 2 also below node 1
+        std::string Order::nodeName(Node n) const {
+            auto itr = n.states.begin();
+            std::string name = "n" + std::to_string(*itr);
+            return name;
+        }
 
-            // add states of node 2 to node 1
-            node1->states.insert(node2->states.begin(), node2->states.end());
+        std::string Order::nodeLabel(Node n) const {
+            if (n.states == top->states) return "=)";
+            if (n.states == bottom->states) return "=(";
+            auto itr = n.states.begin();
+            std::string label = "s" + std::to_string(*itr);
+            ++itr;
+            if (itr != n.states.end()) label = "[" + label + "]";
+            return label;
+        }
 
-            for(auto const& i : node2->states) {
-                nodes[i] = node1;
+        bool Order::existsNextState() {
+            return !doneStates.full();
+        }
+
+        bool Order::isTrivial(uint_fast64_t state) {
+            return trivialStates[state];
+        }
+
+        std::pair<uint_fast64_t, bool> Order::getNextStateNumber() {
+            while (!statesSorted.empty()) {
+                auto state = statesSorted.back();
+                statesSorted.pop_back();
+                if (!doneStates[state]) {
+                    return {state, true};
+                }
             }
+            return {numberOfStates, true};
         }
 
-        void Order::merge(uint_fast64_t var1, uint_fast64_t var2) {
-            mergeNodes(getNode(var1), getNode(var2));
+        std::pair<uint_fast64_t, bool> Order::getStateToHandle() {
+            assert (existsStateToHandle());
+            auto state = statesToHandle.back();
+            statesToHandle.pop_back();
+            return {state, false};
         }
 
-        std::vector<uint_fast64_t> Order::getStatesSorted() {
-            return statesSorted;
+        bool Order::existsStateToHandle() {
+            while (!statesToHandle.empty() && doneStates[statesToHandle.back()]) {
+                statesToHandle.pop_back();
+            }
+            return !statesToHandle.empty();
         }
 
-        uint_fast64_t Order::getNextSortedState() {
-            if (statesSorted.begin() != statesSorted.end()) {
-                return *(statesSorted.begin());
-            } else {
-                return numberOfStates;
+        void Order::addStateToHandle(uint_fast64_t state) {
+            if (!doneStates[state]) {
+                statesToHandle.push_back(state);
             }
         }
 
-        void Order::removeFirstStatesSorted() {
-            statesSorted.erase(statesSorted.begin());
+        void Order::addStateSorted(uint_fast64_t state) {
+            statesSorted.push_back(state);
+        }
+
+        std::pair<bool, bool> Order::allAboveBelow(std::vector<uint_fast64_t> const states, uint_fast64_t state) {
+            auto allAbove = true;
+            auto allBelow = true;
+            for (auto & checkState : states) {
+                auto comp = compare(checkState, state);
+                allAbove &= (comp == ABOVE || comp == SAME);
+                allBelow &= (comp == BELOW || comp == SAME);
+            }
+            return {allAbove, allBelow};
         }
 
-        void Order::removeStatesSorted(uint_fast64_t state) {
-            assert(containsStatesSorted(state));
-            statesSorted.erase(std::find(statesSorted.begin(), statesSorted.end(), state));
+        uint_fast64_t Order::getNumberOfDoneStates() const {
+            return doneStates.getNumberOfSetBits();
         }
 
-        bool Order::containsStatesSorted(uint_fast64_t state) {
-            return std::find(statesSorted.begin(), statesSorted.end(), state) != statesSorted.end();
+        bool Order::isInvalid() const {
+            return invalid;
         }
     }
 }
diff --git a/src/storm-pars/analysis/Order.h b/src/storm-pars/analysis/Order.h
index fa4397498..0639760f6 100644
--- a/src/storm-pars/analysis/Order.h
+++ b/src/storm-pars/analysis/Order.h
@@ -1,22 +1,22 @@
 #ifndef ORDER_ORDER_H
 #define ORDER_ORDER_H
 
+#include <boost/container/flat_set.hpp>
 #include <iostream>
-#include <set>
 #include <vector>
-#include <unordered_map>
-#include <boost/container/flat_set.hpp>
+#include <memory>
 
 #include "storm/storage/BitVector.h"
+#include "storm/storage/StronglyConnectedComponent.h"
+
 
 namespace storm {
             namespace analysis {
-
                 class Order {
 
                 public:
                     /*!
-                     * Constants for comparison of nodes/states
+                     * Constants for comparison of nodes/states.
                      */
                     enum NodeComparison {
                         UNKNOWN,
@@ -24,127 +24,177 @@ namespace storm {
                         ABOVE,
                         SAME,
                     };
+
+                    /*!
+                     * Nodes of the Reachability Order. Contains all states with the same reachability.
+                     */
                     struct Node {
                         boost::container::flat_set<uint_fast64_t> states;
                         storm::storage::BitVector statesAbove;
                     };
 
                     /*!
-                     * Constructs an order with the given top node and bottom node.
+                     * Constructs an order with the given top and bottom states.
                      *
-                     * @param topNode The top node of the resulting order.
-                     * @param bottomNode The bottom node of the resulting order.
+                     * @param topStates A Bitvector with the top states of the resulting order.
+                     * @param bottomStates A Bitvector with the bottom states of the resulting order.
+                     * @param numberOfStates Maximum number of states in order.
+                     * @param statesSorted Pointer to a vector which contains the states which still need to added to the order.
                      */
-                    Order(storm::storage::BitVector* topStates,
-                            storm::storage::BitVector* bottomStates,
-                            storm::storage::BitVector* initialMiddleStates,
-                            uint_fast64_t numberOfStates,
-                            std::vector<uint_fast64_t>* statesSorted);
+                    Order(storm::storage::BitVector* topStates, storm::storage::BitVector* bottomStates, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> sccsSorted, std::vector<uint_fast64_t> statesSorted);
 
                     /*!
                      * Constructs an order with the given top state and bottom state.
                      *
                      * @param top The top state of the resulting order.
                      * @param bottom The bottom state of the resulting order.
-                     * @param numberOfStates Max number of states in order.
+                     * @param numberOfStates Maximum number of states in order.
+                     * @param statesSorted Pointer to a vector which contains the states which still need to added to the order.
                      */
-                    Order(uint_fast64_t top,
-                            uint_fast64_t bottom,
-                            uint_fast64_t numberOfStates,
-                            std::vector<uint_fast64_t>* statesSorted);
+                    Order(uint_fast64_t top, uint_fast64_t bottom, uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent> sccsSorted, std::vector<uint_fast64_t> statesSorted);
 
                     /*!
-                     * Constructs a copy of the given order.
+                     * Constructs a new Order.
+                     */
+                    Order();
+
+                    /*!
+                     * Adds state between the top and bottom node of the order.
                      *
-                     * @param order The original order.
+                     * @param state The given state.
                      */
-                    Order(Order* order);
+                    void add(uint_fast64_t state);
+
+                    /*!
+                    * Adds a node with the given state above the given node.
+                    *
+                    * @param state The state which is added.
+                    * @param node The pointer to the node above which the state is added, should not be nullptr.
+                    */
+                    void addAbove(uint_fast64_t state, Node *node);
+
+                    /*!
+                    * Adds a node with the given state below the given node.
+                    *
+                    * @param state The state which is added.
+                    * @param node The pointer to the node below which the state is added, should not be nullptr.
+                    */
+                    void addBelow(uint_fast64_t state, Node *node);
 
                     /*!
                      * Adds a node with the given state below node1 and above node2.
+                     *
                      * @param state The given state.
-                     * @param node1 The pointer to the node below which a new node (with state) is added
-                     * @param node2 The pointer to the node above which a new node (with state) is added
+                     * @param node1 The pointer to the node below which a new node (with state) is added.
+                     * @param node2 The pointer to the node above which a new node (with state) is added.
                      */
                     void addBetween(uint_fast64_t state, Node *node1, Node *node2);
 
                     /*!
                      * Adds a node with the given state between the nodes of below and above.
                      * Result: below -> state -> above
+                     *
                      * @param state The given state.
-                     * @param above The state number of the state below which a new node (with state) is added
-                     * @param below The state number of the state above which a new node (with state) is added
+                     * @param above The state number of the state below which a new node (with state) is added.
+                     * @param below The state number of the state above which a new node (with state) is added.
                      */
                     void addBetween(uint_fast64_t state, uint_fast64_t above, uint_fast64_t below);
 
+                    /*!
+                     * Adds a new relation between two nodes to the order.
+                     *
+                     * @param above The node closest to the top Node of the Order.
+                     * @param below The node closest to the bottom Node of the Order.
+                     */
+                    void addRelationNodes(storm::analysis::Order::Node *above, storm::analysis::Order::Node * below, bool allowMerge = false);
+
+                    /*!
+                    * Adds a new relation between two states to the order.
+                     *
+                    * @param above The state closest to the top Node of the Order.
+                    * @param below The state closest to the bottom Node of the Order.
+                    */
+                    void addRelation(uint_fast64_t above, uint_fast64_t below, bool allowMerge = false);
+
                     /*!
                      * Adds state to the states of the given node.
+                     *
                      * @param state The state which is added.
                      * @param node The pointer to the node to which state is added, should not be nullptr.
                      */
                     void addToNode(uint_fast64_t state, Node *node);
 
                     /*!
-                     * Adds state between the top and bottom node of the order
-                     * @param state The given state
+                     * Merges node2 into node1.
+                     * @return false when merging leads to invalid order
                      */
-                    void add(uint_fast64_t state);
+                    bool mergeNodes(Node* node1, Node* node2);
 
                     /*!
-                     * Adds a new relation between two nodes to the order
-                     * @param above The node closest to the top Node of the Order.
-                     * @param below The node closest to the bottom Node of the Order.
+                     * Merges node of var2 into node of var1.
+                     * @return false when merging leads to invalid order
                      */
-                    void addRelationNodes(storm::analysis::Order::Node *above, storm::analysis::Order::Node * below);
+                    bool merge(uint_fast64_t var1, uint_fast64_t var2);
 
                     /*!
-                    * Adds a new relation between two states to the order
-                    * @param above The state closest to the top Node of the Order.
-                    * @param below The state closest to the bottom Node of the Order.
-                    */
-                    void addRelation(uint_fast64_t above, uint_fast64_t below);
+                     * Compares the level of the nodes of the states.
+                     * Behaviour unknown when one or more of the states does not occur at any Node in the Order.
+                     *
+                     * @param State1 the first state.
+                     * @param State2 the second state.
+                     * @return SAME if the nodes are on the same level;
+                     *         ABOVE if the node of the first state is closer to top than the node of the second state;
+                     *         BELOW if the node of the second state is closer to top than the node of the first state;
+                     *         UNKNOWN if it is unclear from the structure of the order how the nodes relate.
+                     */
+                    Order::NodeComparison compare(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis = UNKNOWN);
+                    Order::NodeComparison compareFast(uint_fast64_t state1, uint_fast64_t state2, NodeComparison hypothesis = UNKNOWN) const;
 
                     /*!
-                     * Compares the level of the nodes of the states.
-                     * Behaviour unknown when one or more of the states doesnot occur at any Node in the Order.
-                     * @param state1 The first state.
-                     * @param state2 The second state.
+                     * Compares the level of the two nodes.
+                     *
+                     * @param node1 The first node.
+                     * @param node2 The second node.
                      * @return SAME if the nodes are on the same level;
-                     * ABOVE if the node of the first state is closer to top then the node of the second state;
-                     * BELOW if the node of the second state is closer to top then the node of the first state;
-                     * UNKNOWN if it is unclear from the structure of the order how the nodes relate.
+                     *         ABOVE if node1 is closer to top than node2;
+                     *         BELOW if node2 is closer to top than node1;
+                     *         UNKNOWN if it is unclear from the structure of the order how the nodes relate.
                      */
-                    Order::NodeComparison compare(uint_fast64_t state1, uint_fast64_t state2);
+                    NodeComparison compare(Node* node1, Node* node2, NodeComparison hypothesis = UNKNOWN);
+                    NodeComparison compareFast(Node* node1, Node* node2, NodeComparison hypothesis = UNKNOWN) const;
 
                     /*!
-                     * Check if state is already in order
-                     * @param state
-                     * @return
+                     * Check if state is already contained in order.
                      */
-                    bool contains(uint_fast64_t state);
+                    bool contains(uint_fast64_t state) const;
+
 
                     /*!
-                     * Retrieves the pointer to a Node at which the state occurs.
-                     *
-                     * @param state The number of the state.
+                     * Retrieves the bottom node of the order.
                      *
-                     * @return The pointer to the node of the state, nullptr if the node does not exist.
+                     * @return The bottom node.
                      */
-                    Node *getNode(uint_fast64_t state);
+                    Node* getBottom() const;
 
                     /*!
-                     * Retrieves the top node of the order.
-                     *
-                     * @return The top node.
+                     * Returns true if done building the order.
                      */
-                    Node* getTop();
+                    bool getDoneBuilding() const;
 
                     /*!
-                     * Retrieves the bottom node of the order.
+                     * Returns the next done state of the order, returns the number of state if end of done states is reached.
+                     */
+                    uint_fast64_t getNextDoneState(uint_fast64_t state) const;
+
+                    uint_fast64_t getNumberOfDoneStates() const;
+
+                    /*!
+                     * Retrieves the pointer to a Node at which the state occurs.
                      *
-                     * @return The bottom node.
+                     * @param state The number of the state.
+                     * @return The pointer to the node of the state, nullptr if the node does not exist.
                      */
-                    Node* getBottom();
+                    Node *getNode(uint_fast64_t state) const;
 
                     /*!
                      * Returns the vector with the nodes of the order.
@@ -153,82 +203,151 @@ namespace storm {
                      *
                      * @return The vector with nodes of the order.
                      */
-                    std::vector<Node*> getNodes();
+                    std::vector<Node*> getNodes() const;
 
+                    std::vector<uint_fast64_t>& getStatesSorted();
                     /*!
-                     * Returns a BitVector in which all added states are set.
+                     * Retrieves the top node of the order.
                      *
-                     * @return The BitVector with all added states.
+                     * @return The top node.
                      */
-                    storm::storage::BitVector* getAddedStates();
+                    Node* getTop() const;
 
                     /*!
-                     * Returns true if done building the order.
-                     * @return
+                     * Returns the number of added states.
                      */
-                    bool getDoneBuilding();
+                    uint_fast64_t getNumberOfAddedStates() const;
 
                     /*!
-                     * Compares two nodes in the order
-                     * @param node1
-                     * @param node2
-                     * @return BELOW, ABOVE, SAME or UNKNOWN
+                     * Returns the number of possible states in the order.
                      */
-                    NodeComparison compare(Node* node1, Node* node2);
+                    uint_fast64_t getNumberOfStates() const;
 
                     /*!
-                     * Sorts the given stats if possible.
+                     * Checks if the given state is a bottom state.
+                     */
+                    bool isBottomState(uint_fast64_t) const;
+
+                    /*!
+                     * Checks if the given state is a top state.
+                     */
+                    bool isTopState(uint_fast64_t) const;
+
+                    /*!
+                     * Returns if the order only consists of bottom and top states (so no in-between nodes).
+                     */
+                    bool isOnlyBottomTopOrder() const;
+
+                    /*!
+                     * Sorts the given states if possible.
                      *
-                     * @param states Bitvector of the states to sort
+                     * @param states Vector of the states to be sorted.
                      * @return Vector with states sorted, length equals number of states to sort.
-                     * If states cannot be sorted, last state of the vector will always equal the length of the BitVector
+                     * If states cannot be sorted, last state of the vector will always equal the length of the BitVector.
                      */
-                    std::vector<uint_fast64_t> sortStates(storm::storage::BitVector* states);
+                    std::vector<uint_fast64_t> sortStates(std::vector<uint_fast64_t>* states);
+                    std::pair<bool, bool> allAboveBelow(std::vector<uint_fast64_t>const states, uint_fast64_t state);
 
                     /*!
-                     * If the order is fully build, this can be set to true.
+                     * Sorts the given states if possible.
+                     *
+                     * @param states Vector of the states to be sorted.
+                     * @return s1, s2, vector
+                     * if s1 == numberOfSTates, all states could be sorted including current
+                     * if s1 < numberOfStates && s2 == numberOfStates, all states excluding s1 could be sorted, forward reasonging can be continued
+                     * else assumption is needed
                      */
-                    void setDoneBuilding(bool done);
+                    std::pair<std::pair<uint_fast64_t,uint_fast64_t>, std::vector<uint_fast64_t>> sortStatesForForward(uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors);
 
                     /*!
-                     * Prints a string representation of the order to the output stream.
+                     * Sorts the given states if possible.
                      *
-                     * @param out The stream to output to.
+                     * @param states Vector of the states to be sorted.
+                     * @return pair of unsortabe states, vector with states sorted (so far).
+                     * If all states could be sorted, both values of the pair are numberOfStates and the vectors length will equal the number of states to sort.
                      */
-                    void toString(std::ostream &out);
+                    std::pair<std::pair<uint_fast64_t ,uint_fast64_t>,std::vector<uint_fast64_t>> sortStatesUnorderedPair(const std::vector<uint_fast64_t>* states);
 
                     /*!
-                     * Merges node2 into node1
-                     * @param node1
-                     * @param node2
+                     * Sorts the given states if possible.
+                     *
+                     * @param states Bitvector of the states to be sorted.
+                     * @return vector with states sorted, length equals number of states to sort.
+                     * If states cannot be sorted, last state of the vector will always equal the length of the BitVector.
                      */
-                    void mergeNodes(Node* node1, Node* node2);
+                    std::vector<uint_fast64_t> sortStates(storm::storage::BitVector* states);
+
+                    bool isTrivial(uint_fast64_t state);
+                    std::pair<uint_fast64_t, bool> getNextStateNumber();
+
+//                    bool existsNextSCC();
+                    bool existsNextState();
+                    bool existsStateToHandle();
+
+                    std::pair<uint_fast64_t, bool> getStateToHandle();
+
+                    void addStateToHandle(uint_fast64_t state);
+                    void addStateSorted(uint_fast64_t state);
+
                     /*!
-                     * Merges node of var2 into node of var1
-                     * @param var1
-                     * @param var2
+                     * If the order is fully built, this can be set to true.
                      */
-                    void merge(uint_fast64_t var1, uint_fast64_t var2);
-
-                    storm::storage::BitVector* statesToHandle;
+                    void setDoneBuilding(bool done = true);
 
-                    uint_fast64_t getNextSortedState();
+                    /*!
+                     * Prints the dot output to normal STORM_PRINT.
+                     */
+                    void toDotOutput() const;
 
-                    bool containsStatesSorted(uint_fast64_t state);
+                    /*!
+                     * Writes dotoutput to the given file.
+                     *
+                     * @param dotOutfile
+                     */
+                    void dotOutputToFile(std::ofstream& dotOutfile) const;
 
-                    void removeFirstStatesSorted();
+                    /*!
+                     * Creates a copy of the calling Order.
+                     *
+                     * @return Pointer to the copy.
+                     */
+                    std::shared_ptr<Order> copy() const;
+//                    void setAddedSCC(uint_fast64_t sccNumber);
+                    void setDoneState(uint_fast64_t sccNumber);
 
-                    void removeStatesSorted(uint_fast64_t state);
+                    bool isInvalid() const;
 
                 protected:
-                    std::vector<uint_fast64_t> getStatesSorted();
+                    storage::Decomposition<storage::StronglyConnectedComponent> getDecomposition() const;
+
 
                 private:
-                    std::vector<Node*> nodes;
+                    bool above(Node * node1, Node * node2);
 
-                    std::vector<uint_fast64_t> statesSorted;
+                    bool above(Node * node1, Node * node2, storm::analysis::Order::Node *nodePrev, storm::storage::BitVector *statesSeen);
+
+                    bool aboveFast(Node * node1, Node * node2) const;
+
+                    void init(uint_fast64_t numberOfStates, storage::Decomposition<storage::StronglyConnectedComponent>, bool doneBuilding = false);
 
-                    storm::storage::BitVector* addedStates;
+                    std::string nodeName(Node n) const;
+
+                    std::string nodeLabel(Node n) const;
+
+                    bool invalid;
+
+                    void nodeOutput();
+
+                    bool doneBuilding;
+
+                    bool onlyBottomTopOrder;
+
+                    storm::storage::BitVector doneStates;
+                    storm::storage::BitVector trivialStates;
+
+                    std::vector<Node*> nodes;
+
+                    std::vector<uint_fast64_t> statesToHandle;
 
                     Node* top;
 
@@ -236,11 +355,10 @@ namespace storm {
 
                     uint_fast64_t numberOfStates;
 
-                    bool above(Node * node1, Node * node2);
+                    uint_fast64_t numberOfAddedStates;
 
-                    bool above(Node * node1, Node * node2, storm::analysis::Order::Node *nodePrev, storm::storage::BitVector *statesSeen);
+                    std::vector<uint_fast64_t> statesSorted;
 
-                    bool doneBuilding;
                 };
             }
 }
diff --git a/src/storm-pars/analysis/OrderExtender.cpp b/src/storm-pars/analysis/OrderExtender.cpp
index ebf8dc1eb..cc4967697 100644
--- a/src/storm-pars/analysis/OrderExtender.cpp
+++ b/src/storm-pars/analysis/OrderExtender.cpp
@@ -1,272 +1,204 @@
-//
-// Created by Jip Spel on 28.08.18.
-//
-
 #include "OrderExtender.h"
-#include "storm/utility/macros.h"
-#include "storm/utility/graph.h"
-#include "storm/storage/SparseMatrix.h"
-#include "storm/utility/graph.h"
-#include <storm/logic/Formula.h>
-#include <storm/modelchecker/propositional/SparsePropositionalModelChecker.h>
-#include "storm/models/sparse/Model.h"
-#include "storm/modelchecker/results/CheckResult.h"
-#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
-
 
-#include "storm/exceptions/NotImplementedException.h"
 #include "storm/exceptions/NotSupportedException.h"
-
-#include <set>
-#include <boost/container/flat_set.hpp>
-#include "storm/storage/StronglyConnectedComponentDecomposition.h"
-#include "storm/storage/StronglyConnectedComponent.h"
-
+#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
+#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
 #include "storm/storage/BitVector.h"
+#include "storm/storage/SparseMatrix.h"
 #include "storm/utility/macros.h"
-#include "storm/utility/Stopwatch.h"
+#include "storm/utility/graph.h"
 
+#include "storm-pars/api/region.h"
+#include "storm-pars/api/export.h"
+#include "storm-pars/analysis/MonotonicityHelper.h"
+#include "storm/storage/StronglyConnectedComponentDecomposition.h"
+
+#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
 
 namespace storm {
     namespace analysis {
 
-        template<typename ValueType>
-        OrderExtender<ValueType>::OrderExtender(std::shared_ptr<storm::models::sparse::Model<ValueType>> model) {
+        template <typename ValueType, typename ConstantType>
+        OrderExtender<ValueType, ConstantType>::OrderExtender(std::shared_ptr<models::sparse::Model<ValueType>> model, std::shared_ptr<logic::Formula const> formula) : monotonicityChecker(MonotonicityChecker<ValueType>(model->getTransitionMatrix())) {
             this->model = model;
             this->matrix = model->getTransitionMatrix();
-            this->assumptionSeen = false;
-            uint_fast64_t numberOfStates = this->model->getNumberOfStates();
+            this->numberOfStates = this->model->getNumberOfStates();
+            this->formula = formula;
+            this->assumptionMaker = new analysis::AssumptionMaker<ValueType, ConstantType>(matrix);
+        }
 
-            // Build stateMap
-            for (uint_fast64_t i = 0; i < numberOfStates; ++i) {
-                stateMap[i] = new storm::storage::BitVector(numberOfStates, false);
-                auto row = matrix.getRow(i);
-                for (auto rowItr = row.begin(); rowItr != row.end(); ++rowItr) {
-                    // ignore self-loops when there are more transitions
-                    if (i != rowItr->getColumn() || row.getNumberOfEntries() == 1) {
-                        stateMap[i]->set(rowItr->getColumn(), true);
-                    }
-                }
-            }
+        template <typename ValueType, typename ConstantType>
+        OrderExtender<ValueType, ConstantType>::OrderExtender(storm::storage::BitVector* topStates,  storm::storage::BitVector* bottomStates, storm::storage::SparseMatrix<ValueType> matrix) : monotonicityChecker(MonotonicityChecker<ValueType>(matrix)) {
+            this->matrix = matrix;
+            this->model = nullptr;
+            this->monotonicityChecker = MonotonicityChecker<ValueType>(matrix);
 
-            // Check if MC contains cycles
-            storm::storage::StronglyConnectedComponentDecompositionOptions const options;
-            this->sccs = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
-            acyclic = true;
-            for (size_t i = 0; acyclic && i < sccs.size(); ++i) {
-                acyclic &= sccs.getBlock(i).size() <= 1;
-            }
-        }
+            storm::storage::StronglyConnectedComponentDecompositionOptions options;
+            options.forceTopologicalSort();
 
-        template <typename ValueType>
-        std::tuple<Order*, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType>::toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas) {
-            STORM_LOG_THROW((++formulas.begin()) == formulas.end(), storm::exceptions::NotSupportedException, "Only one formula allowed for monotonicity analysis");
-            STORM_LOG_THROW((*(formulas[0])).isProbabilityOperatorFormula()
-                            && ((*(formulas[0])).asProbabilityOperatorFormula().getSubformula().isUntilFormula()
-                                || (*(formulas[0])).asProbabilityOperatorFormula().getSubformula().isEventuallyFormula()), storm::exceptions::NotSupportedException, "Expecting until or eventually formula");
-
-            uint_fast64_t numberOfStates = this->model->getNumberOfStates();
-
-            storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<ValueType>> propositionalChecker(*model);
-            storm::storage::BitVector phiStates;
-            storm::storage::BitVector psiStates;
-            if ((*(formulas[0])).asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
-                phiStates = propositionalChecker.check((*(formulas[0])).asProbabilityOperatorFormula().getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
-                psiStates = propositionalChecker.check((*(formulas[0])).asProbabilityOperatorFormula().getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
-            } else {
-                phiStates = storm::storage::BitVector(numberOfStates, true);
-                psiStates = propositionalChecker.check((*(formulas[0])).asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+            this->numberOfStates = matrix.getColumnCount();
+            std::vector<uint64_t> firstStates;
+
+            storm::storage::BitVector subStates (topStates->size(), true);
+            for (auto state : *topStates) {
+                firstStates.push_back(state);
+                subStates.set(state, false);
+            }
+            for (auto state : *bottomStates) {
+                firstStates.push_back(state);
+                subStates.set(state, false);
+            }
+            cyclic = storm::utility::graph::hasCycle(matrix, subStates);
+            storm::storage::StronglyConnectedComponentDecomposition<ValueType> decomposition;
+            if (cyclic) {
+                decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
             }
 
+            auto statesSorted = storm::utility::graph::getTopologicalSort(matrix.transpose(), firstStates);
+            this->bottomTopOrder = std::shared_ptr<Order>(new Order(topStates, bottomStates, numberOfStates, std::move(decomposition), std::move(statesSorted)));
 
-            // Get the maybeStates
-            std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(this->model->getBackwardTransitions(), phiStates, psiStates);
-            storm::storage::BitVector topStates = statesWithProbability01.second;
-            storm::storage::BitVector bottomStates = statesWithProbability01.first;
-
-            STORM_LOG_THROW(topStates.begin() != topStates.end(), storm::exceptions::NotImplementedException, "Formula yields to no 1 states");
-            STORM_LOG_THROW(bottomStates.begin() != bottomStates.end(), storm::exceptions::NotImplementedException, "Formula yields to no zero states");
-
-            // Transform to Order
-            auto matrix = this->model->getTransitionMatrix();
-
-            auto initialMiddleStates = storm::storage::BitVector(numberOfStates);
-            // Add possible cycle breaking states
-            if (!acyclic) {
-                for (size_t i = 0; i < sccs.size(); ++i) {
-                    auto scc = sccs.getBlock(i);
-                    if (scc.size() > 1) {
-                        auto states = scc.getStates();
-                        // check if the state has already one successor in bottom of top, in that case pick it
-                        for (auto const& state : states) {
-                            auto successors = stateMap[state];
-                            if (successors->getNumberOfSetBits() == 2) {
-                                auto succ1 = successors->getNextSetIndex(0);
-                                auto succ2 = successors->getNextSetIndex(succ1 + 1);
-                                auto intersection = bottomStates | topStates;
-                                if (intersection[succ1] || intersection[succ2]) {
-                                    initialMiddleStates.set(state);
-                                    break;
-                                }
-                            }
+            // Build stateMap
+            for (uint_fast64_t state = 0; state < numberOfStates; ++state) {
+                auto const& row = matrix.getRow(state);
+                stateMap[state] = std::vector<uint_fast64_t>();
+                std::set<VariableType> occurringVariables;
+
+                for (auto& entry : matrix.getRow(state)) {
+
+                    // ignore self-loops when there are more transitions
+                    if (state != entry.getColumn() || row.getNumberOfEntries() == 1) {
+                        if (!subStates[entry.getColumn()] && !bottomTopOrder->contains(state)) {
+                            bottomTopOrder->add(state);
                         }
+                        stateMap[state].push_back(entry.getColumn());
                     }
+                    storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
+
+                }
+                if (occurringVariables.empty()) {
+                    nonParametricStates.insert(state);
+                }
+
+                for (auto& var : occurringVariables) {
+                    occuringStatesAtVariable[var].push_back(state);
                 }
+                occuringVariablesAtState.push_back(std::move(occurringVariables));
             }
-            std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(matrix);
-            Order *order = new Order(&topStates, &bottomStates, &initialMiddleStates, numberOfStates, &statesSorted);
 
-            return this->extendOrder(order);
+            this->assumptionMaker = new analysis::AssumptionMaker<ValueType, ConstantType>(matrix);
         }
 
+        template <typename ValueType, typename ConstantType>
+        std::shared_ptr<Order> OrderExtender<ValueType, ConstantType>::getBottomTopOrder() {
+            if (bottomTopOrder == nullptr) {
+                assert (model != nullptr);
+                STORM_LOG_THROW(matrix.getRowCount() == matrix.getColumnCount(), exceptions::NotSupportedException,"Creating order not supported for non-square matrix");
+                modelchecker::SparsePropositionalModelChecker<models::sparse::Model<ValueType>> propositionalChecker(*model);
+                storage::BitVector phiStates;
+                storage::BitVector psiStates;
+                assert (formula->isProbabilityOperatorFormula());
+                if (formula->asProbabilityOperatorFormula().getSubformula().isUntilFormula()) {
+                    phiStates = propositionalChecker.check(
+                            formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+                    psiStates = propositionalChecker.check(
+                            formula->asProbabilityOperatorFormula().getSubformula().asUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+                } else {
+                    assert (formula->asProbabilityOperatorFormula().getSubformula().isEventuallyFormula());
+                    phiStates = storage::BitVector(numberOfStates, true);
+                    psiStates = propositionalChecker.check(
+                            formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+                }
+                // Get the maybeStates
+                std::pair<storage::BitVector, storage::BitVector> statesWithProbability01 = utility::graph::performProb01(this->model->getBackwardTransitions(), phiStates, psiStates);
+                storage::BitVector topStates = statesWithProbability01.second;
+                storage::BitVector bottomStates = statesWithProbability01.first;
+
+                STORM_LOG_THROW(topStates.begin() != topStates.end(), exceptions::NotSupportedException,"Formula yields to no 1 states");
+                STORM_LOG_THROW(bottomStates.begin() != bottomStates.end(), exceptions::NotSupportedException,"Formula yields to no zero states");
+                auto& matrix = this->model->getTransitionMatrix();
+                std::vector<uint64_t> firstStates;
+
+                storm::storage::BitVector subStates (topStates.size(), true);
+                for (auto state : topStates) {
+                    firstStates.push_back(state);
+                    subStates.set(state, false);
+                }
+                for (auto state : bottomStates) {
+                    firstStates.push_back(state);
+                    subStates.set(state, false);
+                }
+                cyclic = storm::utility::graph::hasCycle(matrix, subStates);
+                storm::storage::StronglyConnectedComponentDecomposition<ValueType> decomposition;
+                if (cyclic) {
+                    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+                    options.forceTopologicalSort();
+                    decomposition = storm::storage::StronglyConnectedComponentDecomposition<ValueType>(matrix, options);
+                }
+                auto statesSorted = storm::utility::graph::getTopologicalSort(matrix.transpose(), firstStates);
+                bottomTopOrder = std::shared_ptr<Order>(new Order(&topStates, &bottomStates, numberOfStates, std::move(decomposition), std::move(statesSorted)));
+
+                // Build stateMap
+                for (uint_fast64_t state = 0; state < numberOfStates; ++state) {
+                    auto const& row = matrix.getRow(state);
+                    stateMap[state] = std::vector<uint_fast64_t>();
+                    std::set<VariableType> occurringVariables;
+
+                    for (auto& entry : matrix.getRow(state)) {
+
+                        // ignore self-loops when there are more transitions
+                        if (state != entry.getColumn() || row.getNumberOfEntries() == 1) {
+//                            if (!subStates[entry.getColumn()] && !bottomTopOrder->contains(state)) {
+//                                bottomTopOrder->add(state);
+//                            }
+                            stateMap[state].push_back(entry.getColumn());
+                        }
+                        storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
 
-        template <typename ValueType>
-        std::tuple<Order*, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType>::toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<double> minValues, std::vector<double> maxValues) {
-            uint_fast64_t numberOfStates = this->model->getNumberOfStates();
-            uint_fast64_t bottom = numberOfStates;
-            uint_fast64_t top = numberOfStates;
-            std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(matrix);
-            Order *order = nullptr;
-
-            for (auto state : statesSorted) {
-                if ((minValues[numberOfStates - 1 - state] == 1 || maxValues[numberOfStates - 1 - state] == 0)
-                        && minValues[numberOfStates - 1 - state] == maxValues[numberOfStates - 1 - state]) {
-                    if (maxValues[numberOfStates - 1 - state] == 0) {
-                        assert (bottom == numberOfStates);
-                        bottom = state;
                     }
-                    if (minValues[numberOfStates - 1 - state] == 1) {
-                        assert (top == numberOfStates);
-                        top = state;
+                    if (occurringVariables.empty()) {
+                        nonParametricStates.insert(state);
                     }
-                    if (bottom != numberOfStates && top != numberOfStates) {
-                        order = new Order(top, bottom, numberOfStates, &statesSorted);
-                    }
-
-                } else {
-                    assert (order != nullptr);
-                    auto successors = stateMap[state];
-                    if (successors->getNumberOfSetBits() == 1) {
-                        auto succ = successors->getNextSetIndex(0);
-                        if (succ != state) {
-                            if (!order->contains(succ)) {
-                                order->add(succ);
-                            }
-                            order->addToNode(state, order->getNode(succ));
-                        }
-                    } else if (successors->getNumberOfSetBits() > 1) {
-                        uint_fast64_t min = numberOfStates;
-                        uint_fast64_t max = numberOfStates;
-                        bool allSorted = true;
-
-                        for (auto succ = successors->getNextSetIndex(0);
-                             succ < numberOfStates; succ = successors->getNextSetIndex(succ + 1)) {
-                            if (min == numberOfStates) {
-                                assert (max == numberOfStates);
-                                min = succ;
-                                max = succ;
-                            } else {
-                                if (minValues[numberOfStates - 1 - succ] > maxValues[numberOfStates - 1 - max]) {
-                                    max = succ;
-                                } else if (maxValues[numberOfStates - 1 - succ] < minValues[numberOfStates - 1 - min]) {
-                                    min = succ;
-                                } else {
-                                    allSorted = false;
-                                    break;
-                                }
-                            }
-                        }
 
-                        if (allSorted && min != max) {
-                            if (order->contains(min) && order->contains(max)) {
-                                assert (order->compare(min,max) == Order::UNKNOWN || order->compare(min,max) == Order::BELOW);
-                                if (order->compare(min, max) == Order::UNKNOWN) {
-                                    order->addRelation(max, min);
-                                }
-                            }
-                            if (!order->contains(min)) {
-                                if (order->contains(max)) {
-                                    order->addBetween(min, order->getNode(max), order->getBottom());
-                                } else {
-                                    order->add(min);
-                                }
-                            }
-                            if (!order->contains(max)) {
-                                // Because of construction min is in the order
-                                order->addBetween(max, order->getTop(), order->getNode(min));
-                            }
-                            assert (order->compare(max, min) == Order::ABOVE);
-                            if (order->contains(state)) {
-                                if (order->compare(max, state) == Order::UNKNOWN) {
-                                    order->addRelation(max, state);
-                                }
-                                if (order->compare(state, min) == Order::UNKNOWN) {
-                                    order->addRelation(state, min);
-                                }
-                            } else {
-                                order->addBetween(state, max, min);
-                            }
-                            assert (order->compare(max, state) == Order::ABOVE);
-                            assert (order->compare(state, min) == Order::ABOVE);
-                        }
+                    for (auto& var : occurringVariables) {
+                        occuringStatesAtVariable[var].push_back(state);
                     }
+                    occuringVariablesAtState.push_back(std::move(occurringVariables));
                 }
+
             }
 
-            assert (order != nullptr);
-
-            // Handle sccs
-            auto addedStates = order->getAddedStates();
-            for (auto scc : sccs) {
-                if (scc.size() > 1) {
-                    auto states = scc.getStates();
-                    auto candidate = -1;
-                    for (auto const& state : states) {
-                        if (addedStates->get(state)) {
-                            candidate = -1;
-                            break;
-                            // if there is a state of the scc already present in the order, there is no need to add one.
-                        }
-                        auto successors = stateMap[state];
-                        if (candidate == -1 && successors->getNumberOfSetBits() == 2) {
-                            auto succ1 = successors->getNextSetIndex(0);
-                            auto succ2 = successors->getNextSetIndex(succ1 + 1);
-                            if (addedStates->get(succ1) || addedStates->get(succ2)) {
-                                candidate = state;
-                            }
-                        }
-                    }
-                    if (candidate != -1) {
-                        order->add(candidate);
-                        order->statesToHandle->set(candidate);
-                    }
-                }
-             }
-            return this->extendOrder(order);
+            if (minValuesInit && maxValuesInit) {
+                continueExtending[bottomTopOrder] = true;
+                usePLA[bottomTopOrder] = true;
+                minValues[bottomTopOrder] = std::move(minValuesInit.get());
+                maxValues[bottomTopOrder] = std::move(maxValuesInit.get());
+            } else {
+                usePLA[bottomTopOrder] = false;
+            }
+            return bottomTopOrder;
         }
 
+        template <typename ValueType, typename ConstantType>
+        std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::toOrder(storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes) {
+            return this->extendOrder(nullptr, region, monRes, nullptr);
+        }
 
-        template <typename ValueType>
-        void OrderExtender<ValueType>::handleAssumption(Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption) {
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::handleAssumption(std::shared_ptr<Order> order, std::shared_ptr<expressions::BinaryRelationExpression> assumption) const {
             assert (assumption != nullptr);
-            assumptionSeen = true;
+            assert (assumption->getFirstOperand()->isVariable() && assumption->getSecondOperand()->isVariable());
 
-            storm::expressions::BinaryRelationExpression expr = *assumption;
-            assert (expr.getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater
-                || expr.getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal);
+            expressions::Variable var1 = assumption->getFirstOperand()->asVariableExpression().getVariable();
+            expressions::Variable var2 = assumption->getSecondOperand()->asVariableExpression().getVariable();
+            auto const& val1 = std::stoul(var1.getName(), nullptr, 0);
+            auto const& val2 = std::stoul(var2.getName(), nullptr, 0);
 
-            if (expr.getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
-                assert (expr.getFirstOperand()->isVariable() && expr.getSecondOperand()->isVariable());
-                storm::expressions::Variable var1 = expr.getFirstOperand()->asVariableExpression().getVariable();
-                storm::expressions::Variable var2 = expr.getSecondOperand()->asVariableExpression().getVariable();
-                auto val1 = std::stoul(var1.getName(), nullptr, 0);
-                auto val2 = std::stoul(var2.getName(), nullptr, 0);
-                auto comp = order->compare(val1, val2);
+            assert (order->compare(val1, val2) == Order::UNKNOWN);
 
-                assert (comp == Order::UNKNOWN);
-                Order::Node *n1 = order->getNode(val1);
-                Order::Node *n2 = order->getNode(val2);
+            Order::Node* n1 = order->getNode(val1);
+            Order::Node* n2 = order->getNode(val2);
 
+            if (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Equal) {
                 if (n1 != nullptr && n2 != nullptr) {
                     order->mergeNodes(n1,n2);
                 } else if (n1 != nullptr) {
@@ -278,17 +210,7 @@ namespace storm {
                     order->addToNode(val2, order->getNode(val1));
                 }
             } else {
-                assert (expr.getFirstOperand()->isVariable() && expr.getSecondOperand()->isVariable());
-                storm::expressions::Variable largest = expr.getFirstOperand()->asVariableExpression().getVariable();
-                storm::expressions::Variable smallest = expr.getSecondOperand()->asVariableExpression().getVariable();
-                auto val1 = std::stoul(largest.getName(), nullptr, 0);
-                auto val2 = std::stoul(smallest.getName(), nullptr, 0);
-                auto compareRes = order->compare(val1, val2);
-
-                assert(compareRes == Order::UNKNOWN);
-                Order::Node *n1 = order->getNode(val1);
-                Order::Node *n2 = order->getNode(val2);
-
+                assert (assumption->getRelationType() == expressions::BinaryRelationExpression::RelationType::Greater);
                 if (n1 != nullptr && n2 != nullptr) {
                     order->addRelationNodes(n1, n2);
                 } else if (n1 != nullptr) {
@@ -302,200 +224,600 @@ namespace storm {
             }
         }
 
-        template <typename ValueType>
-        std::tuple<Order*, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType>::extendAllSuccAdded(Order* order, uint_fast64_t const & stateNumber, storm::storage::BitVector* successors) {
-            auto numberOfStates = successors->size();
-            assert (order->getAddedStates()->size() == numberOfStates);
-
-            if (successors->getNumberOfSetBits() == 1) {
-                // As there is only one successor the current state and its successor must be at the same nodes.
-                order->addToNode(stateNumber, order->getNode(successors->getNextSetIndex(0)));
-            } else if (successors->getNumberOfSetBits() == 2) {
-                // Otherwise, check how the two states compare, and add if the comparison is possible.
-                uint_fast64_t successor1 = successors->getNextSetIndex(0);
-                uint_fast64_t successor2 = successors->getNextSetIndex(successor1 + 1);
-
-                int compareResult = order->compare(successor1, successor2);
-                if (compareResult == Order::ABOVE) {
-                    // successor 1 is closer to top than successor 2
-                    order->addBetween(stateNumber, order->getNode(successor1),
-                                        order->getNode(successor2));
-                } else if (compareResult == Order::BELOW) {
-                    // successor 2 is closer to top than successor 1
-                    order->addBetween(stateNumber, order->getNode(successor2),
-                                        order->getNode(successor1));
-                } else if (compareResult == Order::SAME) {
-                    // the successors are at the same level
-                    order->addToNode(stateNumber, order->getNode(successor1));
-                } else {
-                    assert(order->compare(successor1, successor2) == Order::UNKNOWN);
-                    return std::make_tuple(order, successor1, successor2);
-                }
-            } else if (successors->getNumberOfSetBits() > 2) {
-                for (auto const& i : *successors) {
-                    for (auto j = successors->getNextSetIndex(i+1); j < numberOfStates; j = successors->getNextSetIndex(j+1)) {
-                        if (order->compare(i,j) == Order::UNKNOWN) {
-                            return std::make_tuple(order, i, j);
+        template <typename ValueType, typename ConstantType>
+        std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendOrder(std::shared_ptr<Order> order, storm::storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes, std::shared_ptr<expressions::BinaryRelationExpression> assumption) {
+            this->region = region;
+            if (order == nullptr) {
+                order = getBottomTopOrder();
+                if (usePLA[order]) {
+                    auto &min = minValues[order];
+                    auto &max = maxValues[order];
+                    // Try to make the order as complete as possible based on pla results
+                    auto &statesSorted = order->getStatesSorted();
+                    auto itr = statesSorted.begin();
+                    while (itr != statesSorted.end()) {
+                        auto state = *itr;
+                        auto &successors = stateMap[state];
+                        bool all = true;
+                        for (auto i = 0; i < successors.size(); ++i) {
+                            auto state1 = successors[i];
+                            for (auto j = i + 1; j < successors.size(); ++j) {
+                                auto state2 = successors[j];
+                                if (min[state1] > max[state2]) {
+                                    if (!order->contains(state1)) {
+                                        order->add(state1);
+                                    }
+                                    if (!order->contains(state2)) {
+                                        order->add(state2);
+                                    }
+                                    order->addRelation(state1, state2, false);
+                                } else if (min[state2] > max[state1]) {
+                                    if (!order->contains(state1)) {
+                                        order->add(state1);
+                                    }
+                                    if (!order->contains(state2)) {
+                                        order->add(state2);
+                                    }
+                                    order->addRelation(state2, state1, false);
+                                } else if (min[state1] == max[state2] && max[state1] == min[state2]) {
+                                    if (!order->contains(state1) && !order->contains(state2)) {
+                                        order->add(state1);
+                                        order->addToNode(state2, order->getNode(state1));
+                                    } else if (!order->contains(state1)) {
+                                        order->addToNode(state1, order->getNode(state2));
+                                    } else if (!order->contains(state2)) {
+                                        order->addToNode(state2, order->getNode(state1));
+                                    } else {
+                                        order->merge(state1, state2);
+                                        assert (!order->isInvalid());
+                                    }
+                                } else {
+                                    all = false;
+                                }
+                            }
                         }
+                        if (all) {
+                            STORM_LOG_INFO("All successors of state " << state << " sorted based on min max values");
+                            order->setDoneState(state);
+                        }
+                        ++itr;
                     }
                 }
+                continueExtending[order] = true;
+            }
+            if (continueExtending[order] || assumption != nullptr) {
+                return extendOrder(order, monRes, assumption);
+            } else {
+                auto& res = unknownStatesMap[order];
+                continueExtending[order] = false;
+                return {order, res.first, res.second};
+            }
+        }
 
-                auto highest = successors->getNextSetIndex(0);
-                auto lowest = highest;
-                for (auto i = successors->getNextSetIndex(highest+1); i < numberOfStates; i = successors->getNextSetIndex(i+1)) {
-                    if (order->compare(i, highest) == Order::ABOVE) {
-                        highest = i;
-                    }
-                    if (order->compare(lowest, i) == Order::ABOVE) {
-                        lowest = i;
+        template <typename ValueType, typename ConstantType>
+        std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendOrder(std::shared_ptr<Order> order, std::shared_ptr<MonotonicityResult<VariableType>> monRes, std::shared_ptr<expressions::BinaryRelationExpression> assumption) {
+            if (assumption != nullptr) {
+                STORM_LOG_INFO("Handling assumption " << *assumption << std::endl);
+                handleAssumption(order, assumption);
+            }
+
+            auto currentStateMode = getNextState(order, numberOfStates, false);
+            while (currentStateMode.first != numberOfStates) {
+                assert (currentStateMode.first < numberOfStates);
+                auto& currentState = currentStateMode.first;
+                auto& successors = stateMap[currentState];
+                std::pair<uint_fast64_t, uint_fast64_t> result =  {numberOfStates, numberOfStates};
+
+                if (successors.size() == 1) {
+                    assert (order->contains(successors[0]));
+                    handleOneSuccessor(order, currentState, successors[0]);
+                } else if (!successors.empty()) {
+                    if (order->isOnlyBottomTopOrder()) {
+                        order->add(currentState);
+                        if (!order->isTrivial(currentState)) {
+                            // This state is part of an scc, therefore, we could do forward reasoning here
+                            result = extendByForwardReasoning(order, currentState, successors, assumption!=nullptr);
+                        } else {
+                            result = {numberOfStates, numberOfStates};
+                        }
+                    } else {
+                        result = extendNormal(order, currentState, successors, assumption != nullptr);
                     }
                 }
-                if (lowest == highest) {
-                    order->addToNode(stateNumber, order->getNode(highest));
+
+                if (result.first == numberOfStates) {
+                    // We did extend the order
+                     assert (result.second == numberOfStates);
+                     assert (order->sortStates(&successors).size() == successors.size());
+                     assert (order->contains(currentState) && order->getNode(currentState) != nullptr);
+
+                    if (monRes != nullptr && currentStateMode.second != -1) {
+                        for (auto& param : occuringVariablesAtState[currentState]) {
+                            checkParOnStateMonRes(currentState, order, param, monRes);
+                        }
+                    }
+                    // Get the next state
+                    currentStateMode = getNextState(order, currentState, true);
                 } else {
-                    order->addBetween(stateNumber, order->getNode(highest), order->getNode(lowest));
+                     assert (result.first < numberOfStates);
+                     assert (result.second < numberOfStates);
+                     assert (order->compare(result.first, result.second) == Order::UNKNOWN);
+                     assert (order->compare(result.second, result.first) == Order::UNKNOWN);
+                    // Try to add states based on min/max and assumptions, only if we are not in statesToHandle mode
+                    if (currentStateMode.second && extendByAssumption(order, currentState, result.first, result.second)) {
+                        continue;
+                    }
+                    // We couldn't extend the order
+                    if (nonParametricStates.find(currentState) != nonParametricStates.end()) {
+                        if (!order->contains(currentState)) {
+                            // State is not parametric, so we hope that just adding it between =) and =( will help us
+                            order->add(currentState);
+                        }
+                        currentStateMode = getNextState(order, currentState, true);
+                        continue;
+                    } else {
+                        if (!currentStateMode.second) {
+                            // The state was based on statesToHandle, so it is not bad if we cannot continue with this.
+                            currentStateMode = getNextState(order, currentState, false);
+                            continue;
+                        } else {
+                            // The state was based on the topological sorting, so we need to return, but first add this state to the states Sorted as we are not done with it
+                            order->addStateSorted(currentState);
+                            continueExtending[order] = false;
+                            return {order, result.first, result.second};
+                        }
+                    }
                 }
+                assert (order->sortStates(&successors).size() == successors.size());
+            }
+
+            assert (order->getDoneBuilding());
+            if (monRes != nullptr) {
+                // monotonicity result for the in-build checking of monotonicity
+                monRes->setDone();
             }
             return std::make_tuple(order, numberOfStates, numberOfStates);
         }
 
 
+        template<typename ValueType, typename ConstantType>
+        std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendNormal(std::shared_ptr<Order> order, uint_fast64_t currentState, const vector<uint_fast64_t> &successors, bool allowMerge)  {
+            // when it is cyclic and the current state is part of an SCC we do forwardreasoning
+            if (cyclic && !order->isTrivial(currentState) && order->contains(currentState)) {
+                // Try to extend the order for this scc
+                return  extendByForwardReasoning(order, currentState, successors, allowMerge);
+            } else {
+                assert (order->isTrivial(currentState) || !order->contains(currentState));
+                // Do backward reasoning, all successor states must be in the order
+                return  extendByBackwardReasoning(order, currentState, successors, allowMerge);
+            }
+        }
 
-        template <typename ValueType>
-        std::tuple<Order*, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType>::extendOrder(Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption) {
-            auto numberOfStates = this->model->getNumberOfStates();
-
-            if (assumption != nullptr) {
-                handleAssumption(order, assumption);
+        template<typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::handleOneSuccessor(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t successor) {
+            assert (order->contains(successor));
+            if (currentState != successor) {
+                if (order->contains(currentState)) {
+                    order->merge(currentState, successor);
+                } else {
+                    order->addToNode(currentState, order->getNode(successor));
+                }
             }
+        }
 
-            auto oldNumberSet = numberOfStates;
-            while (oldNumberSet != order->getAddedStates()->getNumberOfSetBits()) {
-                oldNumberSet = order->getAddedStates()->getNumberOfSetBits();
-
-                // Forward reasoning for cycles;
-                if (!acyclic) {
-                    auto statesToHandle = order->statesToHandle;
-                    auto stateNumber = statesToHandle->getNextSetIndex(0);
-
-                    while (stateNumber != numberOfStates) {
-                        storm::storage::BitVector *successors = stateMap[stateNumber];
-                        // Checking for states which are already added to the order, and only have one successor left which haven't been added yet
-                        auto succ1 = successors->getNextSetIndex(0);
-                        auto succ2 = successors->getNextSetIndex(succ1 + 1);
-
-                        assert (order->contains(stateNumber));
-                        if (successors->getNumberOfSetBits() == 1) {
-                            if (!order->contains(succ1)) {
-                                order->addToNode(succ1, order->getNode(stateNumber));
-                                statesToHandle->set(succ1, true);
-                                if (order->containsStatesSorted(succ1)) {
-                                    order->removeStatesSorted(succ1);
-                                }
+        template <typename ValueType, typename ConstantType>
+        std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendByBackwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge) {
+             assert (!order->isOnlyBottomTopOrder());
+             assert (successors.size() > 1);
+
+            bool pla = (usePLA.find(order) != usePLA.end() && usePLA.at(order));
+            std::vector<uint_fast64_t> sortedSuccs;
+
+            if (pla && (continueExtending.find(order) == continueExtending.end() || continueExtending.at(order))) {
+                for (auto& state1 : successors) {
+                    if (sortedSuccs.size() == 0) {
+                        sortedSuccs.push_back(state1);
+                    } else {
+                        bool added = false;
+                        for (auto itr = sortedSuccs.begin(); itr != sortedSuccs.end(); ++itr) {
+                            auto& state2 = *itr;
+                            auto compareRes = order->compareFast(state1, state2);
+                            if (compareRes == Order::NodeComparison::UNKNOWN) {
+                                compareRes = addStatesBasedOnMinMax(order, state1, state2);
                             }
-                            statesToHandle->set(stateNumber, false);
-                            stateNumber = statesToHandle->getNextSetIndex(0);
-                        } else if (successors->getNumberOfSetBits() == 2
-                                   && ((order->contains(succ1) && !order->contains(succ2))
-                                       || (!order->contains(succ1) && order->contains(succ2)))) {
-
-                            if (!order->contains(succ1)) {
-                                std::swap(succ1, succ2);
+                            if (compareRes == Order::NodeComparison::UNKNOWN) {
+                                // If fast comparison did not help, we continue by checking "slow" comparison
+                                compareRes = order->compare(state1, state2);
                             }
-
-                            auto compare = order->compare(stateNumber, succ1);
-                            if (compare == Order::ABOVE) {
-                                if (order->containsStatesSorted(succ2)) {
-                                    order->removeStatesSorted(succ2);
-                                }
-                                order->addBetween(succ2, order->getTop(), order->getNode(stateNumber));
-                                statesToHandle->set(succ2);
-                                statesToHandle->set(stateNumber, false);
-                                stateNumber = statesToHandle->getNextSetIndex(0);
-                            } else if (compare == Order::BELOW) {
-                                if (order->containsStatesSorted(succ2)) {
-                                    order->removeStatesSorted(succ2);
-                                }
-                                order->addBetween(succ2, order->getNode(stateNumber), order->getBottom());
-                                statesToHandle->set(succ2);
-                                statesToHandle->set(stateNumber, false);
-                                stateNumber = statesToHandle->getNextSetIndex(0);
-                            } else {
-                                // We don't know positions, so we set the current state number to false
-                                statesToHandle->set(stateNumber, false);
-                                stateNumber = statesToHandle->getNextSetIndex(0);
+                            if (compareRes == Order::NodeComparison::ABOVE ||
+                                compareRes == Order::NodeComparison::SAME) {
+                                // insert at current pointer (while keeping other values)
+                                sortedSuccs.insert(itr, state1);
+                                added = true;
+                                break;
+                            } else if (compareRes == Order::NodeComparison::UNKNOWN) {
+                                continueExtending[order] = false;
+                                return {state1, state2};
                             }
-
-                        } else if (!((order->contains(succ1) && !order->contains(succ2))
-                                     || (!order->contains(succ1) && order->contains(succ2)))) {
-                            stateNumber = statesToHandle->getNextSetIndex(stateNumber + 1);
-                        } else {
-                            statesToHandle->set(stateNumber, false);
-                            stateNumber = statesToHandle->getNextSetIndex(0);
+                        }
+                        if (!added) {
+                            sortedSuccs.push_back(state1);
                         }
                     }
+                }
+            } else {
+                auto temp = order->sortStatesUnorderedPair(&successors);
+                if (temp.first.first != numberOfStates) {
+                    return temp.first;
+                } else {
+                    sortedSuccs = std::move(temp.second);
+                }
+            }
 
+            if (order->compare(sortedSuccs[0], sortedSuccs[sortedSuccs.size() - 1]) == Order::SAME) {
+                if (!order->contains(currentState)) {
+                    order->addToNode(currentState, order->getNode(sortedSuccs[0]));
+                } else {
+                    order->merge(currentState, sortedSuccs[0]);
+                }
+            } else {
+                if (!order->contains(sortedSuccs[0])) {
+                    assert (order->isBottomState(sortedSuccs[sortedSuccs.size() - 1]));
+                    assert (sortedSuccs.size() == 2);
+                    order->addAbove(sortedSuccs[0], order->getBottom());
+                }
+                if (!order->contains(sortedSuccs[sortedSuccs.size() - 1])) {
+                    assert (order->isTopState(sortedSuccs[0]));
+                    assert (sortedSuccs.size() == 2);
+                    order->addBelow(sortedSuccs[sortedSuccs.size() - 1], order->getTop());
+                }
+                // sortedSuccs[0] is highest
+                if (!order->contains(currentState)) {
+                    order->addBetween(currentState, sortedSuccs[0], sortedSuccs[sortedSuccs.size()-1]);
+                } else {
+                    order->addRelation(sortedSuccs[0], currentState, allowMerge);
+                    order->addRelation(currentState, sortedSuccs[sortedSuccs.size() - 1], allowMerge);
                 }
 
-                // Normal backwardreasoning
-                auto stateNumber = order->getNextSortedState();
-                while (stateNumber != numberOfStates && order->contains(stateNumber)) {
-                    order->removeFirstStatesSorted();
-                    stateNumber = order->getNextSortedState();
+            }
+            assert (order->contains(currentState) && order->compare(order->getNode(currentState), order->getBottom()) == Order::ABOVE && order->compare(order->getNode(currentState), order->getTop()) == Order::BELOW);
+            return {numberOfStates, numberOfStates};
+        }
 
-                    if (stateNumber != numberOfStates && order->contains(stateNumber)) {
-                        auto resAllAdded = allSuccAdded(order, stateNumber);
-                        if (!std::get<0>(resAllAdded)) {
-                            return std::make_tuple(order, std::get<1>(resAllAdded), std::get<2>(resAllAdded));
+        template <typename ValueType, typename ConstantType>
+        std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::extendByForwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge)  {
+            assert (successors.size() > 1);
+            assert (order->contains(currentState));
+            assert (cyclic);
+
+            std::vector<uint_fast64_t> statesSorted;
+            statesSorted.push_back(currentState);
+            bool pla = (usePLA.find(order) != usePLA.end() && usePLA.at(order));
+            // Go over all states
+            bool oneUnknown = false;
+            bool unknown = false;
+            uint_fast64_t s1 = numberOfStates;
+            uint_fast64_t s2 = numberOfStates;
+            for (auto& state : successors) {
+                unknown = false;
+                bool added = false;
+                for (auto itr = statesSorted.begin(); itr != statesSorted.end(); ++itr) {
+                    auto compareRes = order->compareFast(state, (*itr));
+                    if (pla && compareRes == Order::NodeComparison::UNKNOWN) {
+                        compareRes = addStatesBasedOnMinMax(order, state, (*itr));
+                    }
+                    if (compareRes == Order::NodeComparison::UNKNOWN) {
+                        compareRes = order->compare(state, *itr);
+                    }
+                    if (compareRes == Order::NodeComparison::ABOVE || compareRes == Order::NodeComparison::SAME) {
+                        if (!order->contains(state) && compareRes == Order::NodeComparison::ABOVE) {
+                            order->add(state);
+                            order->addStateToHandle(state);
                         }
+                        added = true;
+                        // insert at current pointer (while keeping other values)
+                        statesSorted.insert(itr, state);
+                        break;
+                    } else if (compareRes == Order::NodeComparison::UNKNOWN && !oneUnknown) {
+                        // We miss state in the result.
+                        s1 = state;
+                        s2 = *itr;
+                        oneUnknown = true;
+                        added = true;
+                        break;
+                    } else if (compareRes == Order::NodeComparison::UNKNOWN && oneUnknown) {
+                        unknown = true;
+                        added = true;
+                        break;
                     }
                 }
+                if (!(unknown && oneUnknown) && !added ) {
+                    // State will be last in the list
+                    statesSorted.push_back(state);
+                }
+                if (unknown && oneUnknown) {
+                    break;
+                }
+            }
+            if (!unknown && oneUnknown) {
+                assert (statesSorted.size() == successors.size());
+                s2 = numberOfStates;
+            }
 
-                if (stateNumber != numberOfStates && !order->contains(stateNumber)) {
-                    auto successors = stateMap[stateNumber];
+            if (s1 == numberOfStates) {
+                assert (statesSorted.size() == successors.size() + 1);
+                // all could be sorted, no need to do anything
+            } else if (s2 == numberOfStates) {
+                if (!order->contains(s1)) {
+                    order->add(s1);
+                }
 
-                    auto result = extendAllSuccAdded(order, stateNumber, successors);
-                    if (std::get<1>(result) != numberOfStates) {
-                        // So we don't know the relation between all successor states
-                        return result;
-                    } else {
-                        assert (order->getNode(stateNumber) != nullptr);
-                        if (!acyclic) {
-                            order->statesToHandle->set(stateNumber);
+                if (statesSorted[0] == currentState) {
+                    order->addRelation(s1, statesSorted[0], allowMerge);
+                    auto res = order->compare(s1, statesSorted[0]);
+                    assert ((order->compare(s1, statesSorted[0]) == Order::ABOVE) || (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)));
+                    order->addRelation(s1, statesSorted[statesSorted.size() - 1], allowMerge);
+                    assert ((order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::ABOVE) || (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)));
+                    order->addStateToHandle(s1);
+                } else if (statesSorted[statesSorted.size() - 1] == currentState) {
+                    order->addRelation(statesSorted[0], s1, allowMerge);
+                    assert ((order->compare(s1, statesSorted[0]) == Order::BELOW) || (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)));
+                    order->addRelation(statesSorted[statesSorted.size() - 1], s1, allowMerge);
+                    assert ((order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::BELOW) || (allowMerge && (order->compare(s1, statesSorted[statesSorted.size() - 1]) == Order::SAME)));
+                    order->addStateToHandle(s1);
+                } else {
+                    bool continueSearch = true;
+                    for (auto& entry :  matrix.getRow(currentState)) {
+                        if (entry.getColumn() == s1) {
+                            if (entry.getValue().isConstant()) {
+                                continueSearch = false;
+                            }
+                        }
+                    }
+                    if (continueSearch) {
+                        for (auto& i : statesSorted) {
+                            if (order->compare(i, s1) == Order::UNKNOWN) {
+                                return {i, s1};
+                            }
                         }
-                        order->removeFirstStatesSorted();
                     }
                 }
-                assert (stateNumber == numberOfStates || order->getNode(stateNumber) != nullptr);
-                assert (stateNumber == numberOfStates || order->contains(stateNumber));
+            } else {
+                return {s1, s2};
+            }
+            assert (order->contains(currentState) && order->compare(order->getNode(currentState), order->getBottom()) == Order::ABOVE && order->compare(order->getNode(currentState), order->getTop()) == Order::BELOW);
+            return {numberOfStates, numberOfStates};
+        }
 
+        template<typename ValueType, typename ConstantType>
+        bool OrderExtender<ValueType, ConstantType>::extendByAssumption(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t stateSucc1, uint_fast64_t stateSucc2) {
+            bool usePLANow = usePLA.find(order) != usePLA.end() && usePLA[order];
+            assert (order->compare(stateSucc1, stateSucc2) == Order::UNKNOWN);
+            auto assumptions = usePLANow ? assumptionMaker->createAndCheckAssumptions(stateSucc1, stateSucc2,  order, region, minValues[order], maxValues[order]) : assumptionMaker->createAndCheckAssumptions(stateSucc1, stateSucc2, order, region);
+            if (assumptions.size() == 1 && assumptions.begin()->second == AssumptionStatus::VALID) {
+                handleAssumption(order, assumptions.begin()->first);
+                // Assumptions worked, we continue
+                return true;
             }
-            assert (order->getAddedStates()->getNumberOfSetBits() == numberOfStates);
-            order->setDoneBuilding(true);
-            return std::make_tuple(order, numberOfStates, numberOfStates);
+            return false;
         }
 
-        template <typename ValueType>
-        std::tuple<bool, uint_fast64_t, uint_fast64_t> OrderExtender<ValueType>::allSuccAdded(storm::analysis::Order *order, uint_fast64_t stateNumber) {
-            auto successors = stateMap[stateNumber];
-            auto numberOfStates = successors->size();
-
-            if (successors->getNumberOfSetBits() == 1) {
-                auto succ = successors->getNextSetIndex(0);
-                return std::make_tuple(order->contains(succ), succ, succ);
-            } else if (successors->getNumberOfSetBits() > 2) {
-                for (auto const& i : *successors) {
-                    for (auto j = successors->getNextSetIndex(i+1); j < numberOfStates; j = successors->getNextSetIndex(j+1)) {
-                        if (order->compare(i,j) == Order::UNKNOWN) {
-                            return std::make_tuple(false, i, j);
-                        }
+        template <typename ValueType, typename ConstantType>
+        Order::NodeComparison OrderExtender<ValueType, ConstantType>::addStatesBasedOnMinMax(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) const {
+            assert (order->compareFast(state1, state2) == Order::UNKNOWN);
+            assert (minValues.find(order) != minValues.end());
+            std::vector<ConstantType> const& mins = minValues.at(order);
+            std::vector<ConstantType> const& maxs = maxValues.at(order);
+            if (mins[state1] == maxs[state1]
+                && mins[state2] == maxs[state2]
+                   && mins[state1] == mins[state2]) {
+                if (order->contains(state1)) {
+                    if (order->contains(state2)) {
+                        order->merge(state1, state2);
+                        assert (!order->isInvalid());
+                    } else {
+                        order->addToNode(state2, order->getNode(state1));
                     }
                 }
+                return Order::SAME;
+            } else if (mins[state1] > maxs[state2]) {
+                // state 1 will always be larger than state2
+                if (!order->contains(state1)) {
+                    order->add(state1);
+                }
+                if (!order->contains(state2)) {
+                    order->add(state2);
+                }
+                assert (order->compare(state1, state2) != Order::BELOW);
+                assert (order->compare(state1, state2) != Order::SAME);
+                order->addRelation(state1, state2);
+
+                return Order::ABOVE;
+            } else if (mins[state2] > maxs[state1]) {
+                // state2 will always be larger than state1
+                if (!order->contains(state1)) {
+                    order->add(state1);
+                }
+                if (!order->contains(state2)) {
+                    order->add(state2);
+                }
+                assert (order->compare(state2, state1) != Order::BELOW);
+                assert (order->compare(state2, state1) != Order::SAME);
+                order->addRelation(state2, state1);
+                return Order::BELOW;
+            } else {
+                // Couldn't add relation between state1 and state 2 based on min/max values;
+                return Order::UNKNOWN;
+            }
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::initializeMinMaxValues(storage::ParameterRegion<ValueType> region) {
+            if (model != nullptr) {
+                // Use parameter lifting modelchecker to get initial min/max values for order creation
+                modelchecker::SparseDtmcParameterLiftingModelChecker<models::sparse::Dtmc<ValueType>, ConstantType> plaModelChecker;
+                std::unique_ptr<modelchecker::CheckResult> checkResult;
+                auto env = Environment();
+                boost::optional<modelchecker::CheckTask<logic::Formula, ValueType>> checkTask;
+                if (this->formula->hasQuantitativeResult()) {
+                    checkTask = modelchecker::CheckTask<logic::Formula, ValueType>(*formula);
+                } else {
+                    storm::logic::OperatorInformation opInfo(boost::none, boost::none);
+                    auto newFormula = std::make_shared<storm::logic::ProbabilityOperatorFormula>(
+                            formula->asProbabilityOperatorFormula().getSubformula().asSharedPointer(), opInfo);
+                    checkTask = modelchecker::CheckTask<logic::Formula, ValueType>(*newFormula);
+                }
+                STORM_LOG_THROW(plaModelChecker.canHandle(model, checkTask.get()), exceptions::NotSupportedException, "Cannot handle this formula");
+                plaModelChecker.specify(env, model, checkTask.get(), false, false);
+
+                modelchecker::ExplicitQuantitativeCheckResult<ConstantType> minCheck = plaModelChecker.check(env, region, solver::OptimizationDirection::Minimize)->template asExplicitQuantitativeCheckResult<ConstantType>();
+                modelchecker::ExplicitQuantitativeCheckResult<ConstantType> maxCheck = plaModelChecker.check(env, region, solver::OptimizationDirection::Maximize)->template asExplicitQuantitativeCheckResult<ConstantType>();
+                minValuesInit = minCheck.getValueVector();
+                maxValuesInit = maxCheck.getValueVector();
+                assert (minValuesInit->size() == numberOfStates);
+                assert (maxValuesInit->size() == numberOfStates);
+            }
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setMinMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType>& minValues, std::vector<ConstantType>& maxValues) {
+            assert (minValues.size() == numberOfStates);
+            assert (maxValues.size() == numberOfStates);
+            usePLA[order] = true;
+            if (unknownStatesMap.find(order) != unknownStatesMap.end()) {
+                auto& unknownStates = unknownStatesMap[order];
+                if (unknownStates.first != numberOfStates) {
+                    continueExtending[order] = minValues[unknownStates.first] >= maxValues[unknownStates.second] ||  minValues[unknownStates.second] >= maxValues[unknownStates.first];
+                } else {
+                    continueExtending[order] = true;
+                }
+            } else {
+                continueExtending[order] = true;
             }
-            return std::make_tuple(true, numberOfStates, numberOfStates);
+            this->minValues[order] = std::move(minValues);
+            this->maxValues[order] = std::move(maxValues);
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setMinValues(std::shared_ptr<Order> order, std::vector<ConstantType>& minValues) {
+            assert (minValues.size() == numberOfStates);
+            auto& maxValues = this->maxValues[order];
+            usePLA[order] = this->maxValues.find(order) != this->maxValues.end();
+            if (maxValues.size() == 0) {
+                continueExtending[order] = false;
+            } else if (unknownStatesMap.find(order) != unknownStatesMap.end()) {
+                auto& unknownStates = unknownStatesMap[order];
+                if (unknownStates.first != numberOfStates) {
+                    continueExtending[order] = minValues[unknownStates.first] >= maxValues[unknownStates.second] ||  minValues[unknownStates.second] >= maxValues[unknownStates.first];
+                } else {
+                    continueExtending[order] = true;
+                }
+            } else {
+                continueExtending[order] = true;
+            }
+            this->minValues[order] = std::move(minValues);
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType>& maxValues) {
+            assert (maxValues.size() == numberOfStates);
+            usePLA[order] = this->minValues.find(order) != this->minValues.end();
+            auto& minValues = this->minValues[order];
+            if (minValues.size() == 0) {
+                continueExtending[order] = false;
+            } else  if (unknownStatesMap.find(order) != unknownStatesMap.end()) {
+                auto& unknownStates = unknownStatesMap[order];
+                if (unknownStates.first != numberOfStates) {
+                    continueExtending[order] =
+                            minValues[unknownStates.first] >= maxValues[unknownStates.second] ||
+                            minValues[unknownStates.second] >= maxValues[unknownStates.first];
+                } else {
+                    continueExtending[order] = true;
+                }
+            } else {
+                continueExtending[order] = true;
+            }
+            this->maxValues[order] = std::move(maxValues);//maxCheck->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
+
+        }
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setMinValuesInit(std::vector<ConstantType>& minValues) {
+            assert (minValues.size() == numberOfStates);
+            this->minValuesInit = std::move(minValues);
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setMaxValuesInit(std::vector<ConstantType>& maxValues) {
+            assert (maxValues.size() == numberOfStates);
+            this->maxValuesInit = std::move(maxValues);//maxCheck->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
+        }
+
+        template <typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::checkParOnStateMonRes(uint_fast64_t s, std::shared_ptr<Order> order, typename OrderExtender<ValueType, ConstantType>::VariableType param, std::shared_ptr<MonotonicityResult<VariableType>> monResult) {
+            auto mon = monotonicityChecker.checkLocalMonotonicity(order, s, param, region);
+            monResult->updateMonotonicityResult(param, mon);
+        }
+
+        template<typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setUnknownStates(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) {
+            assert (state1 != numberOfStates && state2 != numberOfStates);
+            unknownStatesMap[order] = {state1, state2};
+        }
+
+        template<typename ValueType, typename ConstantType>
+        std::pair<uint_fast64_t, uint_fast64_t> OrderExtender<ValueType, ConstantType>::getUnknownStates(std::shared_ptr<Order> order) const {
+            if (unknownStatesMap.find(order) != unknownStatesMap.end()) {
+                return unknownStatesMap.at(order);
+            }
+            return {numberOfStates, numberOfStates};
+        }
+
+        template<typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::setUnknownStates(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy) {
+            assert (unknownStatesMap.find(orderCopy) == unknownStatesMap.end());
+            unknownStatesMap.insert({orderCopy,{unknownStatesMap[orderOriginal].first, unknownStatesMap[orderOriginal].second}});
+        }
+
+        template<typename ValueType, typename ConstantType>
+        void OrderExtender<ValueType, ConstantType>::copyMinMax(std::shared_ptr<Order> orderOriginal,
+                                                                std::shared_ptr<Order> orderCopy) {
+            usePLA[orderCopy] = usePLA[orderOriginal];
+            if (usePLA[orderCopy]) {
+                minValues[orderCopy] = minValues[orderOriginal];
+                assert (maxValues.find(orderOriginal) != maxValues.end());
+                maxValues[orderCopy] = maxValues[orderOriginal];
+            }
+            continueExtending[orderCopy] = continueExtending[orderOriginal];
+        }
+
+        template<typename ValueType, typename ConstantType>
+        std::pair<uint_fast64_t, bool> OrderExtender<ValueType, ConstantType>::getNextState(std::shared_ptr<Order> order, uint_fast64_t currentState, bool done) {
+            if (done && currentState != numberOfStates) {
+                order->setDoneState(currentState);
+            }
+            if (cyclic && order->existsStateToHandle()) {
+                return order->getStateToHandle();
+            }
+            if (currentState == numberOfStates) {
+                return order->getNextStateNumber();
+            }
+            if (currentState != numberOfStates) {
+                return order->getNextStateNumber();
+            }
+            return {numberOfStates, true};
+        }
 
+        template<typename ValueType, typename ConstantType>
+        bool OrderExtender<ValueType, ConstantType>::isHope(std::shared_ptr<Order> order, storage::ParameterRegion<ValueType> region) {
+            assert (unknownStatesMap.find(order) != unknownStatesMap.end());
+            assert (!order->getDoneBuilding());
+            // First check if bounds helped us
+            bool yesThereIsHope = continueExtending[order];
+            // TODO: maybe extend this
+            return yesThereIsHope;
         }
 
-        template class OrderExtender<storm::RationalFunction>;
+        template class OrderExtender<RationalFunction, double>;
+        template class OrderExtender<RationalFunction, RationalNumber>;
     }
 }
diff --git a/src/storm-pars/analysis/OrderExtender.h b/src/storm-pars/analysis/OrderExtender.h
index 30d3d4137..fe66211a1 100644
--- a/src/storm-pars/analysis/OrderExtender.h
+++ b/src/storm-pars/analysis/OrderExtender.h
@@ -1,81 +1,132 @@
-#ifndef STORM_LATTICEEXTENDER_H
-#define STORM_LATTICEEXTENDER_H
+#ifndef STORM_ORDEREXTENDER_H
+#define STORM_ORDEREXTENDER_H
 
-#include <storm/logic/Formula.h>
-#include "storm/models/sparse/Dtmc.h"
-#include "storm-pars/analysis/Order.h"
+#include <boost/container/flat_set.hpp>
 #include "storm/api/storm.h"
-#include "storm-pars/storage/ParameterRegion.h"
-#include "storm/storage/StronglyConnectedComponentDecomposition.h"
-#include "storm/storage/StronglyConnectedComponent.h"
+#include "storm/logic/Formula.h"
+#include "storm/models/sparse/Model.h"
+#include "storm/storage/expressions/BinaryRelationExpression.h"
+#include "storm/storage/expressions/VariableExpression.h"
 
+#include "storm-pars/analysis/Order.h"
+#include "storm-pars/analysis/MonotonicityResult.h"
+#include "storm-pars/analysis/MonotonicityChecker.h"
+#include "storm-pars/storage/ParameterRegion.h"
+#include "AssumptionMaker.h"
 
 
 namespace storm {
     namespace analysis {
-
-
-        template<typename ValueType>
+        template<typename ValueType, typename ConstantType>
         class OrderExtender {
 
         public:
+            typedef typename utility::parametric::CoefficientType<ValueType>::type CoefficientType;
+            typedef typename utility::parametric::VariableType<ValueType>::type VariableType;
+            typedef typename MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+
+           /*!
+            * Constructs a new OrderExtender.
+            *
+            * @param model The model for which the order should be extended.
+            * @param formula The considered formula.
+            * @param region The Region of the model's parameters.
+            */
+           OrderExtender(std::shared_ptr<models::sparse::Model<ValueType>> model, std::shared_ptr<logic::Formula const> formula);
+
             /*!
-             * Constructs OrderExtender which can extend an order
+             * Constructs a new OrderExtender.
              *
-             * @param model The model for which the order should be extended.
+             * @param topStates The top states of the order.
+             * @param bottomStates The bottom states of the order.
+             * @param matrix The matrix of the considered model.
              */
-            OrderExtender(std::shared_ptr<storm::models::sparse::Model<ValueType>> model);
+           OrderExtender(storm::storage::BitVector* topStates,  storm::storage::BitVector* bottomStates, storm::storage::SparseMatrix<ValueType> matrix);
 
             /*!
              * Creates an order based on the given formula.
              *
-             * @param formulas The formulas based on which the order is created, only the first is used.
+             * @param monRes The monotonicity result so far.
              * @return A triple with a pointer to the order and two states of which the current place in the order
              *         is unknown but needed. When the states have as number the number of states, no states are
              *         unplaced but needed.
              */
-            std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas);
+            std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> toOrder(storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes = nullptr);
 
             /*!
-             * Creates an order based on the given extremal values.
+             * Extends the order for the given region.
              *
-             * @return A triple with a pointer to the order and two states of which the current place in the order
+             * @param order pointer to the order.
+             * @param region The region on which the order needs to be extended.
+             * @return Two states of which the current place in the order
              *         is unknown but needed. When the states have as number the number of states, no states are
-             *         unplaced but needed.
+             *         unplaced or needed.
              */
-            std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> toOrder(std::vector<std::shared_ptr<storm::logic::Formula const>> formulas, std::vector<double> minValues, std::vector<double> maxValues);
+            std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> extendOrder(std::shared_ptr<Order> order, storm::storage::ParameterRegion<ValueType> region, std::shared_ptr<MonotonicityResult<VariableType>> monRes = nullptr, std::shared_ptr<expressions::BinaryRelationExpression> assumption = nullptr);
 
+            void setMinMaxValues(std::shared_ptr<Order> order, std::vector<ConstantType> &minValues, std::vector<ConstantType> &maxValues);
+            void setMinValues(std::shared_ptr<Order> order, std::vector<ConstantType> &minValues);
+            void setMaxValues(std::shared_ptr<Order> order,std::vector<ConstantType> &maxValues);
+            void setMinValuesInit(std::vector<ConstantType> &minValues);
+            void setMaxValuesInit(std::vector<ConstantType> &minValues);
 
-            /*!
-             * Extends the order based on the given assumption.
-             *
-             * @param order The order.
-             * @param assumption The assumption on states.
-             * @return A triple with a pointer to the order and two states of which the current place in the order
-             *         is unknown but needed. When the states have as number the number of states, no states are
-             *         unplaced but needed.
-             */
-            std::tuple<storm::analysis::Order*, uint_fast64_t, uint_fast64_t> extendOrder(storm::analysis::Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption = nullptr);
+            void setUnknownStates(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2);
 
+            std::pair<uint_fast64_t, uint_fast64_t> getUnknownStates(std::shared_ptr<Order> order) const;
+            void setUnknownStates(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy);
+            void copyMinMax(std::shared_ptr<Order> orderOriginal, std::shared_ptr<Order> orderCopy);
+            void initializeMinMaxValues(storage::ParameterRegion<ValueType> region);
+            void checkParOnStateMonRes(uint_fast64_t s, std::shared_ptr<Order> order, typename OrderExtender<ValueType, ConstantType>::VariableType param, std::shared_ptr<MonotonicityResult<VariableType>> monResult);
 
+            bool isHope(std::shared_ptr<Order> order, storage::ParameterRegion<ValueType>);
         private:
-            std::shared_ptr<storm::models::sparse::Model<ValueType>> model;
 
-            std::map<uint_fast64_t, storm::storage::BitVector*> stateMap;
+            Order::NodeComparison addStatesBasedOnMinMax(std::shared_ptr<Order> order, uint_fast64_t state1, uint_fast64_t state2) const;
+            std::tuple<std::shared_ptr<Order>, uint_fast64_t, uint_fast64_t> extendOrder(std::shared_ptr<Order> order, std::shared_ptr<MonotonicityResult<VariableType>> monRes, std::shared_ptr<expressions::BinaryRelationExpression> assumption = nullptr);
+            std::pair<uint_fast64_t, uint_fast64_t> extendNormal(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
+            std::pair<uint_fast64_t, uint_fast64_t> extendByBackwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
+            std::pair<uint_fast64_t, uint_fast64_t> extendByForwardReasoning(std::shared_ptr<Order> order, uint_fast64_t currentState, std::vector<uint_fast64_t> const& successors, bool allowMerge);
+            bool extendByAssumption(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t succState2, uint_fast64_t succState1);
+
+            void handleOneSuccessor(std::shared_ptr<Order> order, uint_fast64_t currentState, uint_fast64_t successor);
+            void handleAssumption(std::shared_ptr<Order> order, std::shared_ptr<expressions::BinaryRelationExpression> assumption) const;
+
+            std::pair<uint_fast64_t, bool> getNextState(std::shared_ptr<Order> order, uint_fast64_t stateNumber, bool done);
+            std::shared_ptr<Order> getBottomTopOrder();
+
+
+            std::shared_ptr<Order> bottomTopOrder = nullptr;
+
+            std::map<std::shared_ptr<Order>, std::vector<ConstantType>> minValues;
+            boost::optional<std::vector<ConstantType>> minValuesInit;
+            boost::optional<std::vector<ConstantType>> maxValuesInit;
+            std::map<std::shared_ptr<Order>, std::vector<ConstantType>> maxValues;
+
+            storage::SparseMatrix<ValueType> matrix;
+            std::shared_ptr<models::sparse::Model<ValueType>> model;
+
+            std::map<uint_fast64_t, std::vector<uint_fast64_t>> stateMap;
+            std::map<std::shared_ptr<Order>, std::pair<uint_fast64_t, uint_fast64_t>> unknownStatesMap;
+
+            std::map<std::shared_ptr<Order>, bool> usePLA;
+            std::map<std::shared_ptr<Order>, bool> continueExtending;
+            bool cyclic;
+
+            std::shared_ptr<logic::Formula const> formula;
 
-            bool acyclic;
+            storage::ParameterRegion<ValueType> region;
 
-            bool assumptionSeen;
+            uint_fast64_t numberOfStates;
 
-            storm::storage::StronglyConnectedComponentDecomposition<ValueType> sccs;
+            analysis::AssumptionMaker<ValueType, ConstantType>* assumptionMaker;
 
-            storm::storage::SparseMatrix<ValueType> matrix;
 
-            void handleAssumption(Order* order, std::shared_ptr<storm::expressions::BinaryRelationExpression> assumption);
+            boost::container::flat_set<uint_fast64_t> nonParametricStates;
 
-            std::tuple<Order*, uint_fast64_t, uint_fast64_t> extendAllSuccAdded(Order* order, uint_fast64_t const & stateNumber, storm::storage::BitVector* successors);
+            std::map<VariableType, std::vector<uint_fast64_t>> occuringStatesAtVariable;
+            std::vector<std::set<VariableType>> occuringVariablesAtState;
+            MonotonicityChecker<ValueType> monotonicityChecker;
 
-            std::tuple<bool, uint_fast64_t, uint_fast64_t> allSuccAdded(Order* order, uint_fast64_t stateNumber);
         };
     }
 }
diff --git a/src/storm-pars/api/analysis.h b/src/storm-pars/api/analysis.h
new file mode 100644
index 000000000..396151e9c
--- /dev/null
+++ b/src/storm-pars/api/analysis.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "storm-pars/analysis/Order.h"
+#include "storm-pars/analysis/OrderExtender.h"
+#include "storm-pars/analysis/AssumptionChecker.h"
+#include "storm-pars/analysis/AssumptionMaker.h"
+#include "storm-pars/analysis/MonotonicityResult.h"
+#include "storm-pars/analysis/MonotonicityHelper.h"
+#include "storm-pars/analysis/LocalMonotonicityResult.h"
+
diff --git a/src/storm-pars/api/region.h b/src/storm-pars/api/region.h
index b147b664f..3665790ab 100644
--- a/src/storm-pars/api/region.h
+++ b/src/storm-pars/api/region.h
@@ -15,6 +15,7 @@
 #include "storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h"
 #include "storm-pars/modelchecker/region/RegionResultHypothesis.h"
 #include "storm-pars/parser/ParameterRegionParser.h"
+#include "storm-pars/parser/MonotonicityParser.h"
 #include "storm-pars/storage/ParameterRegion.h"
 #include "storm-pars/utility/parameterlifting.h"
 
@@ -30,20 +31,31 @@
 namespace storm {
     
     namespace api {
-        
+        struct MonotonicitySetting {
+            MonotonicitySetting(bool a = false, bool b = false, bool c = false) { useMonotonicity = a; useOnlyGlobalMonotonicity = b; useBoundsFromPLA = c;}
+            bool useMonotonicity;
+            bool useOnlyGlobalMonotonicity;
+            bool useBoundsFromPLA;
+        };
+
         template <typename ValueType>
-        std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables) {
+        std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none) {
             // If the given input string looks like a file (containing a dot and there exists a file with that name),
             // we try to parse it as a file, otherwise we assume it's a region string.
             if (inputString.find(".") != std::string::npos && std::ifstream(inputString).good()) {
-                return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegionsFromFile(inputString, consideredVariables);
+                return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegionsFromFile(inputString, consideredVariables, splittingThreshold);
             } else {
-                return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegions(inputString, consideredVariables);
+                return storm::parser::ParameterRegionParser<ValueType>().parseMultipleRegions(inputString, consideredVariables, splittingThreshold);
             }
         }
-        
+
         template <typename ValueType>
-        std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, storm::models::ModelBase const& model) {
+        storm::storage::ParameterRegion<ValueType> createRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold= boost::none) {
+            return storm::parser::ParameterRegionParser<ValueType>().createRegion(inputString, consideredVariables, splittingThreshold);
+        }
+
+        template <typename ValueType>
+        std::vector<storm::storage::ParameterRegion<ValueType>> parseRegions(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
             std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
             if (model.isSparseModel()) {
                 auto const& sparseModel = dynamic_cast<storm::models::sparse::Model<ValueType> const&>(model);
@@ -53,23 +65,37 @@ namespace storm {
             } else {
                 STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Retrieving model parameters is not supported for the given model type.");
             }
-            return parseRegions<ValueType>(inputString, modelParameters);
+            return parseRegions<ValueType>(inputString, modelParameters, splittingThreshold);
+        }
+
+        template <typename ValueType>
+        std::vector<storm::storage::ParameterRegion<ValueType>> createRegion(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
+            std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
+            if (model.isSparseModel()) {
+                auto const& sparseModel = dynamic_cast<storm::models::sparse::Model<ValueType> const&>(model);
+                modelParameters = storm::models::sparse::getProbabilityParameters(sparseModel);
+                auto rewParameters = storm::models::sparse::getRewardParameters(sparseModel);
+                modelParameters.insert(rewParameters.begin(), rewParameters.end());
+            } else {
+                STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Retrieving model parameters is not supported for the given model type.");
+            }
+            return std::vector<storm::storage::ParameterRegion<ValueType>>({createRegion<ValueType>(inputString, modelParameters, splittingThreshold)});
         }
         
         template <typename ValueType>
-        storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables) {
+        storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> const& consideredVariables, boost::optional<int> splittingThreshold= boost::none) {
             // Handle the "empty region" case
             if (inputString == "" && consideredVariables.empty()) {
                 return storm::storage::ParameterRegion<ValueType>();
             }
             
-            auto res = parseRegions<ValueType>(inputString, consideredVariables);
+            auto res = parseRegions<ValueType>(inputString, consideredVariables, splittingThreshold);
             STORM_LOG_THROW(res.size() == 1, storm::exceptions::InvalidOperationException, "Parsed " << res.size() << " regions but exactly one was expected.");
             return res.front();
         }
         
         template <typename ValueType>
-        storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, storm::models::ModelBase const& model) {
+        storm::storage::ParameterRegion<ValueType> parseRegion(std::string const& inputString, storm::models::ModelBase const& model, boost::optional<int> splittingThreshold= boost::none) {
             // Handle the "empty region" case
             if (inputString == "" && !model.hasParameters()) {
                 return storm::storage::ParameterRegion<ValueType>();
@@ -80,15 +106,27 @@ namespace storm {
             return res.front();
         }
 
+        template <typename ValueType>
+        std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>> parseMonotoneParameters(std::string const& fileName, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model) {
+            std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType> modelParameters;
+            modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+            auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+            modelParameters.insert(rewParameters.begin(), rewParameters.end());
+            return std::move(storm::parser::MonotonicityParser<typename storm::storage::ParameterRegion<ValueType>::VariableType>().parseMonotoneVariablesFromFile(fileName, modelParameters));
+        }
+
         template <typename ParametricType, typename ConstantType>
-        std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> initializeParameterLiftingRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ParametricType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ParametricType> const& task, bool generateSplitEstimates = false, bool allowModelSimplification = true) {
+        std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> initializeParameterLiftingRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ParametricType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ParametricType> const& task, bool generateSplitEstimates = false, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>>> monotoneParameters = boost::none) {
 
             STORM_LOG_WARN_COND(storm::utility::parameterlifting::validateParameterLiftingSound(*model, task.getFormula()), "Could not validate whether parameter lifting is applicable. Please validate manually...");
+            STORM_LOG_WARN_COND(!(allowModelSimplification && monotonicitySetting.useMonotonicity), "Allowing model simplification when using monotonicity is not useful, as for monotonicity checking model simplification is done as preprocessing");
+            STORM_LOG_WARN_COND(!(monotoneParameters && !monotonicitySetting.useMonotonicity), "Setting monotone parameters without setting monotonicity usage doesn't work");
 
             std::shared_ptr<storm::models::sparse::Model<ParametricType>> consideredModel = model;
 
             // Treat continuous time models
             if (consideredModel->isOfType(storm::models::ModelType::Ctmc) || consideredModel->isOfType(storm::models::ModelType::MarkovAutomaton)) {
+                    STORM_LOG_WARN_COND(!monotonicitySetting.useMonotonicity, "Usage of monotonicity not supported for this type of model, continuing without montonicity checking");
                     STORM_LOG_WARN("Parameter lifting not supported for continuous time models. Transforming continuous model to discrete model...");
                     std::vector<std::shared_ptr<storm::logic::Formula const>> taskFormulaAsVector { task.getFormula().asSharedPointer() };
                     consideredModel = storm::api::transformContinuousToDiscreteTimeSparseModel(consideredModel, taskFormulaAsVector).first;
@@ -99,7 +137,14 @@ namespace storm {
             std::shared_ptr<storm::modelchecker::RegionModelChecker<ParametricType>> checker;
             if (consideredModel->isOfType(storm::models::ModelType::Dtmc)) {
                 checker = std::make_shared<storm::modelchecker::SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<ParametricType>, ConstantType>>();
+                checker->setUseMonotonicity(monotonicitySetting.useMonotonicity);
+                checker->setUseOnlyGlobal(monotonicitySetting.useOnlyGlobalMonotonicity);
+                checker->setUseBounds(monotonicitySetting.useBoundsFromPLA);
+                if (monotonicitySetting.useMonotonicity && monotoneParameters) {
+                    checker->setMonotoneParameters(monotoneParameters.get());
+                }
             } else if (consideredModel->isOfType(storm::models::ModelType::Mdp)) {
+                STORM_LOG_WARN_COND(!monotonicitySetting.useMonotonicity, "Usage of monotonicity not supported for this type of model, continuing without montonicity checking");
                 checker = std::make_shared<storm::modelchecker::SparseMdpParameterLiftingModelChecker<storm::models::sparse::Mdp<ParametricType>, ConstantType>>();
             } else {
                 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type.");
@@ -134,21 +179,21 @@ namespace storm {
             } else {
                 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Unable to perform parameterLifting on the provided model type.");
             }
-            
+
             checker->specify(env, consideredModel, task, generateSplitEstimates, allowModelSimplification);
-            
             return checker;
         }
         
         template <typename ValueType>
-        std::shared_ptr<storm::modelchecker::RegionModelChecker<ValueType>> initializeRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::modelchecker::RegionCheckEngine engine) {
+        std::shared_ptr<storm::modelchecker::RegionModelChecker<ValueType>> initializeRegionModelChecker(Environment const& env, std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::modelchecker::RegionCheckEngine engine, bool generateSplitEstimates = false, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>> monotoneParameters = boost::none) {
             switch (engine) {
+                // TODO: now we always use regionsplitestimates
                     case storm::modelchecker::RegionCheckEngine::ParameterLifting:
-                            return initializeParameterLiftingRegionModelChecker<ValueType, double>(env, model, task);
+                            return initializeParameterLiftingRegionModelChecker<ValueType, double>(env, model, task, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
                     case storm::modelchecker::RegionCheckEngine::ExactParameterLifting:
-                            return initializeParameterLiftingRegionModelChecker<ValueType, storm::RationalNumber>(env, model, task);
+                            return initializeParameterLiftingRegionModelChecker<ValueType, storm::RationalNumber>(env, model, task, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
                     case storm::modelchecker::RegionCheckEngine::ValidatingParameterLifting:
-                            return initializeValidatingRegionModelChecker<ValueType, double, storm::RationalNumber>(env, model, task);
+                            return initializeValidatingRegionModelChecker<ValueType, double, storm::RationalNumber>(env, model, task, generateSplitEstimates, allowModelSimplification);
                     default:
                             STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected region model checker type.");
             }
@@ -180,14 +225,18 @@ namespace storm {
          * @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
          * @param refinementDepthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
          * @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
+         * @param allowModelSimplification
+         * @param useMonotonicity
+         * @param monThresh if given, determines at which depth to start using monotonicity
          */
         template <typename ValueType>
-        std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> checkAndRefineRegionWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, boost::optional<ValueType> const& coverageThreshold, boost::optional<uint64_t> const& refinementDepthThreshold = boost::none, storm::modelchecker::RegionResultHypothesis hypothesis = storm::modelchecker::RegionResultHypothesis::Unknown) {
+        std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ValueType>> checkAndRefineRegionWithSparseEngine(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, boost::optional<ValueType> const& coverageThreshold, boost::optional<uint64_t> const& refinementDepthThreshold = boost::none, storm::modelchecker::RegionResultHypothesis hypothesis = storm::modelchecker::RegionResultHypothesis::Unknown, bool allowModelSimplification = true, MonotonicitySetting monotonicitySetting = MonotonicitySetting(), uint64_t monThresh = 0) {
             Environment env;
-            auto regionChecker = initializeRegionModelChecker(env, model, task, engine);
-            return regionChecker->performRegionRefinement(env, region, coverageThreshold, refinementDepthThreshold, hypothesis);
+            auto regionChecker = initializeRegionModelChecker(env, model, task, engine, true, allowModelSimplification, monotonicitySetting);
+            return regionChecker->performRegionRefinement(env, region, coverageThreshold, refinementDepthThreshold, hypothesis, monThresh);
         }
-    
+
+        // TODO: update documentation
         /*!
          * Finds the extremal value in the given region
          * @param engine The considered region checking engine
@@ -196,11 +245,28 @@ namespace storm {
          * @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
          */
         template <typename ValueType>
-        std::pair<ValueType, typename storm::storage::ParameterRegion<ValueType>::Valuation> computeExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision) {
+        std::pair<ValueType, typename storm::storage::ParameterRegion<ValueType>::Valuation> computeExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision, MonotonicitySetting monotonicitySetting, bool generateSplitEstimates = false, boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
             Environment env;
-            auto regionChecker = initializeRegionModelChecker(env, model, task, engine);
+            bool allowModelSimplification = !monotonicitySetting.useMonotonicity;
+            auto regionChecker = initializeRegionModelChecker(env, model, task, engine, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
             return regionChecker->computeExtremalValue(env, region, dir, precision.is_initialized() ? precision.get() : storm::utility::zero<ValueType>());
         }
+
+        // TODO: update documentation
+        /*!
+         * Checks if a given extremal value is indeed the extremal value in the given region
+         * @param engine The considered region checking engine
+         * @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
+         * @param refinementDepthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
+         * @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds (and NOT the complementary result).
+         */
+        template <typename ValueType>
+        bool checkExtremalValue(std::shared_ptr<storm::models::sparse::Model<ValueType>> const& model, storm::modelchecker::CheckTask<storm::logic::Formula, ValueType> const& task, storm::storage::ParameterRegion<ValueType> const& region, storm::modelchecker::RegionCheckEngine engine, storm::solver::OptimizationDirection const& dir, boost::optional<ValueType> const& precision, ValueType const& suggestion, MonotonicitySetting monotonicitySetting, bool generateSplitEstimates = false, boost::optional<std::pair<std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ValueType>::VariableType>>>& monotoneParameters = boost::none) {
+            Environment env;
+            bool allowModelSimplification = !monotonicitySetting.useMonotonicity;
+            auto regionChecker = initializeRegionModelChecker(env, model, task, engine, generateSplitEstimates, allowModelSimplification, monotonicitySetting, monotoneParameters);
+            return regionChecker->checkExtremalValue(env, region, dir, precision.is_initialized() ? precision.get() : storm::utility::zero<ValueType>(), suggestion);
+        }
         
         template <typename ValueType>
         void exportRegionCheckResultToFile(std::unique_ptr<storm::modelchecker::CheckResult> const& checkResult, std::string const& filename, bool onlyConclusiveResults = false) {
@@ -211,6 +277,7 @@ namespace storm {
             std::ofstream filestream;
             storm::utility::openFile(filename, filestream);
             for (auto const& res : regionCheckResult->getRegionResults()) {
+
                 if (!onlyConclusiveResults || res.second == storm::modelchecker::RegionResult::AllViolated || res.second == storm::modelchecker::RegionResult::AllSat) {
                     filestream << res.second << ": " << res.first << std::endl;
                 }
diff --git a/src/storm-pars/api/storm-pars.h b/src/storm-pars/api/storm-pars.h
index a1a32ac05..bea608840 100644
--- a/src/storm-pars/api/storm-pars.h
+++ b/src/storm-pars/api/storm-pars.h
@@ -1,4 +1,5 @@
 #pragma once
 
 #include "storm-pars/api/region.h"
-#include "storm-pars/api/export.h"
\ No newline at end of file
+#include "storm-pars/api/export.h"
+#include "storm-pars/api/analysis.h"
diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp
index 23539650d..e97005830 100644
--- a/src/storm-pars/modelchecker/region/RegionModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/RegionModelChecker.cpp
@@ -1,21 +1,19 @@
 #include <sstream>
 #include <queue>
 
+#include "storm-pars/analysis/OrderExtender.cpp"
 #include "storm-pars/modelchecker/region/RegionModelChecker.h"
 
 #include "storm/adapters/RationalFunctionAdapter.h"
 
-
-#include "storm/utility/vector.h"
 #include "storm/models/sparse/StandardRewardModel.h"
 #include "storm/models/sparse/Dtmc.h"
-#include "storm/models/sparse/Mdp.h"
 
 #include "storm/settings/SettingsManager.h"
 #include "storm/settings/modules/CoreSettings.h"
+
 #include "storm/exceptions/NotImplementedException.h"
 #include "storm/exceptions/NotSupportedException.h"
-#include "storm/exceptions/InvalidStateException.h"
 #include "storm/exceptions/InvalidArgumentException.h"
 
 
@@ -50,7 +48,7 @@ namespace storm {
             }
         
             template <typename ParametricType>
-            std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionModelChecker<ParametricType>::performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis) {
+            std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> RegionModelChecker<ParametricType>::performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold, RegionResultHypothesis const& hypothesis, uint64_t monThresh) {
                 STORM_LOG_INFO("Applying refinement on region: " << region.toString(true) << " .");
                 
                 auto thresholdAsCoefficient = coverageThreshold ? storm::utility::convertNumber<CoefficientType>(coverageThreshold.get()) : storm::utility::zero<CoefficientType>();
@@ -58,16 +56,18 @@ namespace storm {
                 auto fractionOfUndiscoveredArea = storm::utility::one<CoefficientType>();
                 auto fractionOfAllSatArea = storm::utility::zero<CoefficientType>();
                 auto fractionOfAllViolatedArea = storm::utility::zero<CoefficientType>();
+                numberOfRegionsKnownThroughMonotonicity = 0;
                 
                 // The resulting (sub-)regions
                 std::vector<std::pair<storm::storage::ParameterRegion<ParametricType>, RegionResult>> result;
                 
                 // FIFO queues storing the data for the regions that we still need to process.
                 std::queue<std::pair<storm::storage::ParameterRegion<ParametricType>, RegionResult>> unprocessedRegions;
+
                 std::queue<uint64_t> refinementDepths;
                 unprocessedRegions.emplace(region, RegionResult::Unknown);
                 refinementDepths.push(0);
-                
+
                 uint_fast64_t numOfAnalyzedRegions = 0;
                 CoefficientType displayedProgress = storm::utility::zero<CoefficientType>();
                 if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
@@ -84,13 +84,17 @@ namespace storm {
                     displayedProgress = storm::utility::zero<CoefficientType>();
                 }
 
-                while (fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
+                // NORMAL WHILE LOOP
+                uint64_t currentDepth = refinementDepths.front();
+                while ((!useMonotonicity || currentDepth < monThresh) && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
                     assert(unprocessedRegions.size() == refinementDepths.size());
-                    uint64_t currentDepth = refinementDepths.front();
                     STORM_LOG_INFO("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentDepth << "; " << storm::utility::convertNumber<double>(fractionOfUndiscoveredArea) * 100 << "% still unknown)");
                     auto& currentRegion = unprocessedRegions.front().first;
                     auto& res = unprocessedRegions.front().second;
+                    std::shared_ptr<storm::analysis::Order> order;
+                    std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult;
                     res = analyzeRegion(env, currentRegion, hypothesis, res, false);
+
                     switch (res) {
                         case RegionResult::AllSat:
                             fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
@@ -106,11 +110,167 @@ namespace storm {
                             // Split the region as long as the desired refinement depth is not reached.
                             if (!depthThreshold || currentDepth < depthThreshold.get()) {
                                 std::vector<storm::storage::ParameterRegion<ParametricType>> newRegions;
+                                RegionResult initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
+                                                                    ((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
+                                                                     RegionResult::Unknown);
+
                                 currentRegion.split(currentRegion.getCenterPoint(), newRegions);
+
+                                bool first = true;
+                                for (auto& newRegion : newRegions) {
+                                    unprocessedRegions.emplace(std::move(newRegion), initResForNewRegions);
+                                    refinementDepths.push(currentDepth + 1);
+                                }
+
+                            } else {
+                                // If the region is not further refined, it is still added to the result
+                                result.push_back(std::move(unprocessedRegions.front()));
+                            }
+                            break;
+                    }
+                    ++numOfAnalyzedRegions;
+                    unprocessedRegions.pop();
+                    refinementDepths.pop();
+                    if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
+                        while (displayedProgress < storm::utility::one<CoefficientType>() - fractionOfUndiscoveredArea) {
+                            STORM_PRINT_AND_LOG("#");
+                            displayedProgress += storm::utility::convertNumber<CoefficientType>(0.01);
+                        }
+                    }
+                    currentDepth = refinementDepths.front();
+                }
+
+                // FIFO queues for the order and local monotonicity results
+                std::queue<std::shared_ptr<storm::analysis::Order>> orders;
+                std::queue<std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>> localMonotonicityResults;
+                std::shared_ptr<storm::analysis::Order> order;
+                std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult;
+                if (useMonotonicity && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
+                    storm::utility::Stopwatch monWatch(true);
+
+                    orders.emplace(extendOrder(env, nullptr, region));
+                    assert (orders.front() != nullptr);
+                    auto monRes = std::shared_ptr< storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(orders.front()->getNumberOfStates()));
+                    extendLocalMonotonicityResult(region, orders.front(), monRes);
+                    localMonotonicityResults.emplace(monRes);
+                    order = orders.front();
+                    localMonotonicityResult = localMonotonicityResults.front();
+
+                    if (!order->getDoneBuilding()) {
+                        // we need to use copies for both order and local mon res
+                        while(unprocessedRegions.size() > orders.size()){
+                            orders.emplace(order->copy());
+                            localMonotonicityResults.emplace(localMonotonicityResult->copy());
+                        }
+                    } else if (!localMonotonicityResult->isDone()) {
+                        // the order will not change anymore
+                        while(unprocessedRegions.size() > orders.size()) {
+                            orders.emplace(order);
+                            localMonotonicityResults.emplace(localMonotonicityResult->copy());
+                        }
+                    } else {
+                        // both will not change anymore
+                        while (unprocessedRegions.size() > orders.size()) {
+                            orders.emplace(order);
+                            localMonotonicityResults.emplace(localMonotonicityResult);
+                        }
+                    }
+                    monWatch.stop();
+                    STORM_PRINT(std::endl << "Time for orderBuilding and monRes initialization: " << monWatch << "." << std::endl << std::endl);
+                }
+                bool useSameOrder = useMonotonicity && order->getDoneBuilding();
+                bool useSameLocalMonotonicityResult = useSameOrder && localMonotonicityResult->isDone();
+
+                // USEMON WHILE LOOP
+                while (useMonotonicity && fractionOfUndiscoveredArea > thresholdAsCoefficient && !unprocessedRegions.empty()) {
+                    assert ((useSameLocalMonotonicityResult && localMonotonicityResults.size() == 1)|| unprocessedRegions.size() == localMonotonicityResults.size());
+                    assert ((useSameOrder && orders.size() == 1) || unprocessedRegions.size() == orders.size());
+                    assert(unprocessedRegions.size() == refinementDepths.size());
+                    currentDepth = refinementDepths.front();
+                    STORM_LOG_INFO("Analyzing region #" << numOfAnalyzedRegions << " (Refinement depth " << currentDepth << "; " << storm::utility::convertNumber<double>(fractionOfUndiscoveredArea) * 100 << "% still unknown)");
+                    auto& currentRegion = unprocessedRegions.front().first;
+                    auto& res = unprocessedRegions.front().second;
+
+                    assert(!orders.empty());
+                    if (!useSameOrder) {
+                        order = orders.front();
+                        if (!order->getDoneBuilding()) {
+                            extendOrder(env, order, currentRegion);
+                        }
+                    }
+                    if (!useSameLocalMonotonicityResult) {
+                        localMonotonicityResult = localMonotonicityResults.front();
+                        if (!localMonotonicityResult->isDone()) {
+                            extendLocalMonotonicityResult(currentRegion, order, localMonotonicityResult);
+                        }
+                    }
+
+                    res = analyzeRegion(env, currentRegion, hypothesis, res, false, order, localMonotonicityResult);
+
+                    switch (res) {
+                        case RegionResult::AllSat:
+                            fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
+                            fractionOfAllSatArea += currentRegion.area() / areaOfParameterSpace;
+                            STORM_LOG_INFO("Region " << unprocessedRegions.front() << " is AllSat");
+                            result.push_back(std::move(unprocessedRegions.front()));
+                            break;
+                        case RegionResult::AllViolated:
+                            fractionOfUndiscoveredArea -= currentRegion.area() / areaOfParameterSpace;
+                            fractionOfAllViolatedArea += currentRegion.area() / areaOfParameterSpace;
+                            STORM_LOG_INFO("Region " << unprocessedRegions.front() << " is AllViolated");
+
+                            result.push_back(std::move(unprocessedRegions.front()));
+                            break;
+                        default:
+                            // Split the region as long as the desired refinement depth is not reached.
+                            if (!depthThreshold || currentDepth < depthThreshold.get()) {
+                                std::vector<storm::storage::ParameterRegion<ParametricType>> newRegions;
                                 RegionResult initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
-                                                                         ((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
-                                                                          RegionResult::Unknown);
+                                                                    ((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
+                                                                     RegionResult::Unknown);
+
+                                std::vector<storm::storage::ParameterRegion<ParametricType>> newKnownRegions;
+                                // Only split in (non)monotone vars
+                                splitSmart(currentRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), false);
+                                assert (newRegions.size() != 0);
+
+                                initResForNewRegions = (res == RegionResult::CenterSat) ? RegionResult::ExistsSat :
+                                                       ((res == RegionResult::CenterViolated) ? RegionResult::ExistsViolated :
+                                                        RegionResult::Unknown);
+                                bool first = true;
                                 for (auto& newRegion : newRegions) {
+                                    if (!useSameOrder) {
+                                        if (first) {
+                                            orders.emplace(order);
+                                            localMonotonicityResults.emplace(localMonotonicityResult);
+                                            first = false;
+                                        } else {
+                                            if (!order->getDoneBuilding()) {
+                                                // we need to use copies for both order and local mon res
+                                                orders.emplace(order->copy());
+                                                localMonotonicityResults.emplace(localMonotonicityResult->copy());
+                                            } else if (!localMonotonicityResult->isDone()) {
+                                                // the order will not change anymore
+                                                orders.emplace(order);
+                                                localMonotonicityResults.emplace(localMonotonicityResult->copy());
+                                            } else {
+                                                // both will not change anymore
+                                                orders.emplace(order);
+                                                localMonotonicityResults.emplace(localMonotonicityResult);
+                                            }
+                                        }
+                                    } else if (!useSameLocalMonotonicityResult) {
+                                        if (first) {
+                                            localMonotonicityResults.emplace(localMonotonicityResult);
+                                            first = false;
+                                        } else {
+                                            if (!localMonotonicityResult->isDone()) {
+                                                localMonotonicityResults.emplace(localMonotonicityResult->copy());
+                                            } else {
+                                                localMonotonicityResults.emplace(localMonotonicityResult);
+                                            }
+                                        }
+                                    }
                                     unprocessedRegions.emplace(std::move(newRegion), initResForNewRegions);
                                     refinementDepths.push(currentDepth + 1);
                                 }
@@ -120,9 +280,17 @@ namespace storm {
                             }
                             break;
                     }
+
                     ++numOfAnalyzedRegions;
                     unprocessedRegions.pop();
                     refinementDepths.pop();
+                    if (!useSameOrder) {
+                        orders.pop();
+                    }
+                    if (!useSameLocalMonotonicityResult) {
+                        localMonotonicityResults.pop();
+                    }
+
                     if (storm::settings::getModule<storm::settings::modules::CoreSettings>().isShowStatisticsSet()) {
                         while (displayedProgress < storm::utility::one<CoefficientType>() - fractionOfUndiscoveredArea) {
                             STORM_PRINT_AND_LOG("#");
@@ -146,18 +314,35 @@ namespace storm {
                     
                     STORM_PRINT_AND_LOG("Region Refinement Statistics:" << std::endl);
                     STORM_PRINT_AND_LOG("    Analyzed a total of " << numOfAnalyzedRegions << " regions." << std::endl);
+
+                    if (useMonotonicity) {
+                        STORM_PRINT_AND_LOG("    " << numberOfRegionsKnownThroughMonotonicity << " regions where discovered with help of monotonicity." << std::endl);
+
+                    }
                 }
                 
                 auto regionCopyForResult = region;
                 return std::make_unique<storm::modelchecker::RegionRefinementCheckResult<ParametricType>>(std::move(result), std::move(regionCopyForResult));
             }
 
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::extendLocalMonotonicityResult(storm::storage::ParameterRegion<ParametricType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult){
+            STORM_LOG_WARN("Initializing local Monotonicity Results not implemented for RegionModelChecker.");
+        }
+
         template <typename ParametricType>
         std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation> RegionModelChecker<ParametricType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision) {
             STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Computing extremal values is not supported for this region model checker.");
             return std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation>();
         }
 
+        template <typename ParametricType>
+        bool RegionModelChecker<ParametricType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision, ParametricType const& valueToCheck) {
+            STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Checking extremal values is not supported for this region model checker.");
+            return false;
+        }
+
         
         template <typename ParametricType>
         bool RegionModelChecker<ParametricType>::isRegionSplitEstimateSupported() const {
@@ -170,7 +355,63 @@ namespace storm {
             return std::map<typename RegionModelChecker<ParametricType>::VariableType, double>();
         }
 
-    
+        template <typename ParametricType>
+        std::shared_ptr<storm::analysis::Order> RegionModelChecker<ParametricType>::extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ParametricType> region) {
+            STORM_LOG_WARN("Extending order for RegionModelChecker not implemented");
+            // Does nothing
+            return order;
+        }
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::setConstantEntries(std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
+            STORM_LOG_WARN("Setting constant entries fo local monotonicity result not implemented");
+            // Does nothing
+        }
+
+        template <typename ParametricType>
+        bool RegionModelChecker<ParametricType>::isUseMonotonicitySet() const{
+            return useMonotonicity;
+        }
+
+        template <typename ParametricType>
+        bool RegionModelChecker<ParametricType>::isUseBoundsSet() {
+            return useBounds;
+        }
+
+        template <typename ParametricType>
+        bool RegionModelChecker<ParametricType>::isOnlyGlobalSet() {
+            return useOnlyGlobal;
+        }
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::setUseMonotonicity(bool monotonicity) {
+            this->useMonotonicity = monotonicity;
+        }
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::setUseBounds(bool bounds) {
+            assert (!bounds || useMonotonicity);
+            this->useBounds = bounds;
+        }
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::setUseOnlyGlobal(bool global) {
+            assert (!global || useMonotonicity);
+            this->useOnlyGlobal = global;
+        }
+
+        template <typename ParametricType>
+        void RegionModelChecker<ParametricType>::splitSmart(storm::storage::ParameterRegion<ParametricType> & currentRegion, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector, std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const {
+            STORM_LOG_WARN("Smart splitting for this model checker not implemented");
+            currentRegion.split(currentRegion.getCenterPoint(), regionVector);
+        }
+
+        template<typename ParametricType>
+        void RegionModelChecker<ParametricType>::setMonotoneParameters(std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneParameters) {
+            monotoneIncrParameters = std::move(monotoneParameters.first);
+            monotoneDecrParameters = std::move(monotoneParameters.second);
+        }
+
 #ifdef STORM_HAVE_CARL
             template class RegionModelChecker<storm::RationalFunction>;
 #endif
diff --git a/src/storm-pars/modelchecker/region/RegionModelChecker.h b/src/storm-pars/modelchecker/region/RegionModelChecker.h
index df3d670f9..d4b8c8325 100644
--- a/src/storm-pars/modelchecker/region/RegionModelChecker.h
+++ b/src/storm-pars/modelchecker/region/RegionModelChecker.h
@@ -2,6 +2,9 @@
 
 #include <memory>
 
+#include "storm-pars/analysis/Order.h"
+#include "storm-pars/analysis/OrderExtender.h"
+#include "storm-pars/analysis/LocalMonotonicityResult.h"
 #include "storm-pars/modelchecker/results/RegionCheckResult.h"
 #include "storm-pars/modelchecker/results/RegionRefinementCheckResult.h"
 #include "storm-pars/modelchecker/region/RegionResult.h"
@@ -37,7 +40,7 @@ namespace storm {
              * @param initialResult encodes what is already known about this region
              * @param sampleVerticesOfRegion enables sampling of the vertices of the region in cases where AllSat/AllViolated could not be shown.
              */
-            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) = 0;
+            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult = nullptr) = 0;
             
              /*!
              * Analyzes the given regions.
@@ -55,17 +58,19 @@ namespace storm {
              * @param coverageThreshold if given, the refinement stops as soon as the fraction of the area of the subregions with inconclusive result is less then this threshold
              * @param depthThreshold if given, the refinement stops at the given depth. depth=0 means no refinement.
              * @param hypothesis if not 'unknown', it is only checked whether the hypothesis holds within the given region.
-             *
+             * @param monThresh if given, determines at which depth to start using monotonicity
              */
-            std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold = boost::none, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown);
-            
+            std::unique_ptr<storm::modelchecker::RegionRefinementCheckResult<ParametricType>> performRegionRefinement(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, boost::optional<ParametricType> const& coverageThreshold, boost::optional<uint64_t> depthThreshold = boost::none, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, uint64_t monThresh = 0);
+
+            // TODO: documentation
             /*!
              * Finds the extremal value within the given region and with the given precision.
              * The returned value v corresponds to the value at the returned valuation.
              * The actual maximum (minimum) lies in the interval [v, v+precision] ([v-precision, v])
              */
             virtual std::pair<ParametricType, typename storm::storage::ParameterRegion<ParametricType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision);
-            
+            virtual bool checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dir, ParametricType const& precision, ParametricType const& valueToCheck);
+
             /*!
              * Returns true if region split estimation (a) was enabled when model and check task have been specified and (b) is supported by this region model checker.
              */
@@ -76,7 +81,36 @@ namespace storm {
              * If a parameter is assigned a high value, we should prefer splitting with respect to this parameter.
              */
             virtual std::map<VariableType, double> getRegionSplitEstimate() const;
-            
+
+            virtual std::shared_ptr<storm::analysis::Order> extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ParametricType> region);
+
+            virtual void setConstantEntries(std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
+
+            bool isUseMonotonicitySet() const;
+            bool isUseBoundsSet();
+            bool isOnlyGlobalSet();
+
+            void setUseMonotonicity(bool monotonicity = true);
+            void setUseBounds(bool bounds = true);
+            void setUseOnlyGlobal(bool global = true);
+
+            void setMonotoneParameters(std::pair<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>, std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneParameters);
+
+        private:
+            bool useMonotonicity = false;
+            bool useOnlyGlobal = false;
+            bool useBounds = false;
+
+        protected:
+
+            uint_fast64_t numberOfRegionsKnownThroughMonotonicity;
+            boost::optional<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneIncrParameters;
+            boost::optional<std::set<typename storm::storage::ParameterRegion<ParametricType>::VariableType>> monotoneDecrParameters;
+
+            virtual void extendLocalMonotonicityResult(storm::storage::ParameterRegion<ParametricType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
+
+            virtual void splitSmart(storm::storage::ParameterRegion<ParametricType> & region, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector, std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const;
+
         };
 
     } //namespace modelchecker
diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp
index 1b938f717..ca9523cd5 100644
--- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.cpp
@@ -38,7 +38,7 @@ namespace storm {
         }
         
         template <typename SparseModelType, typename ConstantType>
-        bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) const {
+        bool SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask) const {
             bool result = parametricModel->isOfType(storm::models::ModelType::Dtmc);
             result &= parametricModel->isSparseModel();
             result &= parametricModel->supportsParameters();
@@ -47,21 +47,25 @@ namespace storm {
             result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
             return result;
         }
-        
+
         template <typename SparseModelType, typename ConstantType>
-        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplification) {
+        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplification) {
             auto dtmc = parametricModel->template as<SparseModelType>();
+            monotonicityChecker = std::make_unique<storm::analysis::MonotonicityChecker<ValueType>>(dtmc->getTransitionMatrix());
             specify_internal(env, dtmc, checkTask, generateRegionSplitEstimates, !allowModelSimplification);
+            if (checkTask.isBoundSet()) {
+                thresholdTask = storm::utility::convertNumber<ConstantType>(checkTask.getBoundThreshold());
+            }
         }
-        
+
         template <typename SparseModelType, typename ConstantType>
-        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
+        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
             STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
-            
+
             reset();
-            
+
             regionSplitEstimationsEnabled = generateRegionSplitEstimates;
-            
+
             if (skipModelSimplification) {
                 this->parametricModel = parametricModel;
                 this->specifyFormula(env, checkTask);
@@ -74,11 +78,11 @@ namespace storm {
                 this->specifyFormula(env, checkTask.substituteFormula(*simplifier.getSimplifiedFormula()));
             }
         }
-        
-        
+
+
+
         template <typename SparseModelType, typename ConstantType>
         void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyBoundedUntilFormula(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ConstantType> const& checkTask) {
-            
             // get the step bound
             STORM_LOG_THROW(!checkTask.getFormula().hasLowerBound(), storm::exceptions::NotSupportedException, "Lower step bounds are not supported.");
             STORM_LOG_THROW(checkTask.getFormula().hasUpperBound(), storm::exceptions::NotSupportedException, "Expected a bounded until formula with an upper bound.");
@@ -108,9 +112,8 @@ namespace storm {
             // if there are maybestates, create the parameterLifter
             if (!maybeStates.empty()) {
                 // Create the vector of one-step probabilities to go to target states.
-                std::vector<typename SparseModelType::ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates);
-                
-                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
+                std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), psiStates);
+                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, false, RegionModelChecker<ValueType>::isUseMonotonicitySet());
             }
             
             // We know some bounds for the results so set them
@@ -118,11 +121,14 @@ namespace storm {
             upperResultBound = storm::utility::one<ConstantType>();
             // No requirements for bounded formulas
             solverFactory->setRequirementsChecked(true);
+
+            // For monotonicity checking
+            std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(this->parametricModel->getBackwardTransitions(), phiStates, psiStates);
+            this->orderExtender = storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
         }
 
         template <typename SparseModelType, typename ConstantType>
         void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyUntilFormula(Environment const& env, CheckTask<storm::logic::UntilFormula, ConstantType> const& checkTask) {
-            
             // get the results for the subformulas
             storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
             STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getLeftSubformula()) && propositionalChecker.canHandle(checkTask.getFormula().getRightSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
@@ -140,9 +146,8 @@ namespace storm {
             // if there are maybestates, create the parameterLifter
             if (!maybeStates.empty()) {
                 // Create the vector of one-step probabilities to go to target states.
-                std::vector<typename SparseModelType::ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second);
-                
-                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
+                std::vector<ValueType> b = this->parametricModel->getTransitionMatrix().getConstrainedRowSumVector(storm::storage::BitVector(this->parametricModel->getTransitionMatrix().getRowCount(), true), statesWithProbability01.second);
+                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled, RegionModelChecker<ValueType>::isUseMonotonicitySet());
             }
             
             // We know some bounds for the results so set them
@@ -154,16 +159,16 @@ namespace storm {
             req.clearBounds();
             STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException, "Solver requirements " + req.getEnabledRequirementsAsString() + " not checked.");
             solverFactory->setRequirementsChecked(true);
+
+            this->orderExtender =  storm::analysis::OrderExtender<ValueType,ConstantType>(&statesWithProbability01.second, &statesWithProbability01.first, this->parametricModel->getTransitionMatrix());
         }
 
         template <typename SparseModelType, typename ConstantType>
         void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyReachabilityRewardFormula(Environment const& env, CheckTask<storm::logic::EventuallyFormula, ConstantType> const& checkTask) {
-            
             // get the results for the subformula
             storm::modelchecker::SparsePropositionalModelChecker<SparseModelType> propositionalChecker(*this->parametricModel);
             STORM_LOG_THROW(propositionalChecker.canHandle(checkTask.getFormula().getSubformula()), storm::exceptions::NotSupportedException, "Parameter lifting with non-propositional subformulas is not supported");
             storm::storage::BitVector targetStates = std::move(propositionalChecker.check(checkTask.getFormula().getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector());
-            
             // get the maybeStates
             storm::storage::BitVector infinityStates = storm::utility::graph::performProb1(this->parametricModel->getBackwardTransitions(), storm::storage::BitVector(this->parametricModel->getNumberOfStates(), true), targetStates);
             infinityStates.complement();
@@ -180,9 +185,9 @@ namespace storm {
 
                 typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
 
-                std::vector<typename SparseModelType::ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
+                std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
                 
-                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
+                parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates, regionSplitEstimationsEnabled);
             }
             
             // We only know a lower bound for the result
@@ -203,7 +208,6 @@ namespace storm {
 
         template <typename SparseModelType, typename ConstantType>
         void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::specifyCumulativeRewardFormula(Environment const& env, CheckTask<storm::logic::CumulativeRewardFormula, ConstantType> const& checkTask) {
-            
             // Obtain the stepBound
             stepBound = checkTask.getFormula().getBound().evaluateAsInt();
             if (checkTask.getFormula().isBoundStrict()) {
@@ -219,10 +223,9 @@ namespace storm {
             // Create the reward vector
             STORM_LOG_THROW((checkTask.isRewardModelSet() && this->parametricModel->hasRewardModel(checkTask.getRewardModel())) || (!checkTask.isRewardModelSet() && this->parametricModel->hasUniqueRewardModel()), storm::exceptions::InvalidPropertyException, "The reward model specified by the CheckTask is not available in the given model.");
             typename SparseModelType::RewardModelType const& rewardModel = checkTask.isRewardModelSet() ? this->parametricModel->getRewardModel(checkTask.getRewardModel()) : this->parametricModel->getUniqueRewardModel();
-            std::vector<typename SparseModelType::ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
-            
-            parameterLifter = std::make_unique<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
+            std::vector<ValueType> b = rewardModel.getTotalRewardVector(this->parametricModel->getTransitionMatrix());
             
+            parameterLifter = std::make_unique<storm::transformer::ParameterLifter<ValueType, ConstantType>>(this->parametricModel->getTransitionMatrix(), b, maybeStates, maybeStates);
             
             // We only know a lower bound for the result
             lowerResultBound = storm::utility::zero<ConstantType>();
@@ -231,25 +234,44 @@ namespace storm {
             solverFactory->setRequirementsChecked(true);
         }
         
+        template <typename SparseModelType, typename ConstantType>
+        storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
+            if (!instantiationCheckerSAT) {
+                instantiationCheckerSAT = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
+                instantiationCheckerSAT->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
+                instantiationCheckerSAT->setInstantiationsAreGraphPreserving(true);
+            }
+            return *instantiationCheckerSAT;
+        }
+
+        template <typename SparseModelType, typename ConstantType>
+        storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
+            if (!instantiationCheckerVIO) {
+                instantiationCheckerVIO = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
+                instantiationCheckerVIO->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
+                instantiationCheckerVIO->setInstantiationsAreGraphPreserving(true);
+            }
+            return *instantiationCheckerVIO;
+        }
+
         template <typename SparseModelType, typename ConstantType>
         storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationChecker() {
             if (!instantiationChecker) {
                 instantiationChecker = std::make_unique<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>>(*this->parametricModel);
-                instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType<typename SparseModelType::ValueType>());
+                instantiationChecker->specifyFormula(this->currentCheckTask->template convertValueType<ValueType>());
                 instantiationChecker->setInstantiationsAreGraphPreserving(true);
             }
             return *instantiationChecker;
         }
         
         template <typename SparseModelType, typename ConstantType>
-        std::unique_ptr<CheckResult> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
-            
+        std::unique_ptr<CheckResult> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
+
             if (maybeStates.empty()) {
                 return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(resultsForNonMaybeStates);
             }
-            
             parameterLifter->specifyRegion(region, dirForParameters);
-            
+
             if (stepBound) {
                 assert(*stepBound > 0);
                 x = std::vector<ConstantType>(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
@@ -267,37 +289,96 @@ namespace storm {
                     std::vector<ConstantType> oneStepProbs;
                     oneStepProbs.reserve(parameterLifter->getMatrix().getRowCount());
                     for (uint64_t row = 0; row < parameterLifter->getMatrix().getRowCount(); ++row) {
-                        oneStepProbs.push_back(storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row));
+                        oneStepProbs.push_back(
+                                storm::utility::one<ConstantType>() - parameterLifter->getMatrix().getRowSum(row));
                     }
                     if (dirForParameters == storm::OptimizationDirection::Minimize) {
-                        storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
+                        storm::modelchecker::helper::DsMpiMdpUpperRewardBoundsComputer<ConstantType> dsmpi(
+                                parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
                         solver->setUpperBounds(dsmpi.computeUpperBounds());
                     } else {
-                        storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
+                        storm::modelchecker::helper::BaierUpperRewardBoundsComputer<ConstantType> baier(
+                                parameterLifter->getMatrix(), parameterLifter->getVector(), oneStepProbs);
                         solver->setUpperBound(baier.computeUpperBound());
                     }
                 }
                 solver->setTrackScheduler(true);
-                if (storm::solver::minimize(dirForParameters) && minSchedChoices) solver->setInitialScheduler(std::move(minSchedChoices.get()));
-                if (storm::solver::maximize(dirForParameters) && maxSchedChoices) solver->setInitialScheduler(std::move(maxSchedChoices.get()));
+
+                if (localMonotonicityResult != nullptr && !this->isOnlyGlobalSet()) {
+                    storm::storage::BitVector fixedStates(parameterLifter->getRowGroupCount(), false);
+
+                    bool useMinimize = storm::solver::minimize(dirForParameters);
+                    if (useMinimize && !minSchedChoices) {
+                        minSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
+                    }
+                    if (!useMinimize && !maxSchedChoices) {
+                        maxSchedChoices = std::vector<uint_fast64_t>(parameterLifter->getRowGroupCount(), 0);
+                    }
+
+                    // TODO: this only works since we decided to keep all columns
+                    auto const & occuringVariables = parameterLifter->getOccurringVariablesAtState();
+                    for (auto state = 0; state < parameterLifter->getRowGroupCount(); ++state) {
+                        auto oldStateNumber = parameterLifter->getOriginalStateNumber(state);
+                        auto& variables = occuringVariables.at(oldStateNumber);
+                        // point at which we start with rows for this state
+
+                        STORM_LOG_THROW(variables.size() <= 1, storm::exceptions::NotImplementedException, "Using localMonRes not yet implemented for states with 2 or more variables, please run without --use-monotonicity");
+
+                        bool allMonotone = true;
+                        for (auto var : variables) {
+                            auto monotonicity = localMonotonicityResult->getMonotonicity(oldStateNumber, var);
+
+                            bool ignoreUpperBound = monotonicity == Monotonicity::Constant || (useMinimize && monotonicity == Monotonicity::Incr) || (!useMinimize && monotonicity == Monotonicity::Decr);
+                            bool ignoreLowerBound = !ignoreUpperBound && ((useMinimize && monotonicity == Monotonicity::Decr) || (!useMinimize && monotonicity == Monotonicity::Incr));
+                            allMonotone &= (ignoreUpperBound || ignoreLowerBound);
+                            if (ignoreLowerBound) {
+                                if (useMinimize) {
+                                    minSchedChoices.get()[state] = 1;
+                                } else {
+                                    maxSchedChoices.get()[state] = 1;
+                                }
+                            } else if (ignoreUpperBound) {
+                                if (useMinimize) {
+                                    minSchedChoices.get()[state] = 0;
+                                } else {
+                                    maxSchedChoices.get()[state] = 0;
+                                }
+                            }
+                        }
+                        if (allMonotone) {
+                            fixedStates.set(state);
+                        }
+                    }
+                    solver->setFixedStates(std::move(fixedStates));
+                }
+
+                if (storm::solver::minimize(dirForParameters) && minSchedChoices)
+                    solver->setInitialScheduler(std::move(minSchedChoices.get()));
+                if (storm::solver::maximize(dirForParameters) && maxSchedChoices)
+                    solver->setInitialScheduler(std::move(maxSchedChoices.get()));
                 if (this->currentCheckTask->isBoundSet() && solver->hasInitialScheduler()) {
                     // If we reach this point, we know that after applying the hint, the x-values can only become larger (if we maximize) or smaller (if we minimize).
                     std::unique_ptr<storm::solver::TerminationCondition<ConstantType>> termCond;
-                    storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet() ? this->parametricModel->getInitialStates() % maybeStates : storm::storage::BitVector(maybeStates.getNumberOfSetBits(), true);
+                    storm::storage::BitVector relevantStatesInSubsystem = this->currentCheckTask->isOnlyInitialStatesRelevantSet()
+                                                                          ? this->parametricModel->getInitialStates() %
+                                                                            maybeStates : storm::storage::BitVector(
+                                    maybeStates.getNumberOfSetBits(), true);
                     if (storm::solver::minimize(dirForParameters)) {
                         // Terminate if the value for ALL relevant states is already below the threshold
-                        termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumBelowThreshold<ConstantType>> (relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), false);
+                        termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumBelowThreshold<ConstantType>>(
+                                relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), false);
                     } else {
                         // Terminate if the value for ALL relevant states is already above the threshold
-                        termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumExceedsThreshold<ConstantType>> (relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), true);
+                        termCond = std::make_unique<storm::solver::TerminateIfFilteredExtremumExceedsThreshold<ConstantType>>(
+                                relevantStatesInSubsystem, true, this->currentCheckTask->getBoundThreshold(), true);
                     }
                     solver->setTerminationCondition(std::move(termCond));
                 }
-            
+
                 // Invoke the solver
                 x.resize(maybeStates.getNumberOfSetBits(), storm::utility::zero<ConstantType>());
                 solver->solveEquations(env, dirForParameters, x, parameterLifter->getVector());
-                if(storm::solver::minimize(dirForParameters)) {
+                if (storm::solver::minimize(dirForParameters)) {
                     minSchedChoices = solver->getSchedulerChoices();
                 } else {
                     maxSchedChoices = solver->getSchedulerChoices();
@@ -306,6 +387,7 @@ namespace storm {
                     computeRegionSplitEstimates(x, solver->getSchedulerChoices(), region, dirForParameters);
                 }
             }
+
             
             // Get the result for the complete model (including maybestates)
             std::vector<ConstantType> result = resultsForNonMaybeStates;
@@ -318,8 +400,8 @@ namespace storm {
         }
         
         template <typename SparseModelType, typename ConstantType>
-        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
-            std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> deltaLower, deltaUpper;
+        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
+            std::map<VariableType, double> deltaLower, deltaUpper;
             for (auto const& p : region.getVariables()) {
                 deltaLower.insert(std::make_pair(p, 0.0));
                 deltaUpper.insert(std::make_pair(p, 0.0));
@@ -327,7 +409,11 @@ namespace storm {
             auto const& choiceValuations = parameterLifter->getRowLabels();
             auto const& matrix = parameterLifter->getMatrix();
             auto const& vector = parameterLifter->getVector();
-            
+
+            auto i = 0;
+            for (auto & x : vector) {
+                ++i;
+            }
             std::vector<ConstantType> stateResults;
             for (uint64_t state = 0; state < schedulerChoices.size(); ++state) {
                 uint64_t rowOffset = matrix.getRowGroupIndices()[state];
@@ -338,12 +424,13 @@ namespace storm {
                 for (uint64_t row = rowOffset; row < matrix.getRowGroupIndices()[state + 1]; ++row) {
                     stateResults.push_back(matrix.multiplyRowWithVector(row, quantitativeResult) + vector[row]);
                 }
+                // Do this twice, once for upperbound once for lowerbound
                 bool checkUpperParameters = false;
                 do {
                     auto const& consideredParameters = checkUpperParameters ? optimalChoiceVal.getUpperParameters() : optimalChoiceVal.getLowerParameters();
                     for (auto const& p : consideredParameters) {
                         // Find the 'best' choice that assigns the parameter to the other bound
-                        ConstantType bestValue;
+                        ConstantType bestValue = 0;
                         bool foundBestValue = false;
                         for (uint64_t choice = 0; choice < stateResults.size(); ++choice) {
                             if (choice != optimalChoice) {
@@ -357,26 +444,36 @@ namespace storm {
                                 }
                             }
                         }
-                        if (checkUpperParameters) {
-                            deltaLower[p] += storm::utility::convertNumber<double>(bestValue);
-                        } else {
-                            deltaUpper[p] += storm::utility::convertNumber<double>(bestValue);
+                        auto optimal = storm::utility::convertNumber<double>(stateResults[optimalChoice]);
+                        auto diff = optimal - storm::utility::convertNumber<double>(bestValue);
+                        if (foundBestValue) {
+                            if (checkUpperParameters) {
+                                deltaLower[p] += std::abs(diff);
+                            } else {
+                                deltaUpper[p] += std::abs(diff);
+                            }
                         }
-
                     }
                     checkUpperParameters = !checkUpperParameters;
                 } while (checkUpperParameters);
             }
             
             regionSplitEstimates.clear();
+            useRegionSplitEstimates = false;
             for (auto const& p : region.getVariables()) {
-                if (deltaLower[p] > deltaUpper[p]) {
-                    regionSplitEstimates.insert(std::make_pair(p, deltaUpper[p]));
-                } else {
-                    regionSplitEstimates.insert(std::make_pair(p, deltaLower[p]));
+                if (this->possibleMonotoneParameters.find(p) != this->possibleMonotoneParameters.end()) {
+                    if (deltaLower[p] > deltaUpper[p] && deltaUpper[p] >= 0.0001) {
+                        regionSplitEstimates.insert(std::make_pair(p, deltaUpper[p]));
+                        useRegionSplitEstimates = true;
+                    } else if (deltaLower[p] <= deltaUpper[p] && deltaLower[p] >= 0.0001) {
+                        {
+                            regionSplitEstimates.insert(std::make_pair(p, deltaLower[p]));
+                            useRegionSplitEstimates = true;
+                        }
+                    }
                 }
             }
-            
+            // large regionsplitestimate implies that parameter p occurs as p and 1-p at least once
         }
         
         template <typename SparseModelType, typename ConstantType>
@@ -437,9 +534,145 @@ namespace storm {
             return regionSplitEstimates;
         }
 
-        
+        template<typename SparseModelType, typename ConstantType>
+        std::shared_ptr<storm::analysis::Order> SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ValueType> region) {
+            if (this->orderExtender) {
+                auto res = this->orderExtender->extendOrder(order, region);
+                order = std::get<0>(res);
+                if (std::get<1>(res) != order->getNumberOfStates()) {
+                    this->orderExtender.get().setUnknownStates(order, std::get<1>(res), std::get<2>(res));
+                }
+            } else {
+                STORM_LOG_WARN("Extending order for RegionModelChecker not implemented");
+            }
+            return order;
+        }
+
+        template <typename SparseModelType, typename ConstantType>
+        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::extendLocalMonotonicityResult(storm::storage::ParameterRegion<ValueType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) {
+            if (this->monotoneIncrParameters && !localMonotonicityResult->isFixedParametersSet()) {
+                for (auto & var : this->monotoneIncrParameters.get()) {
+                    localMonotonicityResult->setMonotoneIncreasing(var);
+                }
+                for (auto & var : this->monotoneDecrParameters.get()) {
+                    localMonotonicityResult->setMonotoneDecreasing(var);
+                }
+            }
+            auto state = order->getNextDoneState(-1);
+            auto const variablesAtState = parameterLifter->getOccurringVariablesAtState();
+            while (state != order->getNumberOfStates()) {
+                if (localMonotonicityResult->getMonotonicity(state) == nullptr) {
+                    auto variables = variablesAtState[state];
+                    if (variables.size() == 0 || order->isBottomState(state) || order->isTopState(state)) {
+                        localMonotonicityResult->setConstant(state);
+                    } else {
+                        for (auto const &var : variables) {
+                            auto monotonicity = localMonotonicityResult->getMonotonicity(state, var);
+                            if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
+                                monotonicity = monotonicityChecker->checkLocalMonotonicity(order, state, var, region);
+                                if (monotonicity == Monotonicity::Unknown || monotonicity == Monotonicity::Not) {
+                                    // TODO: Skip for now?
+                                } else {
+                                    localMonotonicityResult->setMonotonicity(state, var, monotonicity);
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    // Do nothing, we already checked this one
+                }
+                state = order->getNextDoneState(state);
+            }
+            auto const statesAtVariable = parameterLifter->getOccuringStatesAtVariable();
+            bool allDone = true;
+            for (auto const & entry : statesAtVariable) {
+                auto states = entry.second;
+                auto var = entry.first;
+                bool done = true;
+                for (auto const& state : states) {
+                    done &= order->contains(state) && localMonotonicityResult->getMonotonicity(state, var) != Monotonicity::Unknown;
+                    auto check = localMonotonicityResult->getMonotonicity(state, var);
+                    if (!done) {
+                        break;
+                    }
+                }
+
+                allDone &= done;
+                if (done) {
+                    localMonotonicityResult->getGlobalMonotonicityResult()->setDoneForVar(var);
+                }
+            }
+            if (allDone) {
+                localMonotonicityResult->setDone();
+                while (order->existsNextState()) {
+                    // Simply add the states we couldn't add sofar between =) and =( as we could find local monotonicity for all parametric states
+                    order->add(order->getNextStateNumber().second);
+                }
+                assert (order->getDoneBuilding());
+            }
+        }
+
+        template <typename SparseModelType, typename ConstantType>
+        void SparseDtmcParameterLiftingModelChecker<SparseModelType, ConstantType>::splitSmart (
+                storm::storage::ParameterRegion<ValueType> &region, std::vector<storm::storage::ParameterRegion<ValueType>> &regionVector,
+                std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const {
+            assert (regionVector.size() == 0);
+
+            std::multimap<double, VariableType> sortedOnValues;
+            std::set<VariableType> consideredVariables;
+            if (splitForExtremum) {
+                if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
+                    STORM_LOG_INFO("Splitting based on region split estimates");
+                    for (auto &entry : regionSplitEstimates) {
+                        assert (!this->isUseMonotonicitySet() || (!monRes.isMonotone(entry.first) && this->possibleMonotoneParameters.find(entry.first) != this->possibleMonotoneParameters.end()));
+//                            sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first))* storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
+                            sortedOnValues.insert({-(entry.second ), entry.first});
+                    }
+
+                    for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
+                        consideredVariables.insert(itr->second);
+                    }
+                    assert (consideredVariables.size() > 0);
+                    region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
+                } else {
+                    STORM_LOG_INFO("Splitting based on sorting");
+
+                    auto &sortedOnDifference = region.getVariablesSorted();
+                    for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
+                        if (!this->isUseMonotonicitySet() || !monRes.isMonotone(itr->second)) {
+                            consideredVariables.insert(itr->second);
+                        }
+                    }
+                    assert (consideredVariables.size() > 0 || (monRes.isDone() && monRes.isAllMonotonicity()));
+                    region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
+                }
+            } else {
+                // split for pla
+                if (regionSplitEstimationsEnabled && useRegionSplitEstimates) {
+                    STORM_LOG_INFO("Splitting based on region split estimates");
+                    ConstantType diff = this->lastValue - (this->currentCheckTask->getFormula().asOperatorFormula().template getThresholdAs<ConstantType>());
+                    for (auto &entry : regionSplitEstimates) {
+                        if ((!this->isUseMonotonicitySet() || !monRes.isMonotone(entry.first)) && entry.second > diff) {
+                            sortedOnValues.insert({-(entry.second * storm::utility::convertNumber<double>(region.getDifference(entry.first)) * storm::utility::convertNumber<double>(region.getDifference(entry.first))), entry.first});
+                        }
+                    }
+
+                    for (auto itr = sortedOnValues.begin(); itr != sortedOnValues.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
+                        consideredVariables.insert(itr->second);
+                    }
+                }
+                if (consideredVariables.size() == 0) {
+                    auto &sortedOnDifference = region.getVariablesSorted();
+                    for (auto itr = sortedOnDifference.begin(); itr != sortedOnDifference.end() && consideredVariables.size() < region.getSplitThreshold(); ++itr) {
+                        consideredVariables.insert(itr->second);
+                    }
+                }
+                assert (consideredVariables.size() > 0);
+                region.split(region.getCenterPoint(), regionVector, std::move(consideredVariables));
+            }
+        }
+
         template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
         template class SparseDtmcParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
-
     }
 }
diff --git a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h
index 03dc1747a..10c04c75c 100644
--- a/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/SparseDtmcParameterLiftingModelChecker.h
@@ -18,21 +18,31 @@ namespace storm {
         template <typename SparseModelType, typename ConstantType>
         class SparseDtmcParameterLiftingModelChecker : public SparseParameterLiftingModelChecker<SparseModelType, ConstantType> {
         public:
+            typedef typename SparseModelType::ValueType ValueType;
+
+            typedef typename RegionModelChecker<ValueType>::VariableType VariableType;
+            typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+            typedef typename storm::storage::ParameterRegion<ValueType>::CoefficientType CoefficientType;
+
             SparseDtmcParameterLiftingModelChecker();
             SparseDtmcParameterLiftingModelChecker(std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>>&& solverFactory);
             virtual ~SparseDtmcParameterLiftingModelChecker() = default;
             
-            virtual bool canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask) const override;
+            virtual bool canHandle(std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask) const override;
 
-            virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplification = true) override;
-            void specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification);
+            virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplification = true) override;
+            void specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification);
 
             boost::optional<storm::storage::Scheduler<ConstantType>> getCurrentMinScheduler();
             boost::optional<storm::storage::Scheduler<ConstantType>> getCurrentMaxScheduler();
 
             virtual bool isRegionSplitEstimateSupported() const override;
-            virtual std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> getRegionSplitEstimate() const override;
-            
+            virtual std::map<VariableType, double> getRegionSplitEstimate() const override;
+
+            virtual std::shared_ptr<storm::analysis::Order> extendOrder(Environment const& env, std::shared_ptr<storm::analysis::Order> order, storm::storage::ParameterRegion<ValueType> region) override;
+
+            virtual void extendLocalMonotonicityResult(storm::storage::ParameterRegion<ValueType> const& region, std::shared_ptr<storm::analysis::Order> order, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult) override;
+
         protected:
                 
             virtual void specifyBoundedUntilFormula(Environment const& env, CheckTask<storm::logic::BoundedUntilFormula, ConstantType> const& checkTask) override;
@@ -41,23 +51,30 @@ namespace storm {
             virtual void specifyCumulativeRewardFormula(Environment const& env, CheckTask<storm::logic::CumulativeRewardFormula, ConstantType> const& checkTask) override;
 
             virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() override;
-                
-            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
+            virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerSAT() override;
+            virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerVIO() override;
+
+            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult = nullptr) override;
             
-            void computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
+            void computeRegionSplitEstimates(std::vector<ConstantType> const& quantitativeResult, std::vector<uint_fast64_t> const& schedulerChoices, storm::storage::ParameterRegion<ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
             
             virtual void reset() override;
-                
-        private:
 
-            
+            virtual void splitSmart(storm::storage::ParameterRegion<ValueType> & region, std::vector<storm::storage::ParameterRegion<ValueType>> &regionVector,  std::shared_ptr<storm::analysis::Order> order, storm::analysis::MonotonicityResult<VariableType> & monRes, bool splitForExtremum) const override;
+
+
+        private:
             storm::storage::BitVector maybeStates;
             std::vector<ConstantType> resultsForNonMaybeStates;
             boost::optional<uint_fast64_t> stepBound;
+
+            boost::optional<ConstantType> thresholdTask;
             
             std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationChecker;
-            
-            std::unique_ptr<storm::transformer::ParameterLifter<typename SparseModelType::ValueType, ConstantType>> parameterLifter;
+            std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationCheckerSAT;
+            std::unique_ptr<storm::modelchecker::SparseDtmcInstantiationModelChecker<SparseModelType, ConstantType>> instantiationCheckerVIO;
+
+            std::unique_ptr<storm::transformer::ParameterLifter<ValueType, ConstantType>> parameterLifter;
             std::unique_ptr<storm::solver::MinMaxLinearEquationSolverFactory<ConstantType>> solverFactory;
             bool solvingRequiresUpperRewardBounds;
             
@@ -67,7 +84,13 @@ namespace storm {
             boost::optional<ConstantType> lowerResultBound, upperResultBound;
             
             bool regionSplitEstimationsEnabled;
-            std::map<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType, double> regionSplitEstimates;
+            std::map<VariableType, double> regionSplitEstimates;
+
+            // Used for monotonicity
+            bool useRegionSplitEstimates;
+            std::unique_ptr<storm::analysis::MonotonicityChecker<ValueType>> monotonicityChecker;
+
+
         };
     }
 }
diff --git a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp
index c58426f79..9ebb19192 100644
--- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.cpp
@@ -43,21 +43,21 @@ namespace storm {
             result &= checkTask.getFormula().isInFragment(storm::logic::reachability().setRewardOperatorsAllowed(true).setReachabilityRewardFormulasAllowed(true).setBoundedUntilFormulasAllowed(true).setCumulativeRewardFormulasAllowed(true).setStepBoundedUntilFormulasAllowed(true).setTimeBoundedCumulativeRewardFormulasAllowed(true).setStepBoundedCumulativeRewardFormulasAllowed(true).setTimeBoundedUntilFormulasAllowed(true));
             return result;
         }
-        
+
         template <typename SparseModelType, typename ConstantType>
         void SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
             auto mdp = parametricModel->template as<SparseModelType>();
             specify_internal(env, mdp, checkTask, generateRegionSplitEstimates, !allowModelSimplifications);
         }
-        
+
         template <typename SparseModelType, typename ConstantType>
         void SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::specify_internal(Environment const& env, std::shared_ptr<SparseModelType> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool skipModelSimplification) {
 
 
             STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
-         
+
             reset();
-            
+
             if (skipModelSimplification) {
                 this->parametricModel = parametricModel;
                 this->specifyFormula(env, checkTask);
@@ -240,7 +240,7 @@ namespace storm {
         }
 
         template <typename SparseModelType, typename ConstantType>
-        std::unique_ptr<CheckResult> SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
+        std::unique_ptr<CheckResult> SparseMdpParameterLiftingModelChecker<SparseModelType, ConstantType>::computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
             
             if (maybeStates.empty()) {
                 return std::make_unique<storm::modelchecker::ExplicitQuantitativeCheckResult<ConstantType>>(resultsForNonMaybeStates);
diff --git a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h
index d29525e2e..c4954081a 100644
--- a/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/SparseMdpParameterLiftingModelChecker.h
@@ -41,7 +41,7 @@ namespace storm {
                 
             virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() override;
 
-            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
+            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
                 
             virtual void reset() override;
                 
diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp
index 2ca96906b..a38dbd5a7 100644
--- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.cpp
@@ -1,6 +1,8 @@
 #include "storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h"
 
 #include <queue>
+#include <boost/container/flat_set.hpp>
+#include <storm-pars/analysis/MonotonicityChecker.h>
 
 #include "storm/adapters/RationalFunctionAdapter.h"
 #include "storm/logic/FragmentSpecification.h"
@@ -28,7 +30,7 @@ namespace storm {
             currentFormula = checkTask.getFormula().asSharedPointer();
             currentCheckTask = std::make_unique<storm::modelchecker::CheckTask<storm::logic::Formula, ConstantType>>(checkTask.substituteFormula(*currentFormula).template convertValueType<ConstantType>());
             
-            if(currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
+            if (currentCheckTask->getFormula().isProbabilityOperatorFormula()) {
                 auto const& probOpFormula = currentCheckTask->getFormula().asProbabilityOperatorFormula();
                 if(probOpFormula.getSubformula().isBoundedUntilFormula()) {
                     specifyBoundedUntilFormula(env, currentCheckTask->substituteFormula(probOpFormula.getSubformula().asBoundedUntilFormula()));
@@ -50,8 +52,11 @@ namespace storm {
         }
         
         template <typename SparseModelType, typename ConstantType>
-        RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion) {
-
+        RegionResult SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
+            typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
+            typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+            typedef typename storm::utility::parametric::Valuation<typename SparseModelType::ValueType> Valuation;
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
             STORM_LOG_THROW(this->currentCheckTask->isOnlyInitialStatesRelevantSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a property where only the value in the initial states is relevant.");
             STORM_LOG_THROW(this->currentCheckTask->isBoundSet(), storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a bounded property.");
             STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Analyzing regions with parameter lifting requires a model with a single initial state.");
@@ -60,22 +65,79 @@ namespace storm {
 
             // Check if we need to check the formula on one point to decide whether to show AllSat or AllViolated
             if (hypothesis == RegionResultHypothesis::Unknown && result == RegionResult::Unknown) {
-                 result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
+                result = getInstantiationChecker().check(env, region.getCenterPoint())->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()] ? RegionResult::CenterSat : RegionResult::CenterViolated;
             }
-            
-            // try to prove AllSat or AllViolated, depending on the hypothesis or the current result
-            if (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat) {
+
+            bool existsSat = (hypothesis == RegionResultHypothesis::AllSat || result == RegionResult::ExistsSat || result == RegionResult::CenterSat);
+            bool existsViolated = (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated);
+
+            // Here we check on global monotonicity
+            if (localMonotonicityResult != nullptr && localMonotonicityResult->isDone()) {
+                // Try to check it with a global monotonicity result
+                auto monRes = localMonotonicityResult->getGlobalMonotonicityResult();
+                bool lowerBound = isLowerBound(this->currentCheckTask->getBound().comparisonType);
+
+                if (monRes->isDone() && monRes->isAllMonotonicity()) {
+                    // Build valuations
+                    auto monMap = monRes->getMonotonicityResult();
+                    Valuation valuationToCheckSat;
+                    Valuation valuationToCheckViolated;
+                    for (auto var : region.getVariables()) {
+                        auto monVar = monMap[var];
+                        if (monVar == Monotonicity::Constant) {
+                            valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                            valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                        } else if (monVar == Monotonicity::Decr) {
+                            if (lowerBound) {
+                                valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
+                                valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                            } else {
+                                valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                                valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
+                            }
+                        } else if (monVar == Monotonicity::Incr) {
+                            if (lowerBound) {
+                                valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                                valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
+                            } else {
+                                valuationToCheckSat.insert(std::pair<VariableType, CoefficientType>(var, region.getUpperBoundary(var)));
+                                valuationToCheckViolated.insert(std::pair<VariableType, CoefficientType>(var, region.getLowerBoundary(var)));
+                            }
+                        }
+                    }
+
+                    // Check for result
+                    if (existsSat && getInstantiationCheckerSAT().check(env, valuationToCheckSat)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
+                        STORM_LOG_INFO("Region " << region << " is AllSat, discovered with instantiation checker on " << valuationToCheckSat << " and help of monotonicity" << std::endl);
+                        RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
+                        return RegionResult::AllSat;
+                    }
+
+                    if (existsViolated && !getInstantiationCheckerVIO().check(env, valuationToCheckViolated)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
+                        STORM_LOG_INFO("Region " << region << " is AllViolated, discovered with instantiation checker on " << valuationToCheckViolated << " and help of monotonicity" << std::endl);
+                        RegionModelChecker<typename SparseModelType::ValueType>::numberOfRegionsKnownThroughMonotonicity++;
+                        return RegionResult::AllViolated;
+                    }
+
+                    return RegionResult::ExistsBoth;
+                }
+            }
+
+            // Try to prove AllSat or AllViolated, depending on the hypothesis or the current result
+            if (existsSat) {
                 // show AllSat:
                 storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
-                if (this->check(env, region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
+                auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
+                if (checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
                     result = RegionResult::AllSat;
                 } else if (sampleVerticesOfRegion) {
                     result = sampleVertices(env, region, result);
                 }
-            } else if (hypothesis == RegionResultHypothesis::AllViolated || result == RegionResult::ExistsViolated || result == RegionResult::CenterViolated) {
+            } else if (existsViolated) {
                 // show AllViolated:
                 storm::solver::OptimizationDirection parameterOptimizationDirection = isLowerBound(this->currentCheckTask->getBound().comparisonType) ? storm::solver::OptimizationDirection::Maximize : storm::solver::OptimizationDirection::Minimize;
-                if (!this->check(env, region, parameterOptimizationDirection)->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
+                auto checkResult = this->check(env, region, parameterOptimizationDirection, reachabilityOrder, localMonotonicityResult);
+                if (!checkResult->asExplicitQualitativeCheckResult()[*this->parametricModel->getInitialStates().begin()]) {
                     result = RegionResult::AllViolated;
                 } else if (sampleVerticesOfRegion) {
                     result = sampleVertices(env, region, result);
@@ -122,10 +184,10 @@ namespace storm {
             return result;
         }
 
-
         template <typename SparseModelType, typename ConstantType>
-        std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
-            auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters);
+        std::unique_ptr<CheckResult> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
+            auto quantitativeResult = computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult);
+            lastValue = quantitativeResult->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
             if(currentCheckTask->getFormula().hasQuantitativeResult()) {
                 return quantitativeResult;
             } else {
@@ -134,9 +196,9 @@ namespace storm {
         }
         
         template <typename SparseModelType, typename ConstantType>
-        std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
+        std::unique_ptr<QuantitativeCheckResult<ConstantType>> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
             STORM_LOG_WARN_COND(this->currentCheckTask->getFormula().hasQuantitativeResult(), "Computing quantitative bounds for a qualitative formula...");
-            return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()));
+            return std::make_unique<ExplicitQuantitativeCheckResult<ConstantType>>(std::move(computeQuantitativeValues(env, region, dirForParameters, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>()));
         }
 
         template <typename SparseModelType, typename ConstantType>
@@ -144,80 +206,268 @@ namespace storm {
             STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting a bound at the initial state requires a model with a single initial state.");
             return storm::utility::convertNumber<typename SparseModelType::ValueType>(getBound(env, region, dirForParameters)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()]);
         }
-        
+
+        template <typename SparseModelType, typename ConstantType>
+        storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerSAT() {
+            return getInstantiationChecker();
+        }
+
+        template <typename SparseModelType, typename ConstantType>
+        storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getInstantiationCheckerVIO() {
+            return getInstantiationChecker();
+        }
+
         template <typename SparseModelType, typename ConstantType>
         struct RegionBound {
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::VariableType VariableType;
+
             RegionBound(RegionBound<SparseModelType, ConstantType> const& other) = default;
-            RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, ConstantType const& b) : region(r), bound(b) {}
+            RegionBound(storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& r, std::shared_ptr<storm::analysis::Order> o, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> l, ConstantType const& b) : region(r), order(o), localMonRes(l), bound(b) {}
             storm::storage::ParameterRegion<typename SparseModelType::ValueType> region;
+            std::shared_ptr<storm::analysis::Order> order;
+            std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes;
             ConstantType bound;
         };
-        
-        template <typename SparseModelType, typename ConstantType>
-        std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
+
+        template<typename SparseModelType, typename ConstantType>
+        std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue) {
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
             STORM_LOG_THROW(this->parametricModel->getInitialStates().getNumberOfSetBits() == 1, storm::exceptions::NotSupportedException, "Getting extremal values at the initial state requires a model with a single initial state.");
-            boost::optional<ConstantType> value;
-            typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation valuation;
-
-            for (auto const& v : region.getVerticesOfRegion(region.getVariables())) {
-                auto currValue = getInstantiationChecker().check(env, v)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
-                if (!value.is_initialized() || (storm::solver::minimize(dir) ? currValue < value.get() : currValue > value.get())) {
-                    value = currValue;
-                    valuation = v;
-                    STORM_LOG_INFO("Current value for extremum: " << value.get() << ".");
-                }
-            }
-            
+            bool const useMonotonicity = this->isUseMonotonicitySet();
+            bool const minimize = storm::solver::minimize(dir);
+
+            // Comparator for the region queue
             auto cmp = storm::solver::minimize(dir) ?
-                    [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
-                    [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
+                       [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound > rhs.bound; } :
+                       [](RegionBound<SparseModelType, ConstantType> const& lhs, RegionBound<SparseModelType, ConstantType> const& rhs) { return lhs.bound < rhs.bound; };
             std::priority_queue<RegionBound<SparseModelType, ConstantType>, std::vector<RegionBound<SparseModelType, ConstantType>>, decltype(cmp)> regionQueue(cmp);
-            regionQueue.emplace(region, storm::utility::zero<ConstantType>());
-            auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
-            auto coveredArea = storm::utility::zero<ConstantType>();
-            while (!regionQueue.empty()) {
-                auto const& currRegion = regionQueue.top().region;
-                
-                // Check whether this region contains a new 'good' value
-                auto currValue = getInstantiationChecker().check(env, currRegion.getCenterPoint())->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
-                if (storm::solver::minimize(dir) ? currValue < value.get() : currValue > value.get()) {
-                    value = currValue;
-                    valuation = currRegion.getCenterPoint();
-                }
+            storm::utility::Stopwatch initialWatch(true);
 
-                // Check whether this region needs further investigation (i.e. splitting)
-                auto currBound = getBound(env, currRegion, dir)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
-                std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
-                if (storm::solver::minimize(dir)) {
-                    if (currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision)) {
-                        currRegion.split(currRegion.getCenterPoint(), newRegions);
+            storm::utility::Stopwatch boundsWatch(false);
+            auto numberOfPLACallsBounds = 0;
+            ConstantType initBound;
+            if (minimize) {
+                initBound = storm::utility::zero<ConstantType>();
+            } else {
+                initBound = storm::utility::one<ConstantType>();
+            }
+            if (useMonotonicity) {
+                if (this->isUseBoundsSet()) {
+                    numberOfPLACallsBounds++;
+                    numberOfPLACallsBounds++;
+                    auto minBound = getBound(env, region, storm::solver::OptimizationDirection::Minimize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
+                    auto maxBound = getBound(env, region, storm::solver::OptimizationDirection::Maximize, nullptr)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
+                    if (minimize) {
+                        initBound = minBound[*this->parametricModel->getInitialStates().begin()];
+                    } else {
+                        initBound = maxBound[*this->parametricModel->getInitialStates().begin()];
                     }
+                    orderExtender->setMinValuesInit(minBound);
+                    orderExtender->setMaxValuesInit(maxBound);
+                }
+                auto order = this->extendOrder(env, nullptr, region);
+                auto monRes = std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>(new storm::analysis::LocalMonotonicityResult<VariableType>(order->getNumberOfStates()));
+                storm::utility::Stopwatch monotonicityWatch(true);
+                this->extendLocalMonotonicityResult(region, order, monRes);
+                monotonicityWatch.stop();
+                STORM_LOG_INFO(std::endl << "Total time for monotonicity checking: " << monotonicityWatch << "." << std::endl << std::endl);
+
+                if (minimize) {
+                    regionQueue.emplace(region, order, monRes, initBound);
                 } else {
-                    if (currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision)) {
-                        currRegion.split(currRegion.getCenterPoint(), newRegions);
-                    }
+                    regionQueue.emplace(region, order, monRes, initBound);
                 }
-                
-                if (newRegions.empty()) {
-                    coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
+            } else {
+                if (minimize) {
+                    regionQueue.emplace(region, nullptr, nullptr, initBound);
+                } else {
+                    regionQueue.emplace(region, nullptr, nullptr, initBound);
                 }
-                regionQueue.pop();
-                for (auto const& r : newRegions) {
-                    regionQueue.emplace(r, currBound);
+            }
+
+            // The results
+            boost::optional<ConstantType> value;
+            Valuation valuation;
+            if (!initialValue) {
+                auto init = getGoodInitialPoint(env, region, dir, regionQueue.top().localMonRes);
+                value = storm::utility::convertNumber<ConstantType>(init.first);
+                valuation = std::move(init.second);
+            } else {
+                value = initialValue;
+            }
+
+            initialWatch.stop();
+            STORM_LOG_INFO(std::endl << "Total time for initial points: " << initialWatch << "." << std::endl << std::endl);
+            if (!initialValue) {
+                STORM_LOG_INFO("Initial value: " << value.get() << " at " << valuation);
+            } else {
+                STORM_LOG_INFO("Initial value: " << value.get() << " as provided by the user");
+            }
+
+            auto numberOfSplits = 0;
+            auto numberOfPLACalls = 0;
+            auto numberOfOrderCopies = 0;
+            auto numberOfMonResCopies = 0;
+            bool first = true;
+            storm::utility::Stopwatch loopWatch(true);
+            if (!(useMonotonicity && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isDone() && regionQueue.top().localMonRes->getGlobalMonotonicityResult()->isAllMonotonicity())) {
+                // Doing the extremal computation, only when we don't use monotonicity or there are possibly not monotone variables.
+                auto totalArea = storm::utility::convertNumber<ConstantType>(region.area());
+                auto coveredArea = storm::utility::zero<ConstantType>();
+                while (!regionQueue.empty()) {
+                    assert (value);
+                    auto currRegion = regionQueue.top().region;
+                    auto order = regionQueue.top().order;
+                    auto localMonotonicityResult = regionQueue.top().localMonRes;
+                    auto currBound = regionQueue.top().bound;
+                    STORM_LOG_INFO("Currently looking at region: " << currRegion);
+                    std::vector<storm::storage::ParameterRegion<typename SparseModelType::ValueType>> newRegions;
+
+                    // Check whether this region needs further investigation based on the bound of the parent region
+                    bool investigateBounds = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
+                                             || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
+                    if (investigateBounds) {
+                        numberOfPLACalls++;
+                        auto bounds = getBound(env, currRegion, dir, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
+                        currBound = bounds[*this->parametricModel->getInitialStates().begin()];
+                        // Check whether this region needs further investigation based on the bound of this region
+                        bool lookAtRegion = (minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
+                                            || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision));
+                        if (lookAtRegion) {
+                            if (useMonotonicity) {
+                                // Continue extending order/monotonicity result
+                                bool changedOrder = false;
+                                if (!order->getDoneBuilding() && orderExtender->isHope(order, currRegion)) {
+                                    if (numberOfCopiesOrder[order] != 1) {
+                                        numberOfCopiesOrder[order]--;
+                                        order = copyOrder(order);
+                                        numberOfOrderCopies++;
+                                    } else {
+                                        assert (numberOfCopiesOrder[order] == 1);
+                                    }
+                                    this->extendOrder(env, order, currRegion);
+                                    changedOrder = true;
+                                }
+                                if (changedOrder) {
+                                    assert(!localMonotonicityResult->isDone());
+
+                                    if (numberOfCopiesMonRes[localMonotonicityResult] != 1) {
+                                        numberOfCopiesMonRes[localMonotonicityResult]--;
+                                        localMonotonicityResult = localMonotonicityResult->copy();
+                                        numberOfMonResCopies++;
+                                    } else {
+                                        assert (numberOfCopiesMonRes[localMonotonicityResult] == 1);
+                                    }
+                                    this->extendLocalMonotonicityResult(currRegion, order, localMonotonicityResult);
+                                    STORM_LOG_INFO("Order and monotonicity result got extended");
+                                }
+                            }
+
+                            // Check whether this region contains a new 'good' value and set this value
+                            auto point = useMonotonicity ? currRegion.getPoint(dir, *(localMonotonicityResult->getGlobalMonotonicityResult())) : currRegion.getCenterPoint();
+                            auto currValue = getInstantiationChecker().check(env, point)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
+                            if (!value || (minimize ? currValue <= value.get() : currValue >= value.get())) {
+                                value = currValue;
+                                valuation = point;
+                            }
+
+                            if ((minimize && currBound < value.get() - storm::utility::convertNumber<ConstantType>(precision))
+                                || (!minimize && currBound > value.get() + storm::utility::convertNumber<ConstantType>(precision))) {
+
+                                // We will split the region in this case, but first we set the bounds to extend the order for the new regions.
+                                if (useMonotonicity && this->isUseBoundsSet() && !order->getDoneBuilding()) {
+                                    boundsWatch.start();
+                                    numberOfPLACallsBounds++;
+                                    if (minimize) {
+                                        orderExtender->setMinMaxValues(order, bounds, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector());
+                                    } else {
+                                        orderExtender->setMinMaxValues(order, getBound(env, currRegion, storm::solver::OptimizationDirection::Maximize, localMonotonicityResult)->template asExplicitQuantitativeCheckResult<ConstantType>().getValueVector(), bounds);
+                                    }
+                                    boundsWatch.stop();
+                                }
+                                // Now split the region
+                                if (useMonotonicity) {
+                                    this->splitSmart(currRegion, newRegions, order, *(localMonotonicityResult->getGlobalMonotonicityResult()), true);
+                                } else if (this->isRegionSplitEstimateSupported()) {
+                                    auto empty = storm::analysis::MonotonicityResult<VariableType>();
+                                    this->splitSmart(currRegion, newRegions, order, empty, true);
+                                } else {
+                                    currRegion.split(currRegion.getCenterPoint(), newRegions);
+                                }
+                            }
+                        }
+                    }
+
+                    if (newRegions.empty()) {
+                        // When the newRegions is empty we are done with the current region
+                        coveredArea += storm::utility::convertNumber<ConstantType>(currRegion.area());
+                        if (order != nullptr) {
+                            numberOfCopiesOrder[order]--;
+                            numberOfCopiesMonRes[localMonotonicityResult]--;
+                        }
+                        regionQueue.pop();
+                    } else {
+                        regionQueue.pop();
+                        STORM_LOG_INFO("Splitting region " << currRegion << " into " << newRegions.size());
+                        numberOfSplits++;
+                        // Add the new regions to the queue
+                        if (useMonotonicity) {
+                            for (auto &r : newRegions) {
+                                r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
+                                regionQueue.emplace(r, order, localMonotonicityResult, currBound);
+                            }
+                            if (numberOfCopiesOrder.find(order) != numberOfCopiesOrder.end()) {
+                                numberOfCopiesOrder[order] += newRegions.size();
+                                numberOfCopiesMonRes[localMonotonicityResult] += newRegions.size();
+                            } else {
+                                numberOfCopiesOrder[order] = newRegions.size();
+                                numberOfCopiesMonRes[localMonotonicityResult] = newRegions.size();
+                            }
+                        } else {
+                            for (auto &r : newRegions) {
+                                r.setBoundParent(storm::utility::convertNumber<CoefficientType>(currBound));
+                                regionQueue.emplace(r, nullptr, nullptr, currBound);
+                            }
+                        }
+                    }
+
+                    STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << currBound << ".");
+                    STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region." << std::endl);
                 }
-                STORM_LOG_INFO("Current value : " << value.get() << ", current bound: " << regionQueue.top().bound << ".");
-                STORM_LOG_INFO("Covered " << (coveredArea * storm::utility::convertNumber<ConstantType>(100.0) / totalArea) << "% of the region.");
+                loopWatch.stop();
             }
-            
+
+            STORM_LOG_INFO("Total number of splits: " << numberOfSplits << std::endl);
+            STORM_PRINT("Total number of plaCalls: " << numberOfPLACalls << std::endl);
+            if (useMonotonicity) {
+                STORM_PRINT("Total number of plaCalls for bounds for monotonicity checking: " << numberOfPLACallsBounds << std::endl);
+                STORM_PRINT("Total number of copies of the order: " << numberOfOrderCopies << std::endl);
+                STORM_PRINT("Total number of copies of the local monotonicity result: " << numberOfMonResCopies
+                                                                                        << std::endl);
+            }
+            STORM_LOG_INFO(std::endl << "Total time for region refinement: " << loopWatch << "." << std::endl << std::endl);
+            STORM_LOG_INFO(std::endl << "Total time for additional bounds: " << boundsWatch << "." << std::endl << std::endl);
+
             return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value.get()), valuation);
         }
-
         
+        template <typename SparseModelType, typename ConstantType>
+        std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision) {
+            return computeExtremalValue(env, region, dir, precision, boost::none);
+        }
+
+        template <typename SparseModelType, typename ConstantType>
+        bool SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) {
+            auto res = computeExtremalValue(env, region, dir, precision, storm::utility::convertNumber<ConstantType>(valueToCheck)).first;
+            return storm::solver::minimize(dir) ? storm::utility::convertNumber<ConstantType>(res) >= storm::utility::convertNumber<ConstantType>(valueToCheck) : storm::utility::convertNumber<ConstantType>(res) <= storm::utility::convertNumber<ConstantType>(valueToCheck);
+        }
+
         template <typename SparseModelType, typename ConstantType>
         SparseModelType const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getConsideredParametricModel() const {
             return *parametricModel;
         }
-        
+
         template <typename SparseModelType, typename ConstantType>
         CheckTask<storm::logic::Formula, ConstantType> const& SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getCurrentCheckTask() const {
             return *currentCheckTask;
@@ -250,10 +500,108 @@ namespace storm {
             STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameter lifting is not supported for the given property.");
         }
 
+        template<typename SparseModelType, typename ConstantType>
+        std::shared_ptr<storm::analysis::Order> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::copyOrder(std::shared_ptr<storm::analysis::Order> order) {
+            auto res = order->copy();
+            if (orderExtender) {
+                orderExtender->setUnknownStates(order, res);
+                orderExtender->copyMinMax(order, res);
+            }
+            return res;
+        }
+
+        template<typename SparseModelType, typename ConstantType>
+        std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::checkForPossibleMonotonicity(Environment const& env,
+                                                                                                             const storage::ParameterRegion<typename SparseModelType::ValueType> &region,
+                                                                                                             std::set<VariableType>& possibleMonotoneIncrParameters,
+                                                                                                             std::set<VariableType>& possibleMonotoneDecrParameters,
+                                                                                                             std::set<VariableType>& possibleNotMonotoneParameters,
+                                                                                                             std::set<VariableType>const& consideredVariables,
+                                                                                                             storm::solver::OptimizationDirection const& dir) {
+            bool minimize = storm::solver::minimize(dir);
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
+            ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
+            Valuation valuation;
+            for (auto& var : consideredVariables) {
+                ConstantType previousCenter = -1;
+                bool monDecr = true;
+                bool monIncr = true;
+                auto valuationCenter = region.getCenterPoint();
+
+                valuationCenter[var] = region.getLowerBoundary(var);
+                // TODO: make cmdline argument or 1/precision
+                int numberOfSamples = 50;
+                auto stepSize = (region.getUpperBoundary(var) - region.getLowerBoundary(var)) / (numberOfSamples - 1);
+
+                while (valuationCenter[var] <= region.getUpperBoundary(var)) {
+                    // Create valuation
+                    ConstantType valueCenter = getInstantiationChecker().check(env, valuationCenter)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
+                    if (storm::solver::minimize(dir) ? valueCenter <= value : valueCenter >= value) {
+                        value = valueCenter;
+                        valuation = valuationCenter;
+                    }
+                    // Calculate difference with result for previous valuation
+                    ConstantType diffCenter = previousCenter - valueCenter;
+                    assert (previousCenter == -1 || (diffCenter >= -1 && diffCenter <= 1));
+                    if (previousCenter != -1) {
+                        assert (previousCenter != -1 && previousCenter != -1);
+                        monDecr &= diffCenter > 0 && diffCenter > 0 && diffCenter > 0; // then previous value is larger than the current value from the initial states
+                        monIncr &= diffCenter < 0 && diffCenter < 0 && diffCenter < 0;
+                    }
+                    previousCenter = valueCenter;
+                    if (!monDecr && ! monIncr) {
+                        break;
+                    }
+                    valuationCenter[var] += stepSize;
+                }
+                if (monIncr) {
+                    possibleMonotoneParameters.insert(var);
+                    possibleMonotoneIncrParameters.insert(var);
+                } else if (monDecr) {
+                    possibleMonotoneParameters.insert(var);
+                    possibleMonotoneDecrParameters.insert(var);
+                } else {
+                    possibleNotMonotoneParameters.insert(var);
+                }
+            }
+            return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
+
+        }
+
+        template<typename SparseModelType, typename ConstantType>
+        std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> SparseParameterLiftingModelChecker<SparseModelType, ConstantType>::getGoodInitialPoint(const Environment &env, const storage::ParameterRegion<typename SparseModelType::ValueType> &region, const OptimizationDirection &dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes) {
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation Valuation;
+            typedef typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::CoefficientType CoefficientType;
+            ConstantType value = storm::solver::minimize(dir) ? 1 : 0;
+            Valuation valuation;
+            std::set<VariableType> monIncr, monDecr, notMon, notMonFirst;
+            STORM_LOG_INFO("Number of parameters: " << region.getVariables().size() << std::endl;);
+
+            if (localMonRes != nullptr) {
+                localMonRes->getGlobalMonotonicityResult()->splitBasedOnMonotonicity(region.getVariables(), monIncr, monDecr, notMonFirst);
+
+                auto numMon = monIncr.size() + monDecr.size();
+                STORM_LOG_INFO("Number of monotone parameters: " << numMon << std::endl;);
+
+                if (numMon < region.getVariables().size()) {
+                    checkForPossibleMonotonicity(env, region, monIncr, monDecr, notMon, notMonFirst, dir);
+                    STORM_LOG_INFO("Number of possible monotone parameters: " << (monIncr.size() + monDecr.size() - numMon) << std::endl;);
+                    STORM_LOG_INFO("Number of definitely not monotone parameters: " << notMon.size() << std::endl;);
+                }
+
+                valuation = region.getPoint(dir, monIncr, monDecr);
+            } else {
+                valuation = region.getCenterPoint();
+            }
+            value = getInstantiationChecker().check(env, valuation)->template asExplicitQuantitativeCheckResult<ConstantType>()[*this->parametricModel->getInitialStates().begin()];
+
+            return std::make_pair(storm::utility::convertNumber<typename SparseModelType::ValueType>(value), std::move(valuation));
+        }
+
         template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, double>;
         template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, double>;
         template class SparseParameterLiftingModelChecker<storm::models::sparse::Dtmc<storm::RationalFunction>, storm::RationalNumber>;
         template class SparseParameterLiftingModelChecker<storm::models::sparse::Mdp<storm::RationalFunction>, storm::RationalNumber>;
-
     }
 }
diff --git a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h
index 62e2a634d..bba0ac8fd 100644
--- a/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/SparseParameterLiftingModelChecker.h
@@ -22,14 +22,17 @@ namespace storm {
         template <typename SparseModelType, typename ConstantType>
         class SparseParameterLiftingModelChecker : public RegionModelChecker<typename SparseModelType::ValueType> {
         public:
+            typedef typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType VariableType;
+            typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+
             SparseParameterLiftingModelChecker();
             virtual ~SparseParameterLiftingModelChecker() = default;
-            
+
 
             /*!
              * Analyzes the given region by means of parameter lifting.
              */
-            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) override;
+            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
 
             /*!
              * Analyzes the 2^#parameters corner points of the given region.
@@ -43,9 +46,9 @@ namespace storm {
              * @param region the region on which parameter lifting is applied
              * @param dirForParameters  The optimization direction for the parameter choices. If this is, e.g., minimize, then the returned result will be a lower bound for all results induced by the parameter evaluations inside the region.
              */
-            std::unique_ptr<CheckResult> check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
+            std::unique_ptr<CheckResult> check(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr);
             
-            std::unique_ptr<QuantitativeCheckResult<ConstantType>> getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
+            std::unique_ptr<QuantitativeCheckResult<ConstantType>> getBound(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr);
             virtual typename SparseModelType::ValueType getBoundAtInitState(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) override;
 
             
@@ -55,7 +58,8 @@ namespace storm {
              * The actual maximum (minimum) lies in the interval [v, v+precision] ([v-precision, v])
              */
             virtual std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision) override;
-            
+            virtual bool checkExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision, typename SparseModelType::ValueType const& valueToCheck) override;
+
             SparseModelType const& getConsideredParametricModel() const;
             CheckTask<storm::logic::Formula, ConstantType> const& getCurrentCheckTask() const;
             
@@ -73,17 +77,28 @@ namespace storm {
 
             
             virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationChecker() = 0;
-            
-            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters) = 0;
-            
-            
+            virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerSAT();
+            virtual storm::modelchecker::SparseInstantiationModelChecker<SparseModelType, ConstantType>& getInstantiationCheckerVIO();
+
+            virtual std::unique_ptr<CheckResult> computeQuantitativeValues(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) = 0;
+
+
             std::shared_ptr<SparseModelType> parametricModel;
             std::unique_ptr<CheckTask<storm::logic::Formula, ConstantType>> currentCheckTask;
+            ConstantType lastValue;
+            boost::optional<storm::analysis::OrderExtender<typename SparseModelType::ValueType, ConstantType>> orderExtender;
+
+            std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> checkForPossibleMonotonicity(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, std::set<VariableType>& possibleMonotoneIncrParameters, std::set<VariableType>& possibleMonotoneDecrParameters, std::set<VariableType>& possibleNotMonotoneParameters, std::set<VariableType>const& consideredVariables, storm::solver::OptimizationDirection const& dir);
+            std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> getGoodInitialPoint(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dir, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonRes);
+            std::set<VariableType> possibleMonotoneParameters;
 
         private:
             // store the current formula. Note that currentCheckTask only stores a reference to the formula.
             std::shared_ptr<storm::logic::Formula const> currentFormula;
-            
+            std::shared_ptr<storm::analysis::Order> copyOrder(std::shared_ptr<storm::analysis::Order> order);
+            std::map<std::shared_ptr<storm::analysis::Order>, uint_fast64_t> numberOfCopiesOrder;
+            std::map<std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>>, uint_fast64_t> numberOfCopiesMonRes;
+            std::pair<typename SparseModelType::ValueType, typename storm::storage::ParameterRegion<typename SparseModelType::ValueType>::Valuation> computeExtremalValue(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, storm::solver::OptimizationDirection const& dirForParameters, typename SparseModelType::ValueType const& precision, boost::optional<ConstantType> const& initialValue);
         };
     }
 }
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp
index 2e9bf221b..f79f3059d 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.cpp
@@ -13,20 +13,20 @@ namespace storm {
         ValidatingSparseDtmcParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::ValidatingSparseDtmcParameterLiftingModelChecker() {
             // Intentionally left empty
         }
-        
+
         template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
         void ValidatingSparseDtmcParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
             STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
-        
+
             auto dtmc = parametricModel->template as<SparseModelType>();
             auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<SparseModelType>(*dtmc);
 
             if (!simplifier.simplify(checkTask.getFormula())) {
                 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
             }
-            
+
             auto simplifiedTask = checkTask.substituteFormula(*simplifier.getSimplifiedFormula());
-            
+
             impreciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
             preciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
         }
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h
index e99792eed..a7182b08c 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseDtmcParameterLiftingModelChecker.h
@@ -11,7 +11,7 @@ namespace storm {
         public:
             ValidatingSparseDtmcParameterLiftingModelChecker();
             virtual ~ValidatingSparseDtmcParameterLiftingModelChecker() = default;
-            
+
             virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplifications = true) override;
 
         protected:
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp
index 444dd334c..47414ef57 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.cpp
@@ -13,21 +13,21 @@ namespace storm {
         ValidatingSparseMdpParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::ValidatingSparseMdpParameterLiftingModelChecker() {
             // Intentionally left empty
         }
-        
-        
+
+
         template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
         void ValidatingSparseMdpParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates, bool allowModelSimplifications) {
             STORM_LOG_ASSERT(this->canHandle(parametricModel, checkTask), "specified model and formula can not be handled by this.");
-        
+
             auto mdp = parametricModel->template as<SparseModelType>();
             auto simplifier = storm::transformer::SparseParametricMdpSimplifier<SparseModelType>(*mdp);
 
             if (!simplifier.simplify(checkTask.getFormula())) {
                 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Simplifying the model was not successfull.");
             }
-            
+
             auto simplifiedTask = checkTask.substituteFormula(*simplifier.getSimplifiedFormula());
-            
+
             impreciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
             preciseChecker.specify(env, simplifier.getSimplifiedModel(), simplifiedTask, false, true);
         }
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h
index 9147e574e..e41bf5b8d 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseMdpParameterLiftingModelChecker.h
@@ -11,7 +11,7 @@ namespace storm {
         public:
             ValidatingSparseMdpParameterLiftingModelChecker();
             virtual ~ValidatingSparseMdpParameterLiftingModelChecker() = default;
-            
+
             virtual void specify(Environment const& env, std::shared_ptr<storm::models::ModelBase> parametricModel, CheckTask<storm::logic::Formula, typename SparseModelType::ValueType> const& checkTask, bool generateRegionSplitEstimates = false, bool allowModelSimplifications = true) override;
 
         protected:
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp
index 4065f236b..4532e40fe 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.cpp
@@ -28,9 +28,9 @@ namespace storm {
         }
  
         template <typename SparseModelType, typename ImpreciseType, typename PreciseType>
-        RegionResult ValidatingSparseParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion) {
+        RegionResult ValidatingSparseParameterLiftingModelChecker<SparseModelType, ImpreciseType, PreciseType>::analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis, RegionResult const& initialResult, bool sampleVerticesOfRegion, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult) {
+
 
-        
             RegionResult currentResult = getImpreciseChecker().analyzeRegion(env, region, hypothesis, initialResult, false);
             
             if (currentResult == RegionResult::AllSat || currentResult == RegionResult::AllViolated) {
diff --git a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h
index f0dbf3534..09dcf4c64 100644
--- a/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h
+++ b/src/storm-pars/modelchecker/region/ValidatingSparseParameterLiftingModelChecker.h
@@ -23,7 +23,7 @@ namespace storm {
              * We first apply unsound solution methods (standard value iteratio with doubles) and then validate the obtained result
              * by means of exact and soud methods.
              */
-            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false) override;
+            virtual RegionResult analyzeRegion(Environment const& env, storm::storage::ParameterRegion<typename SparseModelType::ValueType> const& region, RegionResultHypothesis const& hypothesis = RegionResultHypothesis::Unknown, RegionResult const& initialResult = RegionResult::Unknown, bool sampleVerticesOfRegion = false, std::shared_ptr<storm::analysis::Order> reachabilityOrder = nullptr, std::shared_ptr<storm::analysis::LocalMonotonicityResult<typename RegionModelChecker<typename SparseModelType::ValueType>::VariableType>> localMonotonicityResult = nullptr) override;
 
         protected:
             
diff --git a/src/storm-pars/parser/MonotonicityParser.cpp b/src/storm-pars/parser/MonotonicityParser.cpp
new file mode 100644
index 000000000..b3d2e19b7
--- /dev/null
+++ b/src/storm-pars/parser/MonotonicityParser.cpp
@@ -0,0 +1,73 @@
+#include "storm-pars/parser/MonotonicityParser.h"
+#include <storm/exceptions/WrongFormatException.h>
+
+#include "storm/utility/macros.h"
+#include "storm/exceptions/InvalidArgumentException.h"
+#include "storm/utility/constants.h"
+#include "storm/io/file.h"
+
+namespace storm {
+    namespace parser {
+
+        template<typename VariableType>
+        std::pair<std::set<VariableType>, std::set<VariableType>>  MonotonicityParser<VariableType>::parseMonotoneVariablesFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables) {
+
+            // Open file and initialize result.
+            std::ifstream inputFileStream;
+            storm::utility::openFile(fileName, inputFileStream);
+
+            std::set<VariableType> monotoneIncrVars;
+            std::set<VariableType> monotoneDecrVars;
+
+            // Now try to parse the contents of the file.
+            try {
+                std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
+                std::vector<std::string> fileSplitted;
+
+                boost::split(fileSplitted, fileContent, boost::is_any_of(";"));
+                STORM_LOG_THROW(fileSplitted.size() == 2, storm::exceptions::WrongFormatException, "Expecting content to contain \";\" between monotone variables");
+                std::vector<std::string> monotoneIncrVarsString;
+                boost::split(monotoneIncrVarsString, fileSplitted[0], boost::is_any_of(" "));
+                std::vector<std::string> monotoneDecrVarsString;
+                boost::split(monotoneDecrVarsString, fileSplitted[0], boost::is_any_of(" "));
+                // TODO: throw errors if file not formatted correctly
+                for (auto varString : monotoneIncrVarsString) {
+                    VariableType var;
+                    for (auto const& v : consideredVariables) {
+                        std::stringstream stream;
+                        stream << v;
+                        if (varString == stream.str()) {
+                            var = v;
+                            break;
+                        }
+                    }
+                    monotoneIncrVars.insert(var);
+                }
+                for (auto varString : monotoneDecrVarsString) {
+                    VariableType var;
+                    for (auto const& v : consideredVariables) {
+                        std::stringstream stream;
+                        stream << v;
+                        if (varString == stream.str()) {
+                            var = v;
+                            break;
+                        }
+                    }
+                    monotoneDecrVars.insert(var);
+                }
+
+            } catch(std::exception& e) {
+                // In case of an exception properly close the file before passing exception.
+                storm::utility::closeFile(inputFileStream);
+                throw e;
+            }
+
+            // Close the stream in case everything went smoothly and return result.
+            storm::utility::closeFile(inputFileStream);
+            return {std::move(monotoneIncrVars), std::move(monotoneDecrVars)};
+        }
+
+
+        template class MonotonicityParser<storm::RationalFunctionVariable>;
+    }
+}
\ No newline at end of file
diff --git a/src/storm-pars/parser/MonotonicityParser.h b/src/storm-pars/parser/MonotonicityParser.h
new file mode 100644
index 000000000..1b3309ba1
--- /dev/null
+++ b/src/storm-pars/parser/MonotonicityParser.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <set>
+#include <string>
+
+namespace storm {
+    namespace parser {
+        template<typename VariableType>
+        class MonotonicityParser{
+        public:
+            static std::pair<std::set<VariableType>, std::set<VariableType>> parseMonotoneVariablesFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables);
+        };
+    }
+}
+
+
diff --git a/src/storm-pars/parser/ParameterRegionParser.cpp b/src/storm-pars/parser/ParameterRegionParser.cpp
index 4c293ab54..4dcb95708 100644
--- a/src/storm-pars/parser/ParameterRegionParser.cpp
+++ b/src/storm-pars/parser/ParameterRegionParser.cpp
@@ -42,7 +42,7 @@ namespace storm {
         }
 
         template<typename ParametricType>
-        storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables) {
+        storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
             Valuation lowerBoundaries;
             Valuation upperBoundaries;
             std::vector<std::string> parameterBoundaries;
@@ -58,24 +58,46 @@ namespace storm {
                 STORM_LOG_THROW(lowerBoundaries.count(v) > 0, storm::exceptions::WrongFormatException, "Variable " << v << " was not defined in region string.");
                 STORM_LOG_ASSERT(upperBoundaries.count(v) > 0, "Variable " << v << " has a lower but not an upper bound.");
             }
-            return storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
+            auto res = storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
+            if (splittingThreshold) {
+                res.setSplitThreshold(splittingThreshold.get());
+            }
+            return res;
+        }
+
+        template<typename ParametricType>
+        storm::storage::ParameterRegion<ParametricType> ParameterRegionParser<ParametricType>::createRegion(std::string const& regionBound, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
+            Valuation lowerBoundaries;
+            Valuation upperBoundaries;
+            std::vector<std::string> parameterBoundaries;
+            CoefficientType bound = storm::utility::convertNumber<CoefficientType>(regionBound);
+            STORM_LOG_THROW(0 < bound && bound < 1, storm::exceptions::WrongFormatException, "Bound must be between 0 and 1, " << bound << " is not.");
+            for (auto const& v : consideredVariables) {
+                lowerBoundaries.emplace(std::make_pair(v, 0+bound));
+                upperBoundaries.emplace(std::make_pair(v, 1-bound));
+            }
+            auto res = storm::storage::ParameterRegion<ParametricType>(std::move(lowerBoundaries), std::move(upperBoundaries));
+            if (splittingThreshold) {
+                res.setSplitThreshold(splittingThreshold.get());
+            }
+            return res;
         }
 
         template<typename ParametricType>
-        std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables) {
+        std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
             std::vector<storm::storage::ParameterRegion<ParametricType>> result;
             std::vector<std::string> regionsStrVec;
             boost::split(regionsStrVec, regionsString, boost::is_any_of(";"));
             for (auto const& regionStr : regionsStrVec){
                 if (!std::all_of(regionStr.begin(),regionStr.end(), ::isspace)){ //skip this string if it only consists of space
-                    result.emplace_back(parseRegion(regionStr, consideredVariables));
+                    result.emplace_back(parseRegion(regionStr, consideredVariables, splittingThreshold));
                 }
             }
             return result;
         }
 
         template<typename ParametricType>
-        std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables) {
+        std::vector<storm::storage::ParameterRegion<ParametricType>> ParameterRegionParser<ParametricType>::parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold) {
      
             // Open file and initialize result.
             std::ifstream inputFileStream;
@@ -86,7 +108,7 @@ namespace storm {
             // Now try to parse the contents of the file.
             try {
                 std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
-                result = parseMultipleRegions(fileContent, consideredVariables);
+                result = parseMultipleRegions(fileContent, consideredVariables, splittingThreshold);
             } catch(std::exception& e) {
                 // In case of an exception properly close the file before passing exception.
                 storm::utility::closeFile(inputFileStream);
diff --git a/src/storm-pars/parser/ParameterRegionParser.h b/src/storm-pars/parser/ParameterRegionParser.h
index 99063a4d6..fb574248e 100644
--- a/src/storm-pars/parser/ParameterRegionParser.h
+++ b/src/storm-pars/parser/ParameterRegionParser.h
@@ -25,20 +25,21 @@ namespace storm {
              * Parse a single region from a string of the form "0.3<=p<=0.5,0.4<=q<=0.7".
              *
              */
-            static storm::storage::ParameterRegion<ParametricType> parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables);
+            static storm::storage::ParameterRegion<ParametricType> parseRegion(std::string const& regionString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
+            static storm::storage::ParameterRegion<ParametricType> createRegion(std::string const& regionBound, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
 
             /*
              * Parse a vector of region from a string of the form "0.3<=p<=0.5,0.4<=q<=0.7;0.1<=p<=0.3,0.2<=q<=0.4".
              *
              */
-            static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables);
+            static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegions(std::string const& regionsString, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
             
 
             /*
              * Parse multiple regions from a file
              *
              */
-            static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables);
+            static std::vector<storm::storage::ParameterRegion<ParametricType>> parseMultipleRegionsFromFile(std::string const& fileName, std::set<VariableType> const& consideredVariables, boost::optional<int> splittingThreshold = boost::none);
             
         };
     }
diff --git a/src/storm-pars/settings/modules/MonotonicitySettings.cpp b/src/storm-pars/settings/modules/MonotonicitySettings.cpp
index dd7a91b80..764012972 100644
--- a/src/storm-pars/settings/modules/MonotonicitySettings.cpp
+++ b/src/storm-pars/settings/modules/MonotonicitySettings.cpp
@@ -11,43 +11,83 @@
 namespace storm {
     namespace settings {
         namespace modules {
-
-            const std::string MonotonicitySettings::moduleName = "monotonicity";
+    // TODO @Svenja, check what the module prefix is, maybe instead of doing mon- we could set this to true for the onces where we now have mon-"optionname"
+            const std::string MonotonicitySettings::moduleName = "mon";
             const std::string MonotonicitySettings::monotonicityAnalysis = "monotonicity-analysis";
-            const std::string MonotonicitySettings::sccElimination = "mon-elim-scc";
-            const std::string MonotonicitySettings::validateAssumptions = "mon-validate-assumptions";
-            const std::string MonotonicitySettings::samplesMonotonicityAnalysis = "mon-samples";
-            const std::string MonotonicitySettings::precision = "mon-precision";
+            const std::string MonotonicitySettings::monotonicityAnalysisShortName = "ma";
+            const std::string MonotonicitySettings::usePLABounds = "useBounds";
+            const std::string MonotonicitySettings::sccElimination = "eliminateSCCs";
+            const std::string MonotonicitySettings::samplesMonotonicityAnalysis = "samples";
+
+            const std::string MonotonicitySettings::dotOutput = "dotOutput";
+            const std::string MonotonicitySettings::exportMonotonicityName = "exportMonotonicity";
+            const std::string MonotonicitySettings::monSolution ="solutionFunction";
+            const std::string MonotonicitySettings::monSolutionShortName ="msf";
+            const std::string MonotonicitySettings::monotonicityThreshold ="depth";
+
+            const std::string MonotonicitySettings::monotoneParameters ="parameters";
 
             MonotonicitySettings::MonotonicitySettings() : ModuleSettings(moduleName) {
-                this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityAnalysis, false, "Sets whether monotonicity analysis is done").setIsAdvanced().build());
-                this->addOption(storm::settings::OptionBuilder(moduleName, sccElimination, false, "Sets whether SCCs should be eliminated in the monotonicity analysis").setIsAdvanced().build());
-                this->addOption(storm::settings::OptionBuilder(moduleName, validateAssumptions, false, "Sets whether assumptions made in monotonicity analysis are validated").setIsAdvanced().build());
-                this->addOption(storm::settings::OptionBuilder(moduleName, samplesMonotonicityAnalysis, false, "Sets whether monotonicity should be checked on samples").setIsAdvanced()
-                                        .addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("mon-samples", "The number of samples taken in monotonicity-analysis can be given, default is 0, no samples").setDefaultValueUnsignedInteger(0).build()).build());
-                this->addOption(storm::settings::OptionBuilder(moduleName, precision, false, "Sets precision of monotonicity checking on samples").setIsAdvanced()
-                                        .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("mon-precision", "The precision of checking monotonicity on samples, default is 1e-6").setDefaultValueDouble(0.000001).build()).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityAnalysis, false, "Sets whether monotonicity analysis is done").setIsAdvanced().setShortName(monotonicityAnalysisShortName).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, usePLABounds, true, "Sets whether pla bounds should be used for monotonicity analysis").setIsAdvanced().build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, sccElimination, true, "Sets whether SCCs should be eliminated in the monotonicity analysis").setIsAdvanced().build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, samplesMonotonicityAnalysis, true, "Sets whether monotonicity should be checked on samples").setIsAdvanced()
+                                        .addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument(samplesMonotonicityAnalysis, "The number of samples taken in monotonicity-analysis can be given, default is 0, no samples").setDefaultValueUnsignedInteger(0).build()).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, monSolution, true, "Sets whether monotonicity should be checked on solution function or reachability order").setIsAdvanced().setShortName(monSolutionShortName).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, dotOutput, true, "Sets whether a dot output of the ROs is needed").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("dotFilename", "The output file.").setDefaultValueString("dotOutput").makeOptional().build()).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, exportMonotonicityName, true, "Exports the result of monotonicity checking to the given file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, monotonicityThreshold, true, "Sets for region refinement after which depth whether monotonicity checking should be used.").setIsAdvanced()
+                                        .addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument(monotonicityThreshold, "The depth threshold from which on monotonicity is used for Parameter Lifting").setDefaultValueUnsignedInteger(0).build()).build());
 
+                this->addOption(storm::settings::OptionBuilder(moduleName, monotoneParameters, true, "Sets monotone parameters from file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("monotoneParametersFilename", "The file where the monotone parameters are set").build()).build());
             }
 
             bool MonotonicitySettings::isMonotonicityAnalysisSet() const {
                 return this->getOption(monotonicityAnalysis).getHasOptionBeenSet();
             }
 
+            bool MonotonicitySettings::isUsePLABoundsSet() const {
+                return this->getOption(usePLABounds).getHasOptionBeenSet();
+            }
+
             bool MonotonicitySettings::isSccEliminationSet() const {
                 return this->getOption(sccElimination).getHasOptionBeenSet();
             }
 
-            bool MonotonicitySettings::isValidateAssumptionsSet() const {
-                return this->getOption(validateAssumptions).getHasOptionBeenSet();
+            bool MonotonicitySettings::isDotOutputSet() const {
+                return this->getOption(dotOutput).getHasOptionBeenSet();
+            }
+
+            bool MonotonicitySettings::isMonotoneParametersSet() const {
+                return this->getOption(monotoneParameters).getHasOptionBeenSet();
+            }
+
+            std::string MonotonicitySettings::getDotOutputFilename() const {
+                return this->getOption(dotOutput).getArgumentByName("dotFilename").getValueAsString();
+            }
+
+            std::string MonotonicitySettings::getMonotoneParameterFilename() const {
+                return this->getOption(monotoneParameters).getArgumentByName("monotoneParametersFilename").getValueAsString();
             }
 
             uint_fast64_t MonotonicitySettings::getNumberOfSamples() const {
-                return this->getOption(samplesMonotonicityAnalysis).getArgumentByName("mon-samples").getValueAsUnsignedInteger();
+                return this->getOption(samplesMonotonicityAnalysis).getArgumentByName("samples").getValueAsUnsignedInteger();
+            }
+
+            bool MonotonicitySettings::isExportMonotonicitySet() const {
+                return this->getOption(exportMonotonicityName).getHasOptionBeenSet();
+            }
+
+            std::string MonotonicitySettings::getExportMonotonicityFilename() const {
+                return this->getOption(exportMonotonicityName).getArgumentByName("filename").getValueAsString();
+            }
+
+            uint_fast64_t MonotonicitySettings::getMonotonicityThreshold() const {
+                return this->getOption(monotonicityThreshold).getArgumentByName("depth").getValueAsUnsignedInteger();
             }
 
-            double MonotonicitySettings::getMonotonicityAnalysisPrecision() const {
-                return this->getOption(precision).getArgumentByName("mon-precision").getValueAsDouble();
+            bool MonotonicitySettings::isMonSolutionSet() const {
+                return this->getOption(monSolution).getHasOptionBeenSet();
             }
         } // namespace modules
     } // namespace settings
diff --git a/src/storm-pars/settings/modules/MonotonicitySettings.h b/src/storm-pars/settings/modules/MonotonicitySettings.h
index f420935cd..046628959 100644
--- a/src/storm-pars/settings/modules/MonotonicitySettings.h
+++ b/src/storm-pars/settings/modules/MonotonicitySettings.h
@@ -23,15 +23,26 @@ namespace storm {
                  */
                 bool isMonotonicityAnalysisSet() const;
 
+                bool isUsePLABoundsSet() const;
+
                 /*!
                  * Retrieves whether SCCs in the monotonicity analysis should be eliminated.
                  */
                 bool isSccEliminationSet() const;
 
                 /*!
-                 * Retrieves whether assumptions in monotonicity analysis should be validated
+                 * Retrieves whether a dot output of the reachability orders should be given
+                 */
+                bool isDotOutputSet() const;
+
+                bool isMonotoneParametersSet() const;
+
+                /*!
+                 * Retrieves the name of the file for a possible dot output
                  */
-                bool isValidateAssumptionsSet() const;
+                std::string getDotOutputFilename() const;
+
+                std::string getMonotoneParameterFilename() const;
 
                 /*!
                  * Retrieves the number of samples used for sampling in the monotonicity analysis
@@ -39,18 +50,38 @@ namespace storm {
                 uint_fast64_t getNumberOfSamples() const;
 
                 /*!
-				 * Retrieves the precision for the extremal value
-				 */
-                double getMonotonicityAnalysisPrecision() const;
+                 *
+                 */
+                bool isExportMonotonicitySet() const;
+
+                bool isMonSolutionSet() const;
+
+                /*!
+                 *
+                 */
+                std::string getExportMonotonicityFilename() const;
+
+                /*!
+                 * Retrieves the depth threshold from which on monotonicity should be used in parameter lifting
+                 */
+                uint64_t getMonotonicityThreshold() const;
+
 
                 const static std::string moduleName;
 
             private:
                 const static std::string monotonicityAnalysis;
+                const static std::string monotonicityAnalysisShortName;
+                const static std::string usePLABounds;
                 const static std::string sccElimination;
-                const static std::string validateAssumptions;
                 const static std::string samplesMonotonicityAnalysis;
-                const static std::string precision;
+                const static std::string dotOutput;
+                static const std::string exportMonotonicityName;
+                const static std::string monotonicityThreshold;
+                const static std::string monotoneParameters;
+                const static std::string monSolution;
+                const static std::string monSolutionShortName;
+
             };
 
         } // namespace modules
diff --git a/src/storm-pars/settings/modules/ParametricSettings.cpp b/src/storm-pars/settings/modules/ParametricSettings.cpp
index 45528844c..b558f5b03 100644
--- a/src/storm-pars/settings/modules/ParametricSettings.cpp
+++ b/src/storm-pars/settings/modules/ParametricSettings.cpp
@@ -21,6 +21,8 @@ namespace storm {
             const std::string ParametricSettings::samplesOptionName = "samples";
             const std::string ParametricSettings::samplesGraphPreservingOptionName = "samples-graph-preserving";
             const std::string ParametricSettings::sampleExactOptionName = "sample-exact";
+            const std::string ParametricSettings::useMonotonicityName = "use-monotonicity";
+//            const std::string ParametricSettings::onlyGlobalName = "onlyGlobal";
 
             ParametricSettings::ParametricSettings() : ModuleSettings(moduleName) {
                 this->addOption(storm::settings::OptionBuilder(moduleName, exportResultOptionName, false, "A path to a file where the parametric result should be saved.")
@@ -32,6 +34,8 @@ namespace storm {
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("samples", "The samples are semicolon-separated entries of the form 'Var1=Val1:Val2:...:Valk,Var2=... that span the sample spaces.").setDefaultValueString("").build()).build());
                 this->addOption(storm::settings::OptionBuilder(moduleName, samplesGraphPreservingOptionName, false, "Sets whether it can be assumed that the samples are graph-preserving.").build());
                 this->addOption(storm::settings::OptionBuilder(moduleName, sampleExactOptionName, false, "Sets whether to sample using exact arithmetic.").build());
+                this->addOption(storm::settings::OptionBuilder(moduleName, useMonotonicityName, false, "If set, monotonicity will be used.").build());
+//                this->addOption(storm::settings::OptionBuilder(moduleName, onlyGlobalName, false, "If set, only global monotonicity will be used.").build());
             }
             
             bool ParametricSettings::exportResultToFile() const {
@@ -66,6 +70,13 @@ namespace storm {
                 return this->getOption(sampleExactOptionName).getHasOptionBeenSet();
             }
 
+            bool ParametricSettings::isUseMonotonicitySet() const {
+                return this->getOption(useMonotonicityName).getHasOptionBeenSet();
+            }
+//            bool ParametricSettings::isOnlyGlobalSet() const {
+//                return this->getOption(onlyGlobalName).getHasOptionBeenSet();
+//            }
+
         } // namespace modules
     } // namespace settings
 } // namespace storm
diff --git a/src/storm-pars/settings/modules/ParametricSettings.h b/src/storm-pars/settings/modules/ParametricSettings.h
index 3fe281e4e..88e3d5d8d 100644
--- a/src/storm-pars/settings/modules/ParametricSettings.h
+++ b/src/storm-pars/settings/modules/ParametricSettings.h
@@ -64,6 +64,13 @@ namespace storm {
                  */
                 bool isSampleExactSet() const;
 
+                /*!
+                 * Retrieves whether monotonicity should be used
+                 */
+                bool isUseMonotonicitySet() const;
+
+//                bool isOnlyGlobalSet() const;
+
                 const static std::string moduleName;
                 
             private:
@@ -75,6 +82,9 @@ namespace storm {
                 const static std::string samplesOptionName;
                 const static std::string samplesGraphPreservingOptionName;
                 const static std::string sampleExactOptionName;
+                const static std::string useMonotonicityName;
+//                const static std::string onlyGlobalName;
+
             };
             
         } // namespace modules
diff --git a/src/storm-pars/settings/modules/RegionSettings.cpp b/src/storm-pars/settings/modules/RegionSettings.cpp
index cad9f4abe..abb6039a9 100644
--- a/src/storm-pars/settings/modules/RegionSettings.cpp
+++ b/src/storm-pars/settings/modules/RegionSettings.cpp
@@ -1,10 +1,11 @@
 #include <storm-pars/modelchecker/region/RegionResultHypothesis.h>
 #include "storm-pars/settings/modules/RegionSettings.h"
 
-#include "storm/settings/Option.h"
+#include "storm/settings/SettingsManager.h"
+#include "storm/settings/modules/GeneralSettings.h"
+
 #include "storm/settings/OptionBuilder.h"
 #include "storm/settings/ArgumentBuilder.h"
-#include "storm/settings/Argument.h"
 
 #include "storm/utility/macros.h"
 #include "storm/exceptions/IllegalArgumentValueException.h"
@@ -17,10 +18,13 @@ namespace storm {
             const std::string RegionSettings::moduleName = "region";
             const std::string RegionSettings::regionOptionName = "region";
             const std::string RegionSettings::regionShortOptionName = "reg";
+            const std::string RegionSettings::regionBoundOptionName = "regionbound";
             const std::string RegionSettings::hypothesisOptionName = "hypothesis";
             const std::string RegionSettings::hypothesisShortOptionName = "hyp";
             const std::string RegionSettings::refineOptionName = "refine";
             const std::string RegionSettings::extremumOptionName = "extremum";
+            const std::string RegionSettings::extremumSuggestionOptionName = "extremum-init";
+            const std::string RegionSettings::splittingThresholdName = "splitting-threshold";
             const std::string RegionSettings::checkEngineOptionName = "engine";
             const std::string RegionSettings::printNoIllustrationOptionName = "noillustration";
             const std::string RegionSettings::printFullResultOptionName = "printfullresult";
@@ -28,7 +32,10 @@ namespace storm {
             RegionSettings::RegionSettings() : ModuleSettings(moduleName) {
                 this->addOption(storm::settings::OptionBuilder(moduleName, regionOptionName, false, "Sets the region(s) considered for analysis.").setShortName(regionShortOptionName)
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("regioninput", "The region(s) given in format a<=x<=b,c<=y<=d seperated by ';'. Can also be a file.").build()).build());
-                
+
+                this->addOption(storm::settings::OptionBuilder(moduleName, regionBoundOptionName, false, "Sets the region bound considered for analysis.")
+                                .addArgument(storm::settings::ArgumentBuilder::createStringArgument("regionbound", "The bound for the region result for all variables: 0+bound <= var <=1-bound").build()).build());
+
                 std::vector<std::string> hypotheses = {"unknown", "allsat", "allviolated"};
                 this->addOption(storm::settings::OptionBuilder(moduleName, hypothesisOptionName, false, "Sets a hypothesis for region analysis. If given, the region(s) are only analyzed w.r.t. that hypothesis.").setShortName(hypothesisShortOptionName)
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("hypothesis", "The hypothesis.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(hypotheses)).setDefaultValueString("unknown").build()).build());
@@ -41,7 +48,13 @@ namespace storm {
                 this->addOption(storm::settings::OptionBuilder(moduleName, extremumOptionName, false, "Computes the extremum within the region.")
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("direction", "The optimization direction").addValidatorString(storm::settings::ArgumentValidatorFactory::createMultipleChoiceValidator(directions)).build())
                                 .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision", "The desired precision").setDefaultValueDouble(0.05).makeOptional().addValidatorDouble(storm::settings::ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0,1.0)).build()).build());
-                
+
+                this->addOption(storm::settings::OptionBuilder(moduleName, extremumSuggestionOptionName, false, "Checks whether the provided value is indeed the extremum")
+                                        .addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("extremum-suggestion", "The provided value for the extremum").addValidatorDouble(storm::settings::ArgumentValidatorFactory::createDoubleRangeValidatorIncluding(0.0,1.0)).build()).build());
+
+                this->addOption(storm::settings::OptionBuilder(moduleName, splittingThresholdName, false, "Sets the threshold for number of parameters in which to split regions.")
+                                        .addArgument(storm::settings::ArgumentBuilder::createIntegerArgument("splitting-threshold", "The threshold for splitting, should be an integer > 0").build()).build());
+
                 std::vector<std::string> engines = {"pl", "exactpl", "validatingpl"};
                 this->addOption(storm::settings::OptionBuilder(moduleName, checkEngineOptionName, true, "Sets which engine is used for analyzing regions.")
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the engine to use.").addValidatorString(ArgumentValidatorFactory::createMultipleChoiceValidator(engines)).setDefaultValueString("pl").build()).build());
@@ -58,6 +71,14 @@ namespace storm {
             std::string RegionSettings::getRegionString() const {
                 return this->getOption(regionOptionName).getArgumentByName("regioninput").getValueAsString();
             }
+
+            bool RegionSettings::isRegionBoundSet() const {
+                return this->getOption(regionBoundOptionName).getHasOptionBeenSet();
+            }
+
+            std::string RegionSettings::getRegionBoundString() const {
+                return this->getOption(regionBoundOptionName).getArgumentByName("regionbound").getValueAsString();
+            }
             
             bool RegionSettings::isHypothesisSet() const {
                 return this->getOption(hypothesisOptionName).getHasOptionBeenSet();
@@ -114,9 +135,23 @@ namespace storm {
             }
 				
             double RegionSettings::getExtremumValuePrecision() const {
+                auto generalSettings = storm::settings::getModule<storm::settings::modules::GeneralSettings>();
+                if (!generalSettings.isPrecisionSet() && generalSettings.isSoundSet()) {
+                    double prec = this->getOption(extremumOptionName).getArgumentByName("precision").getValueAsDouble() / 10;
+                    generalSettings.setPrecision(std::to_string(prec));
+                    STORM_LOG_WARN("Reset precision for solver to " << prec << " this is sufficient for extremum value precision of " << (prec)*10 << std::endl);
+                }
                 return this->getOption(extremumOptionName).getArgumentByName("precision").getValueAsDouble();
             }
 
+            bool RegionSettings::isExtremumSuggestionSet() const {
+                return this->getOption(extremumSuggestionOptionName).getHasOptionBeenSet();
+            }
+
+            double RegionSettings::getExtremumSuggestion() const {
+                return this->getOption(extremumSuggestionOptionName).getArgumentByName("extremum-suggestion").getValueAsDouble();
+            }
+
             storm::modelchecker::RegionCheckEngine RegionSettings::getRegionCheckEngine() const {
                 std::string engineString = this->getOption(checkEngineOptionName).getArgumentByName("name").getValueAsString();
                 
@@ -139,6 +174,10 @@ namespace storm {
                     STORM_LOG_ERROR("Can not compute extremum values AND perform region refinement.");
                     return false;
                 }
+                if (getExtremumValuePrecision() < storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision()) {
+                    STORM_LOG_ERROR("Computing extremum value for precision " << getExtremumValuePrecision() << " makes no sense when solver precision is set to " << storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
+                    return false;
+                }
                 return true;
             }
 
@@ -149,7 +188,15 @@ namespace storm {
             bool RegionSettings::isPrintFullResultSet() const {
                 return this->getOption(printFullResultOptionName).getHasOptionBeenSet();
             }
-            
+
+            int RegionSettings::getSplittingThreshold() const {
+                return this->getOption(splittingThresholdName).getArgumentByName("splitting-threshold").getValueAsInteger();
+            }
+
+            bool RegionSettings::isSplittingThresholdSet() const {
+                return this->getOption(splittingThresholdName).getHasOptionBeenSet();
+            }
+
 
         } // namespace modules
     } // namespace settings
diff --git a/src/storm-pars/settings/modules/RegionSettings.h b/src/storm-pars/settings/modules/RegionSettings.h
index b129b19d7..3d1e10b28 100644
--- a/src/storm-pars/settings/modules/RegionSettings.h
+++ b/src/storm-pars/settings/modules/RegionSettings.h
@@ -30,7 +30,16 @@ namespace storm {
 				 * Retrieves the region definition string
 				 */
 				std::string getRegionString() const;
-				
+
+                /*!
+                 * Retrieves whether region bound is declared
+                 */
+                bool isRegionBoundSet() const;
+
+                /*!
+                 * Retrieves the region definition string
+                 */
+                std::string getRegionBoundString() const;
 				/*!
 				 * Retrieves whether region(s) were declared
 				 */
@@ -77,6 +86,14 @@ namespace storm {
 				 */
 				double getExtremumValuePrecision() const;
 
+                bool isExtremumSuggestionSet() const;
+
+                double getExtremumSuggestion() const;
+
+                bool isSplittingThresholdSet() const;
+
+                int getSplittingThreshold() const;
+
 				/*!
 				 * Retrieves which type of region check should be performed
 				 */
@@ -99,10 +116,13 @@ namespace storm {
             private:
 				const static std::string regionOptionName;
 				const static std::string regionShortOptionName;
+				const static std::string regionBoundOptionName;
 				const static std::string hypothesisOptionName;
 				const static std::string hypothesisShortOptionName;
 				const static std::string refineOptionName;
+				const static std::string splittingThresholdName;
 				const static std::string extremumOptionName;
+				const static std::string extremumSuggestionOptionName;
 				const static std::string checkEngineOptionName;
 				const static std::string printNoIllustrationOptionName;
 				const static std::string printFullResultOptionName;
diff --git a/src/storm-pars/storage/ParameterRegion.cpp b/src/storm-pars/storage/ParameterRegion.cpp
index 71030ff4d..ba64bf30e 100644
--- a/src/storm-pars/storage/ParameterRegion.cpp
+++ b/src/storm-pars/storage/ParameterRegion.cpp
@@ -33,10 +33,12 @@ namespace storm {
                 STORM_LOG_THROW((variableWithUpperBoundary != upperBoundaries.end()), storm::exceptions::InvalidArgumentException, "Could not create region. No upper boundary specified for Variable " << variableWithLowerBoundary.first);
                 STORM_LOG_THROW((variableWithLowerBoundary.second<=variableWithUpperBoundary->second), storm::exceptions::InvalidArgumentException, "Could not create region. The lower boundary for " << variableWithLowerBoundary.first << " is larger then the upper boundary");
                 this->variables.insert(variableWithLowerBoundary.first);
+                this->sortedOnDifference.insert({variableWithLowerBoundary.second - variableWithUpperBoundary->second, variableWithLowerBoundary.first});
             }
             for (auto const& variableWithBoundary : this->upperBoundaries) {
                 STORM_LOG_THROW((this->variables.find(variableWithBoundary.first) != this->variables.end()), storm::exceptions::InvalidArgumentException, "Could not create region. No lower boundary specified for Variable " << variableWithBoundary.first);
             }
+            this->splitThreshold = variables.size();
         }
 
         template<typename ParametricType>
@@ -45,7 +47,12 @@ namespace storm {
         }
 
         template<typename ParametricType>
-            typename ParameterRegion<ParametricType>::CoefficientType const& ParameterRegion<ParametricType>::getLowerBoundary(VariableType const& variable) const {
+        std::multimap<typename ParameterRegion<ParametricType>::CoefficientType , typename ParameterRegion<ParametricType>::VariableType> const& ParameterRegion<ParametricType>::getVariablesSorted() const {
+            return this->sortedOnDifference;
+        }
+
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::CoefficientType const& ParameterRegion<ParametricType>::getLowerBoundary(VariableType const& variable) const {
             auto const& result = lowerBoundaries.find(variable);
             STORM_LOG_THROW(result != lowerBoundaries.end(), storm::exceptions::InvalidArgumentException, "Tried to find a lower boundary for variable " << variable << " which is not specified by this region");
             return (*result).second;
@@ -78,6 +85,16 @@ namespace storm {
             STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to find an upper boundary for variableName " << varName << " which is not specified by this region");
         }
 
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getDifference(VariableType const& variable) const {
+            return getUpperBoundary(variable) - getLowerBoundary(variable);
+        }
+
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getDifference(const std::string varName) const {
+            return getUpperBoundary(varName) - getLowerBoundary(varName);
+        }
+
         template<typename ParametricType>
         typename ParameterRegion<ParametricType>::Valuation const& ParameterRegion<ParametricType>::getUpperBoundaries() const {
             return upperBoundaries;
@@ -100,7 +117,8 @@ namespace storm {
                 //the consideredVariables.size() least significant bits of vertex will always represent the next vertex
                 //(00...0 = lower boundaries for all variables, 11...1 = upper boundaries for all variables)
                 uint_fast64_t variableIndex = 0;
-                for (auto const& variable : consideredVariables) {
+
+                for (auto variable : consideredVariables) {
                     if ((vertexId >> variableIndex) % 2 == 0) {
                         resultingVector[vertexId].insert(std::pair<VariableType, CoefficientType>(variable, getLowerBoundary(variable)));
                     } else {
@@ -112,6 +130,7 @@ namespace storm {
             return resultingVector;
         }
 
+
         template<typename ParametricType>
         typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getSomePoint() const {
             return this->getLowerBoundaries();
@@ -136,30 +155,38 @@ namespace storm {
         }
 
         template<typename ParametricType>
-        void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType> >& regionVector) const{
-            //Check if splittingPoint is valid.
-            STORM_LOG_THROW(splittingPoint.size() == this->variables.size(), storm::exceptions::InvalidArgumentException, "Tried to split a region w.r.t. a point, but the point considers a different number of variables.");
-            for(auto const& variable : this->variables){
-                auto splittingPointEntry=splittingPoint.find(variable);
-                STORM_LOG_THROW(splittingPointEntry != splittingPoint.end(), storm::exceptions::InvalidArgumentException, "Tried to split a region but a variable of this region is not defined by the splitting point.");
-                STORM_LOG_THROW(this->getLowerBoundary(variable) <=splittingPointEntry->second, storm::exceptions::InvalidArgumentException, "Tried to split a region but the splitting point is not contained in the region.");
-                STORM_LOG_THROW(this->getUpperBoundary(variable) >=splittingPointEntry->second, storm::exceptions::InvalidArgumentException, "Tried to split a region but the splitting point is not contained in the region.");
-            }
-                
-            //Now compute the subregions.
-            std::vector<Valuation> vertices(this->getVerticesOfRegion(this->variables));
-            for(auto const& vertex : vertices){
+        void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector) const{
+            return split(splittingPoint, regionVector, variables);
+        }
+
+        template<typename ParametricType>
+        void ParameterRegion<ParametricType>::split(Valuation const& splittingPoint, std::vector<storm::storage::ParameterRegion<ParametricType>> &regionVector,
+                                                    const std::set<VariableType> &consideredVariables) const {
+
+            auto vertices = getVerticesOfRegion(consideredVariables);
+
+            for (auto const &vertex : vertices) {
                 //The resulting subregion is the smallest region containing vertex and splittingPoint.
                 Valuation subLower, subUpper;
-                for(auto variableBound : this->lowerBoundaries){
+                for (auto variableBound : this->lowerBoundaries) {
                     VariableType variable = variableBound.first;
-                    auto vertexEntry=vertex.find(variable);
-                    auto splittingPointEntry=splittingPoint.find(variable);
-                    subLower.insert(typename Valuation::value_type(variable, std::min(vertexEntry->second, splittingPointEntry->second)));
-                    subUpper.insert(typename Valuation::value_type(variable, std::max(vertexEntry->second, splittingPointEntry->second)));
+                    auto vertexEntry = vertex.find(variable);
+                    if (vertexEntry != vertex.end()) {
+                        auto splittingPointEntry = splittingPoint.find(variable);
+                        subLower.insert(typename Valuation::value_type(variable, std::min(vertexEntry->second,
+                                                                                          splittingPointEntry->second)));
+                        subUpper.insert(typename Valuation::value_type(variable, std::max(vertexEntry->second,
+                                                                                          splittingPointEntry->second)));
+                    } else {
+                        subLower.insert(typename Valuation::value_type(variable, getLowerBoundary(variable)));
+                        subUpper.insert(typename Valuation::value_type(variable, getUpperBoundary(variable)));
+                    }
                 }
+
                 ParameterRegion<ParametricType> subRegion(std::move(subLower), std::move(subUpper));
-                if(!storm::utility::isZero(subRegion.area())){
+                subRegion.setSplitThreshold(this->getSplitThreshold());
+
+                if (!storm::utility::isZero(subRegion.area())) {
                     regionVector.push_back(std::move(subRegion));
                 }
             }
@@ -192,18 +219,84 @@ namespace storm {
             regionstring = regionstring.substr(0, regionstring.length() - 1) + ";";
             return regionstring;
         }
-        
+
+        template <typename ParametricType>
+        bool ParameterRegion<ParametricType>::isSubRegion(ParameterRegion<ParametricType> subRegion) {
+            auto varsRegion = getVariables();
+            auto varsSubRegion = subRegion.getVariables();
+            for (auto var : varsRegion) {
+                if (std::find(varsSubRegion.begin(), varsSubRegion.end(), var) != varsSubRegion.end()) {
+                    if (getLowerBoundary(var) > subRegion.getLowerBoundary(var) || getUpperBoundary(var) < getUpperBoundary(var)) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getPoint(storm::solver::OptimizationDirection dir, storm::analysis::MonotonicityResult<VariableType> &monRes) const {
+            auto val = this->getCenterPoint();
+            for (auto monResEntry : monRes.getMonotonicityResult()) {
+                if (monRes.isDoneForVar(monResEntry.first)) {
+                    if (monResEntry.second == storm::analysis::MonotonicityResult<VariableType>::Monotonicity::Incr) {
+                        val[monResEntry.first] = storm::solver::minimize(dir) ? getLowerBoundary(monResEntry.first) : getUpperBoundary(monResEntry.first);
+                    } else if (monResEntry.second == storm::analysis::MonotonicityResult<VariableType>::Monotonicity::Decr) {
+                        val[monResEntry.first] = storm::solver::maximize(dir) ? getLowerBoundary(monResEntry.first) : getUpperBoundary(monResEntry.first);
+                    }
+                }
+            }
+            return val;
+        }
+
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::Valuation ParameterRegion<ParametricType>::getPoint(storm::solver::OptimizationDirection dir,
+                                                                             std::set<VariableType> const &monIncrParameters,
+                                                                             std::set<VariableType> const &monDecrParameters) const{
+            auto val = this->getCenterPoint();
+            for (auto var : monIncrParameters) {
+                val[var] = storm::solver::minimize(dir) ? getLowerBoundary(var) : getUpperBoundary(var);
+            }
+            for (auto var : monDecrParameters) {
+                val[var] = storm::solver::maximize(dir) ? getLowerBoundary(var) : getUpperBoundary(var);
+            }
+
+            return val;
+        }
+
+        template<typename ParametricType>
+        typename ParameterRegion<ParametricType>::CoefficientType ParameterRegion<ParametricType>::getBoundParent() {
+            return parentBound;
+        }
+
+        template<typename ParametricType>
+        void ParameterRegion<ParametricType>::setBoundParent(CoefficientType bound) {
+            parentBound = bound;
+        }
+
         template <typename ParametricType>
         std::ostream& operator<<(std::ostream& out, ParameterRegion<ParametricType> const& region) {
             out << region.toString();
             return out;
         }
 
-        
+        template <typename ParametricType>
+        void ParameterRegion<ParametricType>::setSplitThreshold(int splitThreshold) {
+            this->splitThreshold = splitThreshold;
+        }
+
+        template <typename ParametricType>
+        int ParameterRegion<ParametricType>::getSplitThreshold() const {
+            return splitThreshold;
+        }
+
+
 #ifdef STORM_HAVE_CARL
-            template class ParameterRegion<storm::RationalFunction>;
-            template std::ostream& operator<<(std::ostream& out, ParameterRegion<storm::RationalFunction> const& region);
+        template class ParameterRegion<storm::RationalFunction>;
+        template std::ostream& operator<<(std::ostream& out, ParameterRegion<storm::RationalFunction> const& region);
 #endif
+
     }
 }
-
diff --git a/src/storm-pars/storage/ParameterRegion.h b/src/storm-pars/storage/ParameterRegion.h
index d4f3ce457..7545bb2ab 100644
--- a/src/storm-pars/storage/ParameterRegion.h
+++ b/src/storm-pars/storage/ParameterRegion.h
@@ -3,6 +3,9 @@
 #include <map>
 
 #include "storm-pars/utility/parametric.h"
+#include "storm-pars/analysis/MonotonicityResult.h"
+#include "storm-pars/modelchecker/region/RegionResult.h"
+#include "storm/solver/OptimizationDirection.h"
 
 namespace storm {
     namespace storage {
@@ -23,10 +26,13 @@ namespace storm {
             virtual ~ParameterRegion() = default;
 
             std::set<VariableType> const& getVariables() const;
+            std::multimap<CoefficientType, VariableType> const& getVariablesSorted() const;
             CoefficientType const& getLowerBoundary(VariableType const& variable) const;
             CoefficientType const& getLowerBoundary(const std::string varName) const;
             CoefficientType const& getUpperBoundary(VariableType const& variable) const;
             CoefficientType const& getUpperBoundary(const std::string varName) const;
+            CoefficientType getDifference(const std::string varName) const;
+            CoefficientType getDifference(VariableType const& variable) const;
             Valuation const& getLowerBoundaries() const;
             Valuation const& getUpperBoundaries() const;
 
@@ -50,7 +56,10 @@ namespace storm {
              * Returns the center point of this region
              */
             Valuation getCenterPoint() const;
-                
+
+            void setSplitThreshold(int splitThreshold);
+            int getSplitThreshold() const;
+
             /*!
              * Returns the area of this region
              */
@@ -62,17 +71,31 @@ namespace storm {
              * Subregions with area()==0 are not inserted in the vector.
              */
             void split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector) const;
+            void split(Valuation const& splittingPoint, std::vector<ParameterRegion<ParametricType>>& regionVector, std::set<VariableType> const& consideredVariables) const;
+
+            Valuation getPoint(storm::solver::OptimizationDirection dir, storm::analysis::MonotonicityResult<VariableType> & monRes) const;
+            Valuation getPoint(storm::solver::OptimizationDirection dir, std::set<VariableType> const& possibleMonotoneIncrParameters, std::set<VariableType>const & monDecrParameters) const;
 
             //returns the region as string in the format 0.3<=p<=0.4,0.2<=q<=0.5;
             std::string toString(bool boundariesAsDouble = false) const;
 
+            bool isSubRegion(ParameterRegion<ParametricType> region);
+
+            CoefficientType getBoundParent();
+            void setBoundParent(CoefficientType bound);
+
         private:
 
             void init();
+
+            bool lastSplitMonotone = false;
+            int splitThreshold;
             
             Valuation lowerBoundaries;
             Valuation upperBoundaries;
             std::set<VariableType> variables;
+            std::multimap<CoefficientType, VariableType> sortedOnDifference;
+            CoefficientType parentBound;
         };
 
         template<typename ParametricType>
diff --git a/src/storm-pars/transformer/ParameterLifter.cpp b/src/storm-pars/transformer/ParameterLifter.cpp
index 9eba6e8f2..c03493185 100644
--- a/src/storm-pars/transformer/ParameterLifter.cpp
+++ b/src/storm-pars/transformer/ParameterLifter.cpp
@@ -1,10 +1,7 @@
 #include "storm-pars/transformer/ParameterLifter.h"
 
-
 #include "storm/adapters/RationalFunctionAdapter.h"
-
 #include "storm/utility/vector.h"
-
 #include "storm/exceptions/UnexpectedException.h"
 #include "storm/exceptions/NotSupportedException.h"
 
@@ -12,41 +9,54 @@ namespace storm {
     namespace transformer {
 
         template<typename ParametricType, typename ConstantType>
-        ParameterLifter<ParametricType, ConstantType>::ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels) {
-        
-            
+        ParameterLifter<ParametricType, ConstantType>::ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns, bool generateRowLabels, bool useMonotonicityInFuture) {
             // get a mapping from old column indices to new ones
-            std::vector<uint_fast64_t> oldToNewColumnIndexMapping(selectedColumns.size(), selectedColumns.size());
+            oldToNewColumnIndexMapping = std::vector<uint_fast64_t>(selectedColumns.size(), selectedColumns.size());
             uint_fast64_t newIndex = 0;
             for (auto const& oldColumn : selectedColumns) {
                 oldToNewColumnIndexMapping[oldColumn] = newIndex++;
             }
+
+            // create vector, such that the occuringVariables for all states can be stored
+            occurringVariablesAtState = std::vector<std::set<VariableType>>(pMatrix.getColumnCount());
             
             // Stores which entries of the original matrix/vector are non-constant. Entries for non-selected rows/columns are omitted
-            storm::storage::BitVector nonConstMatrixEntries(pMatrix.getEntryCount(), false); //this vector has to be resized later
-            storm::storage::BitVector nonConstVectorEntries(selectedRows.getNumberOfSetBits(), false);
+            auto nonConstMatrixEntries = storm::storage::BitVector(pMatrix.getEntryCount(), false); //this vector has to be resized later
+            auto nonConstVectorEntries = storm::storage::BitVector(selectedRows.getNumberOfSetBits(), false);
             // Counters for selected entries in the pMatrix and the pVector
             uint_fast64_t pMatrixEntryCount = 0;
             uint_fast64_t pVectorEntryCount = 0;
             
             // The matrix builder for the new matrix. The correct number of rows and entries is not known yet.
             storm::storage::SparseMatrixBuilder<ConstantType> builder(0, selectedColumns.getNumberOfSetBits(), 0, true, true, selectedRows.getNumberOfSetBits());
+            rowGroupToStateNumber = std::vector<uint_fast64_t>();
             uint_fast64_t newRowIndex = 0;
+            uint_fast64_t countNonParam = 0;
             for (auto const& rowIndex : selectedRows) {
                 builder.newRowGroup(newRowIndex);
+                rowGroupToStateNumber.push_back(rowIndex);
                 
                 // Gather the occurring variables within this row and set which entries are non-constant
                 std::set<VariableType> occurringVariables;
+                bool constant = true;
                 for (auto const& entry : pMatrix.getRow(rowIndex)) {
                     if (selectedColumns.get(entry.getColumn())) {
                         if (!storm::utility::isConstant(entry.getValue())) {
                             storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
                             nonConstMatrixEntries.set(pMatrixEntryCount, true);
+                            constant = false;
                         }
                         ++pMatrixEntryCount;
+                    } else {
+                        if (!storm::utility::isConstant(entry.getValue())) {
+                            storm::utility::parametric::gatherOccurringVariables(entry.getValue(), occurringVariables);
+                        }
                     }
                 }
-                
+
+                if (constant) {
+                    countNonParam++;
+                }
                 ParametricType const& pVectorEntry = pVector[rowIndex];
                 std::set<VariableType> vectorEntryVariables;
                 if (!storm::utility::isConstant(pVectorEntry)) {
@@ -59,15 +69,14 @@ namespace storm {
                     nonConstVectorEntries.set(pVectorEntryCount, true);
                 }
                 ++pVectorEntryCount;
-                
                 // Compute the (abstract) valuation for each row
                 auto rowValuations = getVerticesOfAbstractRegion(occurringVariables);
-                
                 for (auto const& val : rowValuations) {
                     if (generateRowLabels) {
                         rowLabels.push_back(val);
                     }
-                    
+
+                    auto countPlaceHolders = 0;
                     // Insert matrix entries for each valuation. For non-constant entries, a dummy value is inserted and the function and the valuation are collected.
                     // The placeholder for the collected function/valuation are stored in the matrixAssignment. The matrixAssignment is completed after the matrix is finished
                     for (auto const& entry: pMatrix.getRow(rowIndex)) {
@@ -78,6 +87,7 @@ namespace storm {
                                 builder.addNextValue(newRowIndex, oldToNewColumnIndexMapping[entry.getColumn()], storm::utility::one<ConstantType>());
                                 ConstantType& placeholder = functionValuationCollector.add(entry.getValue(), val);
                                 matrixAssignment.push_back(std::pair<typename storm::storage::SparseMatrix<ConstantType>::iterator, ConstantType&>(typename storm::storage::SparseMatrix<ConstantType>::iterator(), placeholder));
+                                countPlaceHolders++;
                             }
                         }
                     }
@@ -100,15 +110,22 @@ namespace storm {
 
                     ++newRowIndex;
                 }
+                if (useMonotonicityInFuture) {
+                    // Save the occuringVariables of a state, needed if we want to use monotonicity
+                    for (auto& var : occurringVariables) {
+                        occuringStatesAtVariable[var].insert(rowIndex);
+                    }
+                    occurringVariablesAtState[rowIndex] = std::move(occurringVariables);
+                }
             }
-            
+
             // Matrix and vector are now filled with constant results from constant functions and place holders for non-constant functions.
             matrix = builder.build(newRowIndex);
             vector.shrink_to_fit();
             matrixAssignment.shrink_to_fit();
             vectorAssignment.shrink_to_fit();
             nonConstMatrixEntries.resize(pMatrixEntryCount);
-            
+
             // Now insert the correct iterators for the matrix and vector assignment
             auto matrixAssignmentIt = matrixAssignment.begin();
             uint_fast64_t startEntryOfRow = 0;
@@ -139,21 +156,41 @@ namespace storm {
         void ParameterLifter<ParametricType, ConstantType>::specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters) {
             // write the evaluation result of each function,evaluation pair into the placeholders
             functionValuationCollector.evaluateCollectedFunctions(region, dirForParameters);
-            
+
             //apply the matrix and vector assignments to write the contents of the placeholder into the matrix/vector
-                            
-            for(auto& assignment : matrixAssignment) {
+            for (auto &assignment : matrixAssignment) {
                 STORM_LOG_WARN_COND(!storm::utility::isZero(assignment.second), "Parameter lifting on region " << region.toString() << " affects the underlying graph structure (the region is not strictly well defined). The result for this region might be incorrect.");
                 assignment.first->setValue(assignment.second);
             }
-            for(auto& assignment : vectorAssignment) {
+
+            for (auto &assignment : vectorAssignment) {
                 *assignment.first = assignment.second;
             }
         }
+
+        template<typename ParametricType, typename ConstantType>
+        uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupIndex(uint_fast64_t originalState) const {
+            return matrix.getRowGroupIndices()[oldToNewColumnIndexMapping[originalState]];
+        }
+
+        template<typename ParametricType, typename ConstantType>
+        uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getOriginalStateNumber(uint_fast64_t newState) const {
+            return rowGroupToStateNumber[newState];
+        }
+
+        template<typename ParametricType, typename ConstantType>
+        uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupSize(uint_fast64_t originalState) const {
+            return matrix.getRowGroupSize(oldToNewColumnIndexMapping[originalState]);
+        }
+        template<typename ParametricType, typename ConstantType>
+        uint_fast64_t ParameterLifter<ParametricType, ConstantType>::getRowGroupCount() const {
+            return matrix.getRowGroupCount();
+        }
     
         template<typename ParametricType, typename ConstantType>
         storm::storage::SparseMatrix<ConstantType> const& ParameterLifter<ParametricType, ConstantType>::getMatrix() const {
             return matrix;
+
         }
     
         template<typename ParametricType, typename ConstantType>
@@ -188,6 +225,16 @@ namespace storm {
             return result;
         }
 
+        template<typename ParametricType, typename ConstantType>
+        const std::vector<std::set<typename ParameterLifter<ParametricType, ConstantType>::VariableType>> & ParameterLifter<ParametricType, ConstantType>::getOccurringVariablesAtState() const {
+            return occurringVariablesAtState;
+        }
+
+        template<typename ParametricType, typename ConstantType>
+        std::map<typename ParameterLifter<ParametricType, ConstantType>::VariableType, std::set<uint_fast64_t>> ParameterLifter<ParametricType, ConstantType>::getOccuringStatesAtVariable() const {
+            return occuringStatesAtVariable;
+        }
+
         template<typename ParametricType, typename ConstantType>
         bool ParameterLifter<ParametricType, ConstantType>::AbstractValuation::operator==(AbstractValuation const& other) const {
             return this->lowerPars == other.lowerPars && this->upperPars == other.upperPars && this->unspecifiedPars == other.unspecifiedPars;
@@ -268,7 +315,13 @@ namespace storm {
             }
             return result;
         }
-        
+
+        template<typename ParametricType, typename ConstantType>
+        uint_fast64_t ParameterLifter<ParametricType, ConstantType>::AbstractValuation::getOriginalState(
+                uint_fast64_t newStateNumber) const {
+            return 0;
+        }
+
         template<typename ParametricType, typename ConstantType>
         ConstantType& ParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::add(ParametricType const& function, AbstractValuation const& valuation) {
             ParametricType simplifiedFunction = function;
@@ -284,17 +337,16 @@ namespace storm {
     
         template<typename ParametricType, typename ConstantType>
         void ParameterLifter<ParametricType, ConstantType>::FunctionValuationCollector::evaluateCollectedFunctions(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForUnspecifiedParameters) {
-            for (auto& collectedFunctionValuationPlaceholder : collectedFunctions) {
-                ParametricType const& function = collectedFunctionValuationPlaceholder.first.first;
-                AbstractValuation const& abstrValuation = collectedFunctionValuationPlaceholder.first.second;
-                ConstantType& placeholder = collectedFunctionValuationPlaceholder.second;
-                
+            for (auto &collectedFunctionValuationPlaceholder : collectedFunctions) {
+                ParametricType const &function = collectedFunctionValuationPlaceholder.first.first;
+                AbstractValuation const &abstrValuation = collectedFunctionValuationPlaceholder.first.second;
+                ConstantType &placeholder = collectedFunctionValuationPlaceholder.second;
                 auto concreteValuations = abstrValuation.getConcreteValuations(region);
                 auto concreteValuationIt = concreteValuations.begin();
                 placeholder = storm::utility::convertNumber<ConstantType>(storm::utility::parametric::evaluate(function, *concreteValuationIt));
-                for(++concreteValuationIt; concreteValuationIt != concreteValuations.end(); ++concreteValuationIt) {
+                for (++concreteValuationIt; concreteValuationIt != concreteValuations.end(); ++concreteValuationIt) {
                     ConstantType currentResult = storm::utility::convertNumber<ConstantType>(storm::utility::parametric::evaluate(function, *concreteValuationIt));
-                    if(storm::solver::minimize(dirForUnspecifiedParameters)) {
+                    if (storm::solver::minimize(dirForUnspecifiedParameters)) {
                         placeholder = std::min(placeholder, currentResult);
                     } else {
                         placeholder = std::max(placeholder, currentResult);
diff --git a/src/storm-pars/transformer/ParameterLifter.h b/src/storm-pars/transformer/ParameterLifter.h
index c7837fa5c..4ce8d50c9 100644
--- a/src/storm-pars/transformer/ParameterLifter.h
+++ b/src/storm-pars/transformer/ParameterLifter.h
@@ -11,6 +11,9 @@
 #include "storm/storage/BitVector.h"
 #include "storm/storage/SparseMatrix.h"
 #include "storm/solver/OptimizationDirection.h"
+#include "storm-pars/analysis/Order.h"
+
+#include "storm-pars/analysis/MonotonicityChecker.h"
 
 namespace storm {
     namespace transformer {
@@ -31,7 +34,8 @@ namespace storm {
             
             typedef typename storm::utility::parametric::VariableType<ParametricType>::type VariableType;
             typedef typename storm::utility::parametric::CoefficientType<ParametricType>::type CoefficientType;
-            
+            typedef typename storm::analysis::MonotonicityResult<VariableType>::Monotonicity Monotonicity;
+
             /*!
              * Lifts the parameter choices to nondeterminisim. The computation is performed on the submatrix specified by the selected rows and columns
              * @param pMatrix the parametric matrix
@@ -39,16 +43,41 @@ namespace storm {
              * @param selectedRows a Bitvector that specifies which rows of the matrix and the vector are considered.
              * @param selectedColumns a Bitvector that specifies which columns of the matrix are considered.
              */
-            ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns,  bool generateRowLabels = false);
+            ParameterLifter(storm::storage::SparseMatrix<ParametricType> const& pMatrix, std::vector<ParametricType> const& pVector, storm::storage::BitVector const& selectedRows, storm::storage::BitVector const& selectedColumns,  bool generateRowLabels = false, bool useMonotonicity = false);
             
             void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters);
-            
+
+            /*!
+             * Specifies the region for the parameterlifter, the Bitvector works as a fixed (partial) scheduler, this might not give sound results!
+             * @param region the region
+             * @param dirForParameters the optimization direction 
+             * @param selectedRows a Bitvector that specifies which rows of the matrix and the vector are considered.
+             */
+            void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters, storm::storage::BitVector const& selectedRows);
+
+            /*!
+             * Specifies the region for the parameterlifter, the reachability order is used to see if there is local monotonicity, such that a fixed (partial) scheduler can be used
+             * @param region the region
+             * @param dirForParameters the optimization direction
+             * @param reachabilityOrder a (possibly insufficient) reachability order, used for local monotonicity
+             */
+            void specifyRegion(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForParameters, std::shared_ptr<storm::analysis::Order> reachabilityOrder, std::shared_ptr<storm::analysis::LocalMonotonicityResult<VariableType>> localMonotonicityResult);
+
             // Returns the resulting matrix. Should only be called AFTER specifying a region
             storm::storage::SparseMatrix<ConstantType> const& getMatrix() const;
             
             // Returns the resulting vector. Should only be called AFTER specifying a region
             std::vector<ConstantType> const& getVector() const;
-            
+
+            std::vector<std::set<VariableType>> const& getOccurringVariablesAtState() const;
+
+            std::map<VariableType, std::set<uint_fast64_t>> getOccuringStatesAtVariable() const;
+
+            uint_fast64_t getRowGroupIndex(uint_fast64_t originalState) const;
+            uint_fast64_t getOriginalStateNumber(uint_fast64_t newState) const;
+            uint_fast64_t getRowGroupSize(uint_fast64_t originalState) const;
+            uint_fast64_t getRowGroupCount() const;
+
             /*
              * During initialization, the actual regions are not known. Hence, we consider abstract valuations,
              * where it is only known whether a parameter will be set to either the lower/upper bound of the region or whether this is unspecified
@@ -68,7 +97,8 @@ namespace storm {
                 std::set<VariableType> const& getLowerParameters() const;
                 std::set<VariableType> const& getUpperParameters() const;
                 std::set<VariableType> const& getUnspecifiedParameters() const;
-                
+                uint_fast64_t getOriginalState(uint_fast64_t newStateNumber) const;
+
                 /*!
                  * Returns the concrete valuation(s) (w.r.t. the provided region) represented by this abstract valuation.
                  * Note that an abstract valuation represents 2^(#unspecified parameters) many concrete valuations.
@@ -82,7 +112,6 @@ namespace storm {
             // Returns for each row the abstract valuation for this row
             // Note: the returned vector might be empty if row label generaion was disabled initially
             std::vector<AbstractValuation> const& getRowLabels() const;
-
             
         private:
             /*
@@ -97,18 +126,19 @@ namespace storm {
             class FunctionValuationCollector {
             public:
                 FunctionValuationCollector() = default;
-                
+
                 /*!
                  * Adds the provided function and valuation.
                  * Returns a reference to a placeholder in which the evaluation result will be written upon calling evaluateCollectedFunctions)
                  */
                 ConstantType& add(ParametricType const& function, AbstractValuation const& valuation);
-                
+
                 void evaluateCollectedFunctions(storm::storage::ParameterRegion<ParametricType> const& region, storm::solver::OptimizationDirection const& dirForUnspecifiedParameters);
                 
             private:
                 // Stores a function and a valuation. The valuation is stored as an index of the collectedValuations-vector.
                 typedef std::pair<ParametricType, AbstractValuation> FunctionValuation;
+
                 class FuncValHash{
                     public:
                         std::size_t operator()(FunctionValuation const& fv) const {
@@ -118,7 +148,7 @@ namespace storm {
                             return seed;
                         }
                 };
-                
+
                 // Stores the collected functions with the valuations together with a placeholder for the result.
                 std::unordered_map<FunctionValuation, ConstantType, FuncValHash> collectedFunctions;
             };
@@ -130,12 +160,18 @@ namespace storm {
             
             std::vector<AbstractValuation> rowLabels;
 
+            std::vector<uint_fast64_t> oldToNewColumnIndexMapping; // Mapping from old to new columnIndex used for monotonicity
+            std::vector<uint_fast64_t> rowGroupToStateNumber; // Mapping from new to old columnIndex used for monotonicity
+
             storm::storage::SparseMatrix<ConstantType> matrix; //The resulting matrix;
             std::vector<std::pair<typename storm::storage::SparseMatrix<ConstantType>::iterator, ConstantType&>> matrixAssignment; // Connection of matrix entries with placeholders
-            
+
             std::vector<ConstantType> vector; //The resulting vector
             std::vector<std::pair<typename std::vector<ConstantType>::iterator, ConstantType&>> vectorAssignment; // Connection of vector entries with placeholders
-                
+
+            // Used for monotonicity in sparsedtmcparameterlifter
+            std::vector<std::set<VariableType>> occurringVariablesAtState;
+            std::map<VariableType, std::set<uint_fast64_t>> occuringStatesAtVariable;
         };
 
     }
diff --git a/src/storm/settings/modules/GeneralSettings.cpp b/src/storm/settings/modules/GeneralSettings.cpp
index 0392b61a3..ccdce224c 100644
--- a/src/storm/settings/modules/GeneralSettings.cpp
+++ b/src/storm/settings/modules/GeneralSettings.cpp
@@ -73,7 +73,14 @@ namespace storm {
             uint64_t GeneralSettings::getShowProgressDelay() const {
                 return this->getOption(showProgressOptionName).getArgumentByName("delay").getValueAsUnsignedInteger();
             }
-            
+
+            bool GeneralSettings::isPrecisionSet() const {
+                return this->getOption(precisionOptionName).getHasOptionBeenSet();
+            }
+
+            void GeneralSettings::setPrecision(std::string precision) {
+                this->getOption(precisionOptionName).getArgumentByName("value").setFromStringValue(precision);
+            }
             double GeneralSettings::getPrecision() const {
                 return this->getOption(precisionOptionName).getArgumentByName("value").getValueAsDouble();
             }
diff --git a/src/storm/settings/modules/GeneralSettings.h b/src/storm/settings/modules/GeneralSettings.h
index 42f6a4dfe..45871e235 100644
--- a/src/storm/settings/modules/GeneralSettings.h
+++ b/src/storm/settings/modules/GeneralSettings.h
@@ -98,7 +98,11 @@ namespace storm {
                  * @return True iff the option was set.
                  */
                 bool isParametricSet() const;
-                
+
+                bool isPrecisionSet() const;
+
+                void setPrecision(std::string precision);
+
                 /*!
                  * Retrieves whether the option enabling exact model checking is set and we should use infinite precision rationals.
                  *
diff --git a/src/storm/settings/modules/IOSettings.cpp b/src/storm/settings/modules/IOSettings.cpp
index 40c1a006c..f0af3f4ad 100644
--- a/src/storm/settings/modules/IOSettings.cpp
+++ b/src/storm/settings/modules/IOSettings.cpp
@@ -26,7 +26,6 @@ namespace storm {
             const std::string IOSettings::exportCdfOptionShortName = "cdf";
             const std::string IOSettings::exportSchedulerOptionName = "exportscheduler";
             const std::string IOSettings::exportCheckResultOptionName = "exportresult";
-            const std::string IOSettings::exportMonotonicityName = "exportmonotonicity";
             const std::string IOSettings::explicitOptionName = "explicit";
             const std::string IOSettings::explicitOptionShortName = "exp";
             const std::string IOSettings::explicitDrnOptionName = "explicit-drn";
@@ -66,7 +65,6 @@ namespace storm {
                 this->addOption(storm::settings::OptionBuilder(moduleName, exportCdfOptionName, false, "Exports the cumulative density function for reward bounded properties into a .csv file.").setIsAdvanced().setShortName(exportCdfOptionShortName).addArgument(storm::settings::ArgumentBuilder::createStringArgument("directory", "A path to an existing directory where the cdf files will be stored.").build()).build());
                 this->addOption(storm::settings::OptionBuilder(moduleName, exportSchedulerOptionName, false, "Exports the choices of an optimal scheduler to the given file (if supported by engine).").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file. Use file extension '.json' to export in json.").build()).build());
                 this->addOption(storm::settings::OptionBuilder(moduleName, exportCheckResultOptionName, false, "Exports the result to a given file (if supported by engine). The export will be in json.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
-                this->addOption(storm::settings::OptionBuilder(moduleName, exportMonotonicityName, false, "Exports the result of monotonicity checking to the given file.").setIsAdvanced().addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "The output file.").build()).build());
                 this->addOption(storm::settings::OptionBuilder(moduleName, exportExplicitOptionName, "", "If given, the loaded model will be written to the specified file in the drn format.")
                                 .addArgument(storm::settings::ArgumentBuilder::createStringArgument("filename", "the name of the file to which the model is to be writen.").build()).build());
                 this->addOption(storm::settings::OptionBuilder(moduleName,  preventDRNPlaceholderOptionName, true, "If given, the exported DRN contains no placeholders").setIsAdvanced().build());
@@ -179,23 +177,15 @@ namespace storm {
             std::string IOSettings::getExportSchedulerFilename() const {
                 return this->getOption(exportSchedulerOptionName).getArgumentByName("filename").getValueAsString();
             }
-            
+
             bool IOSettings::isExportCheckResultSet() const {
                 return this->getOption(exportCheckResultOptionName).getHasOptionBeenSet();
             }
-            
+
             std::string IOSettings::getExportCheckResultFilename() const {
                 return this->getOption(exportCheckResultOptionName).getArgumentByName("filename").getValueAsString();
             }
 
-            bool IOSettings::isExportMonotonicitySet() const {
-                return this->getOption(exportMonotonicityName).getHasOptionBeenSet();
-            }
-
-            std::string IOSettings::getExportMonotonicityFilename() const {
-                return this->getOption(exportMonotonicityName).getArgumentByName("filename").getValueAsString();
-            }
-            
             bool IOSettings::isExplicitSet() const {
                 return this->getOption(explicitOptionName).getHasOptionBeenSet();
             }
diff --git a/src/storm/settings/modules/IOSettings.h b/src/storm/settings/modules/IOSettings.h
index 34875441f..3be8c646d 100644
--- a/src/storm/settings/modules/IOSettings.h
+++ b/src/storm/settings/modules/IOSettings.h
@@ -105,27 +105,17 @@ namespace storm {
                  * Retrieves a filename to which an optimal scheduler will be exported.
                  */
                  std::string getExportSchedulerFilename() const;
-                
+
                 /*!
                  * Retrieves whether the check result should be exported.
                  */
                 bool isExportCheckResultSet() const;
-                
+
                 /*!
                  * Retrieves a filename to which the check result should be exported.
                  */
                  std::string getExportCheckResultFilename() const;
 
-                /*!
-                * Retrieves whether an optimal scheduler is to be exported
-                */
-                bool isExportMonotonicitySet() const;
-
-                /*!
-                 * Retrieves a filename to which an optimal scheduler will be exported.
-                 */
-                std::string getExportMonotonicityFilename() const;
-                
                 /*!
                  * Retrieves whether the explicit option was set.
                  *
@@ -376,7 +366,6 @@ namespace storm {
                 static const std::string exportCdfOptionShortName;
                 static const std::string exportSchedulerOptionName;
                 static const std::string exportCheckResultOptionName;
-                static const std::string exportMonotonicityName;
                 static const std::string explicitOptionName;
                 static const std::string explicitOptionShortName;
                 static const std::string explicitDrnOptionName;
diff --git a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
index 5e71c0503..5d2ff03d6 100644
--- a/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
+++ b/src/storm/solver/IterativeMinMaxLinearEquationSolver.cpp
@@ -97,12 +97,19 @@ namespace storm {
             
             // Resolve the nondeterminism according to the given scheduler.
             bool convertToEquationSystem = this->linearEquationSolverFactory->getEquationProblemFormat(env) == LinearEquationSolverProblemFormat::EquationSystem;
-            storm::storage::SparseMatrix<ValueType> submatrix = this->A->selectRowsFromRowGroups(scheduler, convertToEquationSystem);
+            storm::storage::SparseMatrix<ValueType> submatrix;
+            if (this->fixedStates) {
+                for (auto state : this->fixedStates.get()) {
+                    assert (this->A->getRowGroupSize(state) == 1);
+                }
+            }
+
+            submatrix = this->A->selectRowsFromRowGroups(scheduler, convertToEquationSystem);
             if (convertToEquationSystem) {
                 submatrix.convertToEquationSystem();
             }
             storm::utility::vector::selectVectorValues<ValueType>(subB, scheduler, this->A->getRowGroupIndices(), originalB);
-            
+
             // Check whether the linear equation solver is already initialized
             if (!linearEquationSolver) {
                 // Initialize the equation solver
@@ -165,29 +172,36 @@ namespace storm {
                 
                 // Go through the multiplication result and see whether we can improve any of the choices.
                 bool schedulerImproved = false;
+                // Group staat voor de states?
                 for (uint_fast64_t group = 0; group < this->A->getRowGroupCount(); ++group) {
                     uint_fast64_t currentChoice = scheduler[group];
-                    for (uint_fast64_t choice = this->A->getRowGroupIndices()[group]; choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
-                        // If the choice is the currently selected one, we can skip it.
-                        if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
-                            continue;
-                        }
-                        
-                        // Create the value of the choice.
-                        ValueType choiceValue = storm::utility::zero<ValueType>();
-                        for (auto const& entry : this->A->getRow(choice)) {
-                            choiceValue += entry.getValue() * x[entry.getColumn()];
-                        }
-                        choiceValue += b[choice];
-                        
-                        // If the value is strictly better than the solution of the inner system, we need to improve the scheduler.
-                        // TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are equal).
-                        // only changing the scheduler if the values are not equal (modulo precision) would make this unsound.
-                        if (valueImproved(dir, x[group], choiceValue)) {
-                            schedulerImproved = true;
-                            scheduler[group] = choice - this->A->getRowGroupIndices()[group];
-                            x[group] = std::move(choiceValue);
+                    // TODO: remove, as this should already be fixed by implementation to determine matrix/vector
+                    if (!this->fixedStates || (this->fixedStates && !(this->fixedStates.get()[group]))) {
+                        for (uint_fast64_t choice = this->A->getRowGroupIndices()[group];
+                             choice < this->A->getRowGroupIndices()[group + 1]; ++choice) {
+                            // If the choice is the currently selected one, we can skip it.
+                            if (choice - this->A->getRowGroupIndices()[group] == currentChoice) {
+                                continue;
+                            }
+
+                            // Create the value of the choice.
+                            ValueType choiceValue = storm::utility::zero<ValueType>();
+                            for (auto const &entry : this->A->getRow(choice)) {
+                                choiceValue += entry.getValue() * x[entry.getColumn()];
+                            }
+                            choiceValue += b[choice];
+
+                            // If the value is strictly better than the solution of the inner system, we need to improve the scheduler.
+                            // TODO: If the underlying solver is not precise, this might run forever (i.e. when a state has two choices where the (exact) values are equal).
+                            // only changing the scheduler if the values are not equal (modulo precision) would make this unsound.
+                            if (valueImproved(dir, x[group], choiceValue)) {
+                                schedulerImproved = true;
+                                scheduler[group] = choice - this->A->getRowGroupIndices()[group];
+                                x[group] = std::move(choiceValue);
+                            }
                         }
+                    } else {
+                        STORM_LOG_INFO("Ignoring state" << group << " as this state is locally monotone");
                     }
                 }
                 
@@ -203,7 +217,8 @@ namespace storm {
                 // Potentially show progress.
                 this->showProgressIterative(iterations);
             } while (status == SolverStatus::InProgress);
-            
+
+            STORM_LOG_INFO("Number of iterations: " << iterations);
             this->reportStatus(status, iterations);
             
             // If requested, we store the scheduler for retrieval.
diff --git a/src/storm/solver/MinMaxLinearEquationSolver.cpp b/src/storm/solver/MinMaxLinearEquationSolver.cpp
index c36e291c2..4366fa0db 100644
--- a/src/storm/solver/MinMaxLinearEquationSolver.cpp
+++ b/src/storm/solver/MinMaxLinearEquationSolver.cpp
@@ -128,6 +128,7 @@ namespace storm {
                 
         template<typename ValueType>
         void MinMaxLinearEquationSolver<ValueType>::setInitialScheduler(std::vector<uint_fast64_t>&& choices) {
+            assert (!this->fixedStates || this->fixedStates.get().size() == choices.size());
             initialScheduler = std::move(choices);
         }
         
@@ -155,7 +156,21 @@ namespace storm {
         bool MinMaxLinearEquationSolver<ValueType>::isRequirementsCheckedSet() const {
             return requirementsChecked;
         }
-        
+
+        template<class ValueType>
+        void MinMaxLinearEquationSolver<ValueType>::setFixedStates(storm::storage::BitVector&& states) {
+            this->fixedStates = std::move(states);
+            assert (this->fixedStates);
+        }
+
+        template<class ValueType>
+        void MinMaxLinearEquationSolver<ValueType>::updateScheduler() {
+            assert (this->initialScheduler && this->fixedStates);
+            for (auto state : this->fixedStates.get()) {
+                this->initialScheduler.get()[state] = 0;
+            }
+        }
+
         template<typename ValueType>
         MinMaxLinearEquationSolverFactory<ValueType>::MinMaxLinearEquationSolverFactory() : requirementsChecked(false) {
             // Intentionally left empty
diff --git a/src/storm/solver/MinMaxLinearEquationSolver.h b/src/storm/solver/MinMaxLinearEquationSolver.h
index 5ab51dc95..836570ffc 100644
--- a/src/storm/solver/MinMaxLinearEquationSolver.h
+++ b/src/storm/solver/MinMaxLinearEquationSolver.h
@@ -69,7 +69,10 @@ namespace storm {
              * Unsets the optimization direction to use for calls to methods that do not explicitly provide one.
              */
             void unsetOptimizationDirection();
-            
+
+            void setFixedStates(storm::storage::BitVector&& states);
+            void updateScheduler();
+
             /*!
              * Sets whether the solution to the min max equation system is known to be unique.
              */
@@ -182,7 +185,9 @@ namespace storm {
             
             // A scheduler that can be used by solvers that require a valid initial scheduler.
             boost::optional<std::vector<uint_fast64_t>> initialScheduler;
-            
+
+            boost::optional<storm::storage::BitVector> fixedStates;
+
         private:
             /// Whether the solver can assume that the min-max equation system has a unique solution
             bool uniqueSolution;
diff --git a/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp b/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
index 6cf09bd45..e186b9dd8 100644
--- a/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
+++ b/src/storm/solver/TopologicalMinMaxLinearEquationSolver.cpp
@@ -69,8 +69,8 @@ namespace storm {
             }
             
             bool returnValue = true;
-            if (this->sortedSccDecomposition->size() == 1) {
-                // Handle the case where there is just one large SCC
+            if (this->sortedSccDecomposition->size() == 1 && (!this->fixedStates || this->fixedStates.get().empty())) {
+                // Handle the case where there is just one large SCC, as there are no fixed states, we solve it like this
                 returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, dir, x, b);
             } else {
                 // Solve each SCC individually
@@ -89,15 +89,24 @@ namespace storm {
                 progress.startNewMeasurement(0);
                 for (auto const& scc : *this->sortedSccDecomposition) {
                     if (scc.size() == 1) {
+                        // TODO: directly use localMonRes on this
                         returnValue = solveTrivialScc(*scc.begin(), dir, x, b) && returnValue;
                     } else {
                         STORM_LOG_TRACE("Solving SCC of size " << scc.size() << ".");
                         sccRowGroupsAsBitVector.clear();
                         sccRowsAsBitVector.clear();
-                        for (auto const& group : scc) {
+                        for (auto const& group : scc) { // Group refers to state
+                            bool allIgnored = true;
                             sccRowGroupsAsBitVector.set(group, true);
-                            for (uint64_t row = this->A->getRowGroupIndices()[group]; row < this->A->getRowGroupIndices()[group + 1]; ++row) {
+
+                            if (!this->fixedStates || !this->fixedStates.get()[group]) {
+                                for (uint64_t row = this->A->getRowGroupIndices()[group]; row < this->A->getRowGroupIndices()[group + 1]; ++row) {
+                                    sccRowsAsBitVector.set(row, true);
+                                }
+                            } else {
+                                auto row = this->A->getRowGroupIndices()[group]+this->getInitialScheduler()[group];
                                 sccRowsAsBitVector.set(row, true);
+                                STORM_LOG_INFO("Fixing state " << group << " to option " << this->getInitialScheduler()[group] << " because of local monotonicity.");
                             }
                         }
                         returnValue = solveScc(sccSolverEnvironment, dir, sccRowGroupsAsBitVector, sccRowsAsBitVector, x, b) && returnValue;
@@ -141,12 +150,13 @@ namespace storm {
             ValueType& xi = globalX[sccState];
             bool firstRow = true;
             uint64_t bestRow;
-            
-            for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
+            if (this->fixedStates && this->fixedStates.get()[sccState]) {
+                assert (this->hasInitialScheduler());
+                uint64_t row = this->A->getRowGroupIndices()[sccState] + this->initialScheduler.get()[sccState];
                 ValueType rowValue = globalB[row];
                 bool hasDiagonalEntry = false;
                 ValueType denominator;
-                for (auto const& entry : this->A->getRow(row)) {
+                for (auto const &entry : this->A->getRow(row)) {
                     if (entry.getColumn() == sccState) {
                         hasDiagonalEntry = true;
                         denominator = storm::utility::one<ValueType>() - entry.getValue();
@@ -155,38 +165,68 @@ namespace storm {
                     }
                 }
                 if (hasDiagonalEntry) {
-                    STORM_LOG_WARN_COND_DEBUG(storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) || storm::utility::isZero(denominator), "State " << sccState << " has a selfloop with probability '1-(" << denominator << ")'. This could be an indication for numerical issues.");
-                    if (storm::utility::isZero(denominator)) {
-                        // In this case we have a selfloop on this state. This can never an optimal choice:
-                        // When minimizing, we are looking for the largest fixpoint (which will never be attained by this action)
-                        // When maximizing, this choice reflects probability zero (non-optimal) or reward infinity (should already be handled during preprocessing).
-                        continue;
-                    } else {
-                        rowValue /= denominator;
-                    }
+                    STORM_LOG_WARN_COND_DEBUG( storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) ||
+                            storm::utility::isZero(denominator), "State " << sccState << " has a selfloop with probability '1-(" << denominator << ")'. This could be an indication for numerical issues.");
+                    assert (!storm::utility::isZero(denominator));
+                    rowValue /= denominator;
                 }
-                if (firstRow) {
+                if (minimize(dir)) {
                     xi = std::move(rowValue);
-                    bestRow = row;
-                    firstRow = false;
                 } else {
-                    if (minimize(dir)) {
-                        if (rowValue < xi) {
-                            xi = std::move(rowValue);
-                            bestRow = row;
+                    xi = std::move(rowValue);
+                }
+                STORM_LOG_INFO("Ignoring state" << sccState << " as the scheduler is fixed by monotonicity, current probability for this state is: " << this->schedulerChoices.get()[sccState]);
+            } else {
+                for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
+                    ValueType rowValue = globalB[row];
+                    bool hasDiagonalEntry = false;
+                    ValueType denominator;
+                    for (auto const &entry : this->A->getRow(row)) {
+                        if (entry.getColumn() == sccState) {
+                            hasDiagonalEntry = true;
+                            denominator = storm::utility::one<ValueType>() - entry.getValue();
+                        } else {
+                            rowValue += entry.getValue() * globalX[entry.getColumn()];
+                        }
+                    }
+                    if (hasDiagonalEntry) {
+                        STORM_LOG_WARN_COND_DEBUG(
+                                storm::NumberTraits<ValueType>::IsExact || !storm::utility::isAlmostZero(denominator) ||
+                                storm::utility::isZero(denominator),
+                                "State " << sccState << " has a selfloop with probability '1-(" << denominator
+                                         << ")'. This could be an indication for numerical issues.");
+                        if (storm::utility::isZero(denominator)) {
+                            // In this case we have a selfloop on this state. This can never an optimal choice:
+                            // When minimizing, we are looking for the largest fixpoint (which will never be attained by this action)
+                            // When maximizing, this choice reflects probability zero (non-optimal) or reward infinity (should already be handled during preprocessing).
+                            continue;
+                        } else {
+                            rowValue /= denominator;
                         }
+                    }
+                    if (firstRow) {
+                        xi = std::move(rowValue);
+                        bestRow = row;
+                        firstRow = false;
                     } else {
-                        if (rowValue > xi) {
-                            xi = std::move(rowValue);
-                            bestRow = row;
+                        if (minimize(dir)) {
+                            if (rowValue < xi) {
+                                xi = std::move(rowValue);
+                                bestRow = row;
+                            }
+                        } else {
+                            if (rowValue > xi) {
+                                xi = std::move(rowValue);
+                                bestRow = row;
+                            }
                         }
                     }
                 }
+                if (this->isTrackSchedulerSet()) {
+                    this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
+                }
+                STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException, "Empty row group in MinMax equation system.");
             }
-            if (this->isTrackSchedulerSet()) {
-                this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
-            }
-            STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException, "Empty row group in MinMax equation system.");
             //std::cout << "Solved trivial scc " << sccState << " with result " << globalX[sccState] << std::endl;
             return true;
         }
@@ -231,19 +271,38 @@ namespace storm {
         
         template<typename ValueType>
         bool TopologicalMinMaxLinearEquationSolver<ValueType>::solveScc(storm::Environment const& sccSolverEnvironment, OptimizationDirection dir, storm::storage::BitVector const& sccRowGroups, storm::storage::BitVector const& sccRows, std::vector<ValueType>& globalX, std::vector<ValueType> const& globalB) const {
-            
+
             // Set up the SCC solver
             if (!this->sccSolver) {
                 this->sccSolver = GeneralMinMaxLinearEquationSolverFactory<ValueType>().create(sccSolverEnvironment);
                 this->sccSolver->setCachingEnabled(true);
             }
+            if (this->fixedStates) {
+                // convert fixed states to only fixed states of sccs
+                storm::storage::BitVector fixedStatesSCC(sccRowGroups.getNumberOfSetBits());
+                auto j = 0;
+                for (auto i : sccRowGroups) {
+                    fixedStatesSCC.set(j, this->fixedStates.get()[i]);
+                    j++;
+                }
+                assert (j = sccRowGroups.getNumberOfSetBits());
+                this->sccSolver->setFixedStates(std::move(fixedStatesSCC));
+            }
             this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
             this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
             this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
             
             // SCC Matrix
-            storm::storage::SparseMatrix<ValueType> sccA = this->A->getSubmatrix(true, sccRowGroups, sccRowGroups);
-            //std::cout << "Matrix is " << sccA << std::endl;
+            storm::storage::SparseMatrix<ValueType> sccA;
+            if (this->fixedStates) {
+                sccA = this->A->getSubmatrix(false, sccRows, sccRowGroups);
+            } else {
+                sccA = this->A->getSubmatrix(true, sccRowGroups, sccRowGroups);
+
+            }
+
+//            std::cout << "Matrix is " << sccA << std::endl;
+
             this->sccSolver->setMatrix(std::move(sccA));
             
             // x Vector
@@ -266,6 +325,9 @@ namespace storm {
             if (this->hasInitialScheduler()) {
                 auto sccInitChoices = storm::utility::vector::filterVector(this->getInitialScheduler(), sccRowGroups);
                 this->sccSolver->setInitialScheduler(std::move(sccInitChoices));
+                if (this->fixedStates) {
+                    this->sccSolver->updateScheduler();
+                }
             }
             
             // lower/upper bounds
diff --git a/src/storm/utility/graph.cpp b/src/storm/utility/graph.cpp
index dc8acb4f3..ddb1a4eca 100644
--- a/src/storm/utility/graph.cpp
+++ b/src/storm/utility/graph.cpp
@@ -1665,6 +1665,40 @@ namespace storm {
                 }
             }
 
+            template <typename T>
+            std::vector<uint_fast64_t> getBFSSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint_fast64_t> const& firstStates) {
+                storm::storage::BitVector seenStates(matrix.getRowGroupCount());
+
+                std::vector<uint_fast64_t> stateQueue;
+                stateQueue.reserve(matrix.getRowGroupCount());
+                std::vector<uint_fast64_t> result;
+                result.reserve(matrix.getRowGroupCount());
+
+                storm::storage::sparse::state_type currentPosition = 0;
+                auto count = matrix.getRowGroupCount() - 1;
+                for (auto const& state : firstStates) {
+                    stateQueue.push_back(state);
+                    result[count] = state;
+                    count--;
+                }
+
+                // Perform a BFS.
+                while (!stateQueue.empty()) {
+                    auto state = stateQueue.back();
+                    stateQueue.pop_back();
+                    seenStates.set(state);
+                    for (auto const& successorEntry : matrix.getRowGroup(state)) {
+                        auto succ = successorEntry.geColumn();
+                        if (!seenStates[succ]) {
+                            result[count] = succ;
+                            count--;
+                            stateQueue.insert(stateQueue.begin(), succ);
+                        }
+                    }
+                }
+                return result;
+            }
+
             template <typename T>
             std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint64_t> const& firstStates) {
                 if (matrix.getRowCount() != matrix.getColumnCount()) {
@@ -1673,7 +1707,7 @@ namespace storm {
                 }
                 
                 uint_fast64_t numberOfStates = matrix.getRowCount();
-                
+
                 // Prepare the result. This relies on the matrix being square.
                 std::vector<uint_fast64_t> topologicalSort;
                 topologicalSort.reserve(numberOfStates);
@@ -1696,7 +1730,6 @@ namespace storm {
                 return topologicalSort;
             }
 
-
             template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix<double> const& transitionMatrix, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional<storm::storage::BitVector> const& choiceFilter);
             
             template storm::storage::BitVector getBsccCover(storm::storage::SparseMatrix<double> const& transitionMatrix);
@@ -1774,7 +1807,7 @@ namespace storm {
             template ExplicitGameProb01Result performProb1(storm::storage::SparseMatrix<double> const& transitionMatrix, std::vector<uint64_t> const& player1RowGrouping, storm::storage::SparseMatrix<double> const& player1BackwardTransitions, std::vector<uint64_t> const& player2BackwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, storm::OptimizationDirection const& player1Direction, storm::OptimizationDirection const& player2Direction, storm::abstraction::ExplicitGameStrategyPair* strategyPair, boost::optional<storm::storage::BitVector> const& player1Candidates);
             
             template std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<double> const& matrix,  std::vector<uint64_t> const& firstStates) ;
-            
+
             // Instantiations for storm::RationalNumber.
 #ifdef STORM_HAVE_CARL
             template storm::storage::BitVector getReachableStates(storm::storage::SparseMatrix<storm::RationalNumber> const& transitionMatrix, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& constraintStates, storm::storage::BitVector const& targetStates, bool useStepBound, uint_fast64_t maximalSteps, boost::optional<storm::storage::BitVector> const& choiceFilter);
@@ -1888,7 +1921,6 @@ namespace storm {
             
             
             template std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<storm::RationalFunction> const& matrix,  std::vector<uint64_t> const& firstStates);
-            
 #endif
             
             // Instantiations for CUDD.
diff --git a/src/storm/utility/graph.h b/src/storm/utility/graph.h
index fdb9a2538..db8b77a12 100644
--- a/src/storm/utility/graph.h
+++ b/src/storm/utility/graph.h
@@ -3,6 +3,7 @@
 
 #include <set>
 #include <limits>
+#include <storm/storage/StronglyConnectedComponent.h>
 
 #include "storm/utility/OsDetection.h"
 
@@ -706,6 +707,9 @@ namespace storm {
             template <typename T>
             std::vector<uint_fast64_t> getTopologicalSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint64_t> const& firstStates = {}) ;
 
+            template <typename T>
+            std::vector<uint_fast64_t> getBFSSort(storm::storage::SparseMatrix<T> const& matrix, std::vector<uint_fast64_t> const& firstStates) ;
+
         } // namespace graph
     } // namespace utility
 } // namespace storm
diff --git a/src/test/storm-pars/CMakeLists.txt b/src/test/storm-pars/CMakeLists.txt
index ba3994449..4850893c7 100644
--- a/src/test/storm-pars/CMakeLists.txt
+++ b/src/test/storm-pars/CMakeLists.txt
@@ -12,7 +12,7 @@ include_directories(${GTEST_INCLUDE_DIR})
 foreach (testsuite analysis modelchecker utility)
 
 	  file(GLOB_RECURSE TEST_${testsuite}_FILES ${STORM_TESTS_BASE_PATH}/${testsuite}/*.h ${STORM_TESTS_BASE_PATH}/${testsuite}/*.cpp)
-      add_executable (test-pars-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp)
+      add_executable (test-pars-${testsuite} ${TEST_${testsuite}_FILES} ${STORM_TESTS_BASE_PATH}/storm-test.cpp analysis/MonotonicityCheckerTest.cpp)
 	  target_link_libraries(test-pars-${testsuite} storm-pars storm-parsers)
 	  target_link_libraries(test-pars-${testsuite} ${STORM_TEST_LINK_LIBRARIES})
 
diff --git a/src/test/storm-pars/analysis/AssumptionCheckerTest.cpp b/src/test/storm-pars/analysis/AssumptionCheckerTest.cpp
index f319587c3..8c549bd00 100644
--- a/src/test/storm-pars/analysis/AssumptionCheckerTest.cpp
+++ b/src/test/storm-pars/analysis/AssumptionCheckerTest.cpp
@@ -1,29 +1,22 @@
-#include "test/storm_gtest.h"
+#include <storm/storage/StronglyConnectedComponentDecomposition.h>
 #include "storm-config.h"
-#include "test/storm_gtest.h"
 
-#include "storm-parsers/parser/FormulaParser.h"
-#include "storm/logic/Formulas.h"
-#include "storm/models/sparse/StandardRewardModel.h"
-#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
-#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+#include "storm-pars/api/analysis.h"
+#include "storm-pars/api/region.h"
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+
+#include "storm-parsers/api/storm-parsers.h"
 #include "storm-parsers/parser/AutoParser.h"
 #include "storm-parsers/parser/PrismParser.h"
-#include "storm/storage/expressions/ExpressionManager.h"
-#include "storm/api/builder.h"
 
-#include "storm-pars/analysis/AssumptionChecker.h"
-#include "storm-pars/analysis/Order.h"
-#include "storm/storage/expressions/BinaryRelationExpression.h"
-#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
-#include "storm-pars/storage/ParameterRegion.h"
-
-#include "storm-pars/api/storm-pars.h"
+#include "storm/api/builder.h"
 #include "storm/api/storm.h"
+#include "storm/logic/Formulas.h"
+#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
+#include "storm/storage/expressions/BinaryRelationExpression.h"
+#include "storm/storage/expressions/ExpressionManager.h"
 
-#include "storm-parsers/api/storm-parsers.h"
-
-#include "storm-pars/api/region.h"
+#include "test/storm_gtest.h"
 
 
 TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
@@ -43,35 +36,38 @@ TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
 
     dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
+    ASSERT_EQ(dtmc->getNumberOfStates(), 193);
+    ASSERT_EQ(dtmc->getNumberOfTransitions(), 383);
 
     // Create the region
-    storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
-    storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
     auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.00001, 0.00001 <= pL <= 0.99999", vars);
 
-    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
-
-    // Check on samples
+    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
     auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
     expressionManager->declareRationalVariable("7");
     expressionManager->declareRationalVariable("5");
+    storm::storage::BitVector above(193);
+    above.set(0);
+    storm::storage::BitVector below(193);
+    below.set(1);
 
-    auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
-            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
-                                                         expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
-                                                         expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
-                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto dummyOrder = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 193, decomposition, statesSorted));
+
+    auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(), expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(), expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(), storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
 
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
@@ -79,40 +75,31 @@ TEST(AssumptionCheckerTest, Brp_no_bisimulation) {
                                                          expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Equal));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
 
 
-    storm::storage::BitVector above(8);
-    above.set(0);
-    storm::storage::BitVector below(8);
-    below.set(1);
-    storm::storage::BitVector initialMiddle(8);
+    checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.validateAssumption(assumption, dummyOrder, region));
 
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder, region));
 
-    auto dummyOrder = new storm::analysis::Order(&above, &below, &initialMiddle, 8, &statesSorted);
-    // Validate assumption
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder));
-//    EXPECT_FALSE(checker.validated(assumption));
 
-    expressionManager->declareRationalVariable("6");
-    expressionManager->declareRationalVariable("8");
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
-                                                         expressionManager->getVariable("6").getExpression().getBaseExpressionPointer(),
-                                                         expressionManager->getVariable("8").getExpression().getBaseExpressionPointer(),
-                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    above = storm::storage::BitVector(13);
-    above.set(12);
-    below = storm::storage::BitVector(13);
-    below.set(9);
-    initialMiddle = storm::storage::BitVector(13);
-
-    dummyOrder = new storm::analysis::Order(&above, &below, &initialMiddle, 13, &statesSorted);
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder));
-//    EXPECT_EQ(checker.validated(assumption));
-//    EXPECT_FALSE(checker.valid(assumption));
+                                                         expressionManager->getVariable("7").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("5").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Equal));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, dummyOrder, region));
 }
 
 TEST(AssumptionCheckerTest, Simple1) {
@@ -131,44 +118,76 @@ TEST(AssumptionCheckerTest, Simple1) {
     model = simplifier.getSimplifiedModel();
     dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
+    ASSERT_EQ(dtmc->getNumberOfStates(), 5);
+    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
 
     // Create the region
     auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
 
-    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
+    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
 
     auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
     expressionManager->declareRationalVariable("1");
     expressionManager->declareRationalVariable("2");
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
 
-    // Checking on samples
+    storm::storage::BitVector above(5);
+    above.set(3);
+    storm::storage::BitVector below(5);
+    below.set(4);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
+
+    // Validating
     auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Equal));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+    region = storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.99", vars);
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Equal));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 }
 
-TEST(AssumptionCheckerTest, Simple2) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple2.pm";
+TEST(AssumptionCheckerTest, Casestudy1) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
     std::string formulaAsString = "P=? [F s=3]";
     std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
 
@@ -183,14 +202,14 @@ TEST(AssumptionCheckerTest, Simple2) {
     model = simplifier.getSimplifiedModel();
     dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
+    ASSERT_EQ(dtmc->getNumberOfStates(), 5);
+    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
 
     // Create the region
     auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
 
-    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
+    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
 
     auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
     expressionManager->declareRationalVariable("1");
@@ -200,28 +219,27 @@ TEST(AssumptionCheckerTest, Simple2) {
     above.set(3);
     storm::storage::BitVector below(5);
     below.set(4);
-    storm::storage::BitVector initialMiddle(5);
 
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
 
-    auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
-
-    // Checking on samples and validate
+    // Validating
     auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
@@ -229,12 +247,34 @@ TEST(AssumptionCheckerTest, Simple2) {
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Equal));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+    checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
+
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Equal));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 }
 
-TEST(AssumptionCheckerTest, Simple3) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple3.pm";
+TEST(AssumptionCheckerTest, Casestudy2) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
     std::string formulaAsString = "P=? [F s=4]";
     std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
 
@@ -256,7 +296,7 @@ TEST(AssumptionCheckerTest, Simple3) {
     auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.99999", vars);
 
-    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
+    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
 
     auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
     expressionManager->declareRationalVariable("1");
@@ -266,11 +306,12 @@ TEST(AssumptionCheckerTest, Simple3) {
     above.set(4);
     storm::storage::BitVector below(6);
     below.set(5);
-    storm::storage::BitVector initialMiddle(6);
 
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
-
-    auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 6, &statesSorted);
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 6, decomposition, statesSorted));
     order->add(3);
 
     // Checking on samples and validate
@@ -279,31 +320,25 @@ TEST(AssumptionCheckerTest, Simple3) {
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Equal));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 }
 
-TEST(AssumptionCheckerTest, Simple4) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple4.pm";
+TEST(AssumptionCheckerTest, Casestudy3) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
     std::string formulaAsString = "P=? [F s=3]";
     std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
 
@@ -318,14 +353,14 @@ TEST(AssumptionCheckerTest, Simple4) {
     model = simplifier.getSimplifiedModel();
     dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
+    ASSERT_EQ(dtmc->getNumberOfStates(), 5);
+    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8);
 
     // Create the region
     auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.4", vars);
 
-    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
+    auto checker = storm::analysis::AssumptionChecker<storm::RationalFunction, double>(dtmc->getTransitionMatrix());
 
     auto expressionManager = std::make_shared<storm::expressions::ExpressionManager>(storm::expressions::ExpressionManager());
     expressionManager->declareRationalVariable("1");
@@ -336,38 +371,53 @@ TEST(AssumptionCheckerTest, Simple4) {
     above.set(3);
     storm::storage::BitVector below(5);
     below.set(4);
-    storm::storage::BitVector initialMiddle(5);
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
-
-    auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
 
     auto assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Greater));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 
     assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
             storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
                                                          expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
                                                          expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
                                                          storm::expressions::BinaryRelationExpression::RelationType::Equal));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.checkOnSamples(assumption));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order));
-    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumptionSMTSolver(assumption, order));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 
+    checker.initializeCheckingOnSamples(formulas[0], dtmc, region, 3);
 
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, checker.validateAssumption(assumption, order, region));
 
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Greater));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
+
+    assumption = std::make_shared<storm::expressions::BinaryRelationExpression>(
+            storm::expressions::BinaryRelationExpression(*expressionManager, expressionManager->getBooleanType(),
+                                                         expressionManager->getVariable("1").getExpression().getBaseExpressionPointer(),
+                                                         expressionManager->getVariable("2").getExpression().getBaseExpressionPointer(),
+                                                         storm::expressions::BinaryRelationExpression::RelationType::Equal));
+    EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, checker.validateAssumption(assumption, order, region));
 }
diff --git a/src/test/storm-pars/analysis/AssumptionMakerTest.cpp b/src/test/storm-pars/analysis/AssumptionMakerTest.cpp
index 510d5757f..ecc764fca 100644
--- a/src/test/storm-pars/analysis/AssumptionMakerTest.cpp
+++ b/src/test/storm-pars/analysis/AssumptionMakerTest.cpp
@@ -1,32 +1,19 @@
-//
-// Created by Jip Spel on 20.09.18.
-//
-
-// TODO: cleanup includes
-#include "test/storm_gtest.h"
+#include <storm/storage/StronglyConnectedComponentDecomposition.h>
 #include "storm-config.h"
 #include "test/storm_gtest.h"
 
-#include "storm-parsers/parser/FormulaParser.h"
-#include "storm/logic/Formulas.h"
-#include "storm/models/sparse/StandardRewardModel.h"
-#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
-#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+#include "storm-pars/api/analysis.h"
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+
+#include "storm-parsers/api/storm-parsers.h"
 #include "storm-parsers/parser/AutoParser.h"
 #include "storm-parsers/parser/PrismParser.h"
-#include "storm/storage/expressions/ExpressionManager.h"
-#include "storm/api/builder.h"
-
-#include "storm-pars/analysis/Order.h"
-#include "storm-pars/analysis/AssumptionMaker.h"
-#include "storm-pars/analysis/AssumptionChecker.h"
-#include "storm-pars/analysis/OrderExtender.h"
-#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
 
-#include "storm-pars/api/storm-pars.h"
+#include "storm/api/builder.h"
 #include "storm/api/storm.h"
-
-#include "storm-parsers/api/storm-parsers.h"
+#include "storm/logic/Formulas.h"
+#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
+#include "storm/models/sparse/StandardRewardModel.h"
 
 TEST(AssumptionMakerTest, Brp_without_bisimulation) {
     std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
@@ -38,280 +25,138 @@ TEST(AssumptionMakerTest, Brp_without_bisimulation) {
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
     std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
-    dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    model = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
     // Create the region
-    auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
+    ASSERT_EQ(model->getNumberOfStates(), 193);
+    ASSERT_EQ(model->getNumberOfTransitions(), 383);
 
-    auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
-    auto criticalTuple = extender->toOrder(formulas);
-    ASSERT_EQ(183ul, std::get<1>(criticalTuple));
-    ASSERT_EQ(186ul, std::get<2>(criticalTuple));
+    auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
+    auto criticalTuple = extender->toOrder(region, nullptr);
+    ASSERT_EQ(183, std::get<1>(criticalTuple));
+    ASSERT_EQ(186, std::get<2>(criticalTuple));
 
-    auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
-    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
-    auto result = assumptionMaker.createAndCheckAssumption(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple));
+    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
+    auto result = assumptionMaker.createAndCheckAssumptions(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple), region);
 
-    EXPECT_EQ(3ul, result.size());
+    EXPECT_EQ(3, result.size());
 
-    bool foundFirst = false;
-    bool foundSecond = false;
-    bool foundThird = false;
-    for (auto itr = result.begin(); itr != result.end(); ++itr) {
-        if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "183") {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundFirst = true;
-        } else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundSecond = true;
-        } else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
-            foundThird = true;
-        } else {
-            EXPECT_TRUE(false);
-        }
+    for (auto res : result) {
+        EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, res.second);
+        EXPECT_EQ(true, res.first->getFirstOperand()->isVariable());
+        EXPECT_EQ(true, res.first->getSecondOperand()->isVariable());
     }
-    EXPECT_TRUE(foundFirst);
-    EXPECT_TRUE(foundSecond);
-    EXPECT_TRUE(foundThird);
-}
-
-TEST(AssumptionMakerTest, Brp_without_bisimulation_no_validation) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
-    std::string formulaAsString = "P=? [F s=4 & i=N ]";
-    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
-
-    // Program and formula
-    storm::prism::Program program = storm::api::parseProgram(programFile);
-    program = storm::utility::prism::preprocess(program, constantsAsString);
-    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
-    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
-    model = simplifier.getSimplifiedModel();
-    dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-
-    // Create the region
-    auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
-    auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
-
-    auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
-    auto criticalTuple = extender->toOrder(formulas);
-    ASSERT_EQ(183ul, std::get<1>(criticalTuple));
-    ASSERT_EQ(186ul, std::get<2>(criticalTuple));
-
-    auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
-    // This one does not validate the assumptions!
-    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), false);
-    auto result = assumptionMaker.createAndCheckAssumption(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple));
-
-    EXPECT_EQ(3ul, result.size());
-
-    bool foundFirst = false;
-    bool foundSecond = false;
-    bool foundThird = false;
-    for (auto itr = result.begin(); itr != result.end(); ++itr) {
-        if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "183") {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundFirst = true;
-        } else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundSecond = true;
-        } else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
-            foundThird = true;
-        } else {
-            EXPECT_TRUE(false);
-        }
-    }
-    EXPECT_TRUE(foundFirst);
-    EXPECT_TRUE(foundSecond);
-    EXPECT_TRUE(foundThird);
+    assumptionMaker.initializeCheckingOnSamples(formulas[0], model, region, 10);
+    result = assumptionMaker.createAndCheckAssumptions(std::get<1>(criticalTuple), std::get<2>(criticalTuple), std::get<0>(criticalTuple), region);
+    EXPECT_EQ(1, result.size());
+    auto itr = result.begin();
+    EXPECT_EQ(storm::analysis::AssumptionStatus::UNKNOWN, itr->second);
+    EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
+    EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
+    EXPECT_EQ("186", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ("183", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
 }
 
+
 TEST(AssumptionMakerTest, Simple1) {
     std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
-    std::string formulaAsString = "P=? [F s=3]";
-    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
 
     storm::prism::Program program = storm::api::parseProgram(programFile);
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
     std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
-    dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
     // Create the region
-    auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.999999", vars);
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
 
     storm::storage::BitVector above(5);
     above.set(3);
     storm::storage::BitVector below(5);
     below.set(4);
-    storm::storage::BitVector initialMiddle(5);
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
-
-    auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
-
-    auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
-    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
-    auto result = assumptionMaker.createAndCheckAssumption(1, 2, order);
-
-    EXPECT_EQ(3ul, result.size());
-
-    bool foundFirst = false;
-    bool foundSecond = false;
-    bool foundThird = false;
-    for (auto itr = result.begin(); itr != result.end(); ++itr) {
-        if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "1") {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundFirst = true;
-        } else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundSecond = true;
-        } else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
-            foundThird = true;
-        } else {
-            EXPECT_TRUE(false);
-        }
-    }
-    EXPECT_TRUE(foundFirst);
-    EXPECT_TRUE(foundSecond);
-    EXPECT_TRUE(foundThird);
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
+
+    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
+    auto result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
+    EXPECT_EQ(0, result.size());
+    assumptionMaker.initializeCheckingOnSamples(formulas[0], model, region, 10);
+    result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
+    EXPECT_EQ(0, result.size());
+
+    region = storm::api::parseRegion<storm::RationalFunction>("0.500001 <= p <= 0.999999", vars);
+    std::vector<std::vector<double>> samples;
+    assumptionMaker.setSampleValues(samples);
+    result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
+    EXPECT_EQ(1, result.size());
+    auto itr = result.begin();
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
+    EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
+    EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
+    EXPECT_EQ("1", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
 }
 
-TEST(AssumptionMakerTest, Simple2) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple2.pm";
-    std::string formulaAsString = "P=? [F s=3]";
-    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+TEST(AssumptionMakerTest, Casestudy1) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
 
     storm::prism::Program program = storm::api::parseProgram(programFile);
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
     std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
-    dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    model = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
     // Create the region
-    auto vars = storm::models::sparse::getProbabilityParameters(*dtmc);
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
     auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= p <= 0.999999", vars);
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 5ul);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 8ul);
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
 
     storm::storage::BitVector above(5);
     above.set(3);
     storm::storage::BitVector below(5);
     below.set(4);
-    storm::storage::BitVector initialMiddle(5);
 
-    std::vector<uint_fast64_t> statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
-
-    auto order = new storm::analysis::Order(&above, &below, &initialMiddle, 5, &statesSorted);
-
-    auto assumptionChecker = storm::analysis::AssumptionChecker<storm::RationalFunction>(formulas[0], dtmc, region, 3);
-    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction>(&assumptionChecker, dtmc->getNumberOfStates(), true);
-    auto result = assumptionMaker.createAndCheckAssumption(1, 2, order);
-
-    EXPECT_EQ(3ul, result.size());
-
-    bool foundFirst = false;
-    bool foundSecond = false;
-    bool foundThird = false;
-    for (auto itr = result.begin(); itr != result.end(); ++itr) {
-        if (!foundFirst && itr->first->getFirstOperand()->asVariableExpression().getVariable().getName() == "1") {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundFirst = true;
-        } else if (!foundSecond && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Greater) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
-            foundSecond = true;
-        } else if (!foundThird && itr->first->getRelationType() == storm::expressions::BinaryRelationExpression::RelationType::Equal) {
-            EXPECT_EQ(storm::analysis::AssumptionStatus::INVALID, itr->second);
-            EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
-            EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
-            EXPECT_EQ("2", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ("1", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
-            EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Equal, itr->first->getRelationType());
-            foundThird = true;
-        } else {
-            EXPECT_TRUE(false);
-        }
-    }
-    EXPECT_TRUE(foundFirst);
-    EXPECT_TRUE(foundSecond);
-    EXPECT_TRUE(foundThird);
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(model->getTransitionMatrix(), options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(model->getTransitionMatrix());
+    auto order = std::shared_ptr<storm::analysis::Order>(new storm::analysis::Order(&above, &below, 5, decomposition, statesSorted));
+
+    auto assumptionMaker = storm::analysis::AssumptionMaker<storm::RationalFunction, double>(model->getTransitionMatrix());
+    auto result = assumptionMaker.createAndCheckAssumptions(1, 2, order, region);
+
+    EXPECT_EQ(1, result.size());
+    auto itr = result.begin();
+    EXPECT_EQ(storm::analysis::AssumptionStatus::VALID, itr->second);
+    EXPECT_EQ(true, itr->first->getFirstOperand()->isVariable());
+    EXPECT_EQ(true, itr->first->getSecondOperand()->isVariable());
+    EXPECT_EQ("1", itr->first->getFirstOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ("2", itr->first->getSecondOperand()->asVariableExpression().getVariable().getName());
+    EXPECT_EQ(storm::expressions::BinaryRelationExpression::RelationType::Greater, itr->first->getRelationType());
 }
-
diff --git a/src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp b/src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp
index 14973cfc2..6fda1867b 100644
--- a/src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp
+++ b/src/test/storm-pars/analysis/MonotonicityCheckerTest.cpp
@@ -1,119 +1,73 @@
-//
-// Created by Jip Spel on 20.09.18.
-//
-
-#include "test/storm_gtest.h"
 #include "storm-config.h"
-#include "test/storm_gtest.h"
-#include "storm-pars/analysis/MonotonicityChecker.h"
-#include "storm/storage/expressions/BinaryRelationExpression.h"
-#include "storm/storage/SparseMatrix.h"
-#include "storm/adapters/RationalFunctionAdapter.h"
 
-#include "storm-parsers/parser/FormulaParser.h"
-#include "storm/logic/Formulas.h"
-#include "storm/models/sparse/StandardRewardModel.h"
-#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
-#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+#include "storm-pars/api/storm-pars.h"
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+
+#include "storm-parsers/api/storm-parsers.h"
 #include "storm-parsers/parser/AutoParser.h"
 #include "storm-parsers/parser/PrismParser.h"
-#include "storm/storage/expressions/ExpressionManager.h"
+
+#include "storm/adapters/RationalFunctionAdapter.h"
 #include "storm/api/builder.h"
+#include "storm/api/storm.h"
+#include "storm/logic/Formulas.h"
+#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
+#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
+#include "storm/storage/SparseMatrix.h"
 
-#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+#include "test/storm_gtest.h"
 
-#include "storm-pars/api/storm-pars.h"
-#include "storm/api/storm.h"
+TEST(MonotonicityCheckerTest, Simple1_larger_region) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
 
-#include "storm-parsers/api/storm-parsers.h"
-TEST(MonotonicityCheckerTest, Derivative_checker) {
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
 
     // Create the region
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
-    auto region =  storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
-
-    // Derivative 0
-    auto constFunction = storm::RationalFunction(0);
-    auto constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
-    EXPECT_TRUE(constFunctionRes.first);
-    EXPECT_TRUE(constFunctionRes.second);
-
-    // Derivative 5
-    constFunction = storm::RationalFunction(5);
-    constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
-    EXPECT_TRUE(constFunctionRes.first);
-    EXPECT_FALSE(constFunctionRes.second);
-
-    // Derivative -4
-    constFunction = storm::RationalFunction(storm::RationalFunction(1)-constFunction);
-    constFunctionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(constFunction, region);
-    EXPECT_FALSE(constFunctionRes.first);
-    EXPECT_TRUE(constFunctionRes.second);
-
-    std::shared_ptr<storm::RawPolynomialCache> cache = std::make_shared<storm::RawPolynomialCache>();
-    carl::StringParser parser;
-    parser.setVariables({"p", "q"});
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
 
-    // Create the region
-    auto functionP = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("p"), cache));
-    auto functionQ = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("q"), cache));
-
-    auto varsP = functionP.gatherVariables();
-    auto varsQ = functionQ.gatherVariables();
-    storm::utility::parametric::Valuation<storm::RationalFunction> lowerBoundaries2;
-    storm::utility::parametric::Valuation<storm::RationalFunction> upperBoundaries2;
-    for (auto var : varsP) {
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
-        lowerBoundaries2.emplace(std::make_pair(var, lb));
-        upperBoundaries2.emplace(std::make_pair(var, ub));
-    }
-    for (auto var : varsQ) {
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
-        lowerBoundaries2.emplace(std::make_pair(var, lb));
-        upperBoundaries2.emplace(std::make_pair(var, ub));
-    }
-    region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries2), std::move(upperBoundaries2));
-
-    // Derivative p
-    auto function = functionP;
-    auto functionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(function, region);
-    EXPECT_TRUE(functionRes.first);
-    EXPECT_FALSE(functionRes.second);
-
-    // Derivative 1-p
-    auto functionDecr = storm::RationalFunction(storm::RationalFunction(1)-function);
-    auto functionDecrRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionDecr, region);
-    EXPECT_TRUE(functionDecrRes.first);
-    EXPECT_FALSE(functionDecrRes.second);
-
-    // Derivative 1-2p
-    auto functionNonMonotonic = storm::RationalFunction(storm::RationalFunction(1)-storm::RationalFunction(2)*function);
-    auto functionNonMonotonicRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionNonMonotonic, region);
-    EXPECT_FALSE(functionNonMonotonicRes.first);
-    EXPECT_FALSE(functionNonMonotonicRes.second);
-
-    // Derivative -p
-    functionDecr = storm::RationalFunction(storm::RationalFunction(0)-function);
-    functionDecrRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(functionDecr, region);
-    EXPECT_FALSE(functionDecrRes.first);
-    EXPECT_TRUE(functionDecrRes.second);
-
-    // Derivative p*q
-    function = functionP * functionQ ;
-    functionRes = storm::analysis::MonotonicityChecker<storm::RationalFunction>::checkDerivative(function, region);
-    EXPECT_TRUE(functionRes.first);
-    EXPECT_FALSE(functionRes.second);
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+    // OrderExtender
+    storm::storage::SparseMatrix<storm::RationalFunction> matrix =  model->getTransitionMatrix();
+    auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
+    // Order
+    auto order = std::get<0>(orderExtender.toOrder(region, nullptr));
+    // monchecker
+    auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(model->getTransitionMatrix());
+
+    //start testing
+    auto var = modelParameters.begin();
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
 }
 
-TEST(MonotonicityCheckerTest, Brp_with_bisimulation_no_samples) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
-    std::string formulaAsString = "P=? [true U s=4 & i=N ]";
-    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+TEST(MonotonicityCheckerTest, Simple1_small_region) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
 
-    // Program and formula
+    // model
     storm::prism::Program program = storm::api::parseProgram(programFile);
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
@@ -123,46 +77,92 @@ TEST(MonotonicityCheckerTest, Brp_with_bisimulation_no_samples) {
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
 
-    // Apply bisimulation
-    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
-    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
-        bisimType = storm::storage::BisimulationType::Weak;
-    }
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
 
-    dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+    // OrderExtender
+    storm::storage::SparseMatrix<storm::RationalFunction> matrix =  model->getTransitionMatrix();
+    auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
+    // Order
+    auto order = std::get<0>(orderExtender.toOrder(region, nullptr));
+    // monchecker
+    auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(model->getTransitionMatrix());
+
+    //start testing
+    auto var = modelParameters.begin();
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 0, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
+}
+
+TEST(MonotonicityCheckerTest, Casestudy1) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
 
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
 
     // Create the region
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
-    std::set<typename storm::storage::ParameterRegion<storm::RationalFunction>::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc);
-    for (auto var : vars) {
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
-        lowerBoundaries.emplace(std::make_pair(var, lb));
-        upperBoundaries.emplace(std::make_pair(var, ub));
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+    // OrderExtender
+    storm::storage::SparseMatrix<storm::RationalFunction> matrix =  model->getTransitionMatrix();
+    auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
+    // Order
+    auto res =orderExtender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    ASSERT_TRUE(order->getDoneBuilding());
+
+    //monchecker
+    auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
+
+    //start testing
+    auto var = modelParameters.begin();
+    for (uint_fast64_t i = 0; i < 3; i++) {
+        EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, i, *var, region));
     }
-    auto region =  storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
-std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
-
-    ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
-
-    storm::analysis::MonotonicityChecker<storm::RationalFunction> monotonicityChecker = storm::analysis::MonotonicityChecker<storm::RationalFunction>(dtmc, formulas, regions, true);
-    auto result = monotonicityChecker.checkMonotonicity(std::cout);
-    EXPECT_EQ(1ul, result.size());
-    EXPECT_EQ(2ul, result.begin()->second.size());
-    auto monotone = result.begin()->second.begin();
-    EXPECT_EQ(true, monotone->second.first);
-    EXPECT_EQ(false, monotone->second.second);
 }
 
-TEST(MonotonicityCheckerTest, Brp_with_bisimulation_samples) {
-    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
-    std::string formulaAsString = "P=? [true U s=4 & i=N ]";
-    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+TEST(MonotonicityCheckerTest, Casestudy2) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
+    std::string formulaAsString = "P=? [F s=4 ]";
+    std::string constantsAsString = "";
 
-    // Program and formula
+    // model
     storm::prism::Program program = storm::api::parseProgram(programFile);
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
@@ -172,35 +172,92 @@ TEST(MonotonicityCheckerTest, Brp_with_bisimulation_samples) {
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
 
-    // Apply bisimulation
-    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
-    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
-        bisimType = storm::storage::BisimulationType::Weak;
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+    // OrderExtender
+    storm::storage::SparseMatrix<storm::RationalFunction> matrix =  model->getTransitionMatrix();
+    auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
+    // Order
+    auto res =orderExtender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    order->addRelation(1,3);
+    order->addRelation(3,2);
+
+    //monchecker
+    auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
+
+    //start testing
+    auto var = modelParameters.begin();
+    for (uint_fast64_t i = 0; i < 3; i++) {
+        EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, i, *var, region));
     }
+}
 
-    dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+TEST(MonotonicityCheckerTest, Casestudy3) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
 
     // Create the region
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
-    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
-    std::set<typename storm::storage::ParameterRegion<storm::RationalFunction>::VariableType> vars = storm::models::sparse::getProbabilityParameters(*dtmc);
-    for (auto var : vars) {
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
-        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
-        lowerBoundaries.emplace(std::make_pair(var, lb));
-        upperBoundaries.emplace(std::make_pair(var, ub));
-    }
-    auto region =  storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
     std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
-
-    auto monotonicityChecker = storm::analysis::MonotonicityChecker<storm::RationalFunction>(dtmc, formulas, regions, true, 50);
-    auto result = monotonicityChecker.checkMonotonicity(std::cout);
-    EXPECT_EQ(1ul, result.size());
-    EXPECT_EQ(2ul, result.begin()->second.size());
-    auto monotone = result.begin()->second.begin();
-    EXPECT_EQ(true, monotone->second.first);
-    EXPECT_EQ(false, monotone->second.second);
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+    // OrderExtender
+    storm::storage::SparseMatrix<storm::RationalFunction> matrix =  model->getTransitionMatrix();
+    auto orderExtender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, matrix);
+    // Order
+    auto res =orderExtender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    ASSERT_TRUE(order->getDoneBuilding());
+
+    //monchecker
+    auto monChecker = new storm::analysis::MonotonicityChecker<storm::RationalFunction>(matrix);
+
+    //start testing
+    auto var = modelParameters.begin();
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Not, monChecker->checkLocalMonotonicity(order, 0, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
+
+    region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Decr, monChecker->checkLocalMonotonicity(order, 0, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 1, *var, region));
+    EXPECT_EQ(storm::analysis::MonotonicityChecker<storm::RationalFunction>::Monotonicity::Incr, monChecker->checkLocalMonotonicity(order, 2, *var, region));
 }
diff --git a/src/test/storm-pars/analysis/MonotonicityHelperTest.cpp b/src/test/storm-pars/analysis/MonotonicityHelperTest.cpp
new file mode 100644
index 000000000..7078cf39c
--- /dev/null
+++ b/src/test/storm-pars/analysis/MonotonicityHelperTest.cpp
@@ -0,0 +1,454 @@
+#include "test/storm_gtest.h"
+#include "storm-config.h"
+#include "test/storm_gtest.h"
+#include "storm/storage/expressions/BinaryRelationExpression.h"
+#include "storm/storage/SparseMatrix.h"
+#include "storm/adapters/RationalFunctionAdapter.h"
+
+#include "storm-parsers/parser/FormulaParser.h"
+#include "storm/logic/Formulas.h"
+#include "storm/models/sparse/StandardRewardModel.h"
+#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
+#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+#include "storm-parsers/parser/AutoParser.h"
+#include "storm-parsers/parser/PrismParser.h"
+#include "storm/storage/expressions/ExpressionManager.h"
+#include "storm/api/builder.h"
+
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+
+#include "storm-pars/api/storm-pars.h"
+#include "storm/api/storm.h"
+
+#include "storm-parsers/api/storm-parsers.h"
+
+TEST(MonotonicityHelperTest, Derivative_checker) {
+    // Create the region
+    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation lowerBoundaries;
+    typename storm::storage::ParameterRegion<storm::RationalFunction>::Valuation upperBoundaries;
+    auto region =  storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries), std::move(upperBoundaries));
+
+    // Derivative 0
+    auto constFunction = storm::RationalFunction(0);
+    auto constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
+    EXPECT_TRUE(constFunctionRes.first);
+    EXPECT_TRUE(constFunctionRes.second);
+
+    // Derivative 5
+    constFunction = storm::RationalFunction(5);
+    constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
+    EXPECT_TRUE(constFunctionRes.first);
+    EXPECT_FALSE(constFunctionRes.second);
+
+    // Derivative -4
+    constFunction = storm::RationalFunction(storm::RationalFunction(1)-constFunction);
+    constFunctionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(constFunction, region);
+    EXPECT_FALSE(constFunctionRes.first);
+    EXPECT_TRUE(constFunctionRes.second);
+
+    std::shared_ptr<storm::RawPolynomialCache> cache = std::make_shared<storm::RawPolynomialCache>();
+    carl::StringParser parser;
+    parser.setVariables({"p", "q"});
+
+    // Create the region
+    auto functionP = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("p"), cache));
+    auto functionQ = storm::RationalFunction(storm::Polynomial(parser.template parseMultivariatePolynomial<storm::RationalFunctionCoefficient>("q"), cache));
+
+    auto varsP = functionP.gatherVariables();
+    auto varsQ = functionQ.gatherVariables();
+    storm::utility::parametric::Valuation<storm::RationalFunction> lowerBoundaries2;
+    storm::utility::parametric::Valuation<storm::RationalFunction> upperBoundaries2;
+    for (auto var : varsP) {
+        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
+        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
+        lowerBoundaries2.emplace(std::make_pair(var, lb));
+        upperBoundaries2.emplace(std::make_pair(var, ub));
+    }
+    for (auto var : varsQ) {
+        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType lb = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(0 + 0.000001);
+        typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType ub = storm::utility::convertNumber<typename storm::storage::ParameterRegion<storm::RationalFunction>::CoefficientType>(1 - 0.000001);
+        lowerBoundaries2.emplace(std::make_pair(var, lb));
+        upperBoundaries2.emplace(std::make_pair(var, ub));
+    }
+    region = storm::storage::ParameterRegion<storm::RationalFunction>(std::move(lowerBoundaries2), std::move(upperBoundaries2));
+
+    // Derivative p
+    auto function = functionP;
+    auto functionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(function, region);
+    EXPECT_TRUE(functionRes.first);
+    EXPECT_FALSE(functionRes.second);
+
+    // Derivative 1-p
+    auto functionDecr = storm::RationalFunction(storm::RationalFunction(1)-function);
+    auto functionDecrRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionDecr, region);
+    EXPECT_TRUE(functionDecrRes.first);
+    EXPECT_FALSE(functionDecrRes.second);
+
+    // Derivative 1-2p
+    auto functionNonMonotonic = storm::RationalFunction(storm::RationalFunction(1)-storm::RationalFunction(2)*function);
+    auto functionNonMonotonicRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionNonMonotonic, region);
+    EXPECT_FALSE(functionNonMonotonicRes.first);
+    EXPECT_FALSE(functionNonMonotonicRes.second);
+
+    // Derivative -p
+    functionDecr = storm::RationalFunction(storm::RationalFunction(0)-function);
+    functionDecrRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(functionDecr, region);
+    EXPECT_FALSE(functionDecrRes.first);
+    EXPECT_TRUE(functionDecrRes.second);
+
+    // Derivative p*q
+    function = functionP * functionQ ;
+    functionRes = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>::checkDerivative(function, region);
+    EXPECT_TRUE(functionRes.first);
+    EXPECT_FALSE(functionRes.second);
+}
+
+TEST(MonotonicityHelperTest, Brp_with_bisimulation_no_samples) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+    std::string formulaAsString = "P=? [true U s=4 & i=N ]";
+    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Apply bisimulation
+    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+        bisimType = storm::storage::BisimulationType::Weak;
+    }
+
+    model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    ASSERT_EQ(model->getNumberOfStates(), 99);
+    ASSERT_EQ(model->getNumberOfTransitions(), 195);
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // Start testing
+    storm::analysis::MonotonicityHelper<storm::RationalFunction, double> MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, true);
+    // Check if correct result size
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    EXPECT_EQ(1, result.size());
+
+    // Check if the order and general monotonicity result is correct.
+    auto order = result.begin()->first;
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_TRUE(monotonicityResult->existsMonotonicity());
+    EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    // Check if result for each variable is correct
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, Brp_with_bisimulation_samples) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+    std::string formulaAsString = "P=? [true U s=4 & i=N ]";
+    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Apply bisimulation
+    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+        bisimType = storm::storage::BisimulationType::Weak;
+    }
+
+    model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    ASSERT_EQ(model->getNumberOfStates(), 99);
+    ASSERT_EQ(model->getNumberOfTransitions(), 195);
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // Start testing
+    storm::analysis::MonotonicityHelper<storm::RationalFunction, double> MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, true, 50);
+    // Check if correct result size
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    EXPECT_EQ(1, result.size());
+
+    // Check if the order and general monotonicity result is correct.
+    auto order = result.begin()->first;
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_TRUE(monotonicityResult->existsMonotonicity());
+    EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    // Check if result for each variable is correct
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, zeroconf) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/zeroconf4.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=5 ]";
+    std::string constantsAsString = "n = 4"; //e.g. pL=0.9,TOACK=0.5
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+        bisimType = storm::storage::BisimulationType::Weak;
+    }
+
+    model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    ASSERT_EQ(model->getNumberOfStates(), 7);
+    ASSERT_EQ(model->getNumberOfTransitions(), 12);
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.9, 0.1<=pK<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // Start testing
+    auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 50);
+    // Check if correct result size
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    EXPECT_EQ(1, result.size());
+
+    // Check if the order and general monotonicity result is correct.
+    auto order = result.begin()->first;
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_TRUE(monotonicityResult->existsMonotonicity());
+    EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
+    // TODO @Jip we have 1 assumption instead of 0 here
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    // Check if result for each variable is correct
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, Simple1) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=3 ]";
+    std::string constantsAsString = "";
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.49", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    // Start testing
+    auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
+
+    // Check if correct result size
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    EXPECT_EQ(1, result.size());
+
+    // Check if the order and general monotonicity result is correct.
+    auto order = result.begin()->first;
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_FALSE(monotonicityResult->existsMonotonicity());
+    EXPECT_FALSE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    // Check if result for each variable is correct
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Unknown, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, Casestudy1) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=3 ]";
+    std::string constantsAsString = "";
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
+
+    auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    ASSERT_EQ(1, result.size());
+
+    auto order = result.begin()->first;
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_TRUE(monotonicityResult->existsMonotonicity());
+    EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, CaseStudy2) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=4 ]";
+    std::string constantsAsString = "";
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    ASSERT_EQ(model->getNumberOfStates(), 6);
+    ASSERT_EQ(model->getNumberOfTransitions(), 12);
+
+    // Start testing
+    auto monotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
+
+    // Check if correct result size
+    auto result = monotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+    EXPECT_EQ(1, result.size());
+    EXPECT_FALSE(result.begin()->first->getDoneBuilding());
+}
+
+TEST(MonotonicityHelperTest, Casestudy3_not_monotone) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=3 ]";
+    std::string constantsAsString = "";
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
+
+    auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+
+    ASSERT_EQ(1, result.size());
+    auto order = result.begin()->first;
+
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_FALSE(monotonicityResult->existsMonotonicity());
+    EXPECT_FALSE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Unknown, entry.second);
+    }
+}
+
+TEST(MonotonicityHelperTest, Casestudy3_monotone) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
+    std::string formulaAsString = "P > 0.5 [ F s=3 ]";
+    std::string constantsAsString = "";
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.49", modelParameters);
+    std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions = {region};
+
+    ASSERT_EQ(model->getNumberOfStates(), 5);
+    ASSERT_EQ(model->getNumberOfTransitions(), 8);
+
+    auto MonotonicityHelper = storm::analysis::MonotonicityHelper<storm::RationalFunction, double>(model, formulas, regions, 10);
+    auto result = MonotonicityHelper.checkMonotonicityInBuild(std::cout, false);
+
+    ASSERT_EQ(1, result.size());
+    auto order = result.begin()->first;
+
+    auto monotonicityResult = result.begin()->second.first;
+    EXPECT_TRUE(monotonicityResult->isDone());
+    EXPECT_TRUE(monotonicityResult->existsMonotonicity());
+    EXPECT_TRUE(monotonicityResult->isAllMonotonicity());
+    auto assumptions = result.begin()->second.second;
+    EXPECT_EQ(0, assumptions.size());
+
+    auto monRes = monotonicityResult->getMonotonicityResult();
+    for (auto entry : monRes) {
+        EXPECT_EQ(storm::analysis::MonotonicityResult<storm::RationalFunctionVariable>::Monotonicity::Incr, entry.second);
+    }
+}
diff --git a/src/test/storm-pars/analysis/OrderExtenderTest.cpp b/src/test/storm-pars/analysis/OrderExtenderTest.cpp
index 96b18bb86..6bfb4d25c 100644
--- a/src/test/storm-pars/analysis/OrderExtenderTest.cpp
+++ b/src/test/storm-pars/analysis/OrderExtenderTest.cpp
@@ -1,27 +1,22 @@
-#include "test/storm_gtest.h"
 #include "storm-config.h"
-#include "test/storm_gtest.h"
 
-#include "storm-parsers/parser/FormulaParser.h"
-#include "storm/logic/Formulas.h"
-#include "storm/models/sparse/StandardRewardModel.h"
-#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
-#include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
+#include "storm-pars/api/storm-pars.h"
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+
+#include "storm-parsers/api/storm-parsers.h"
 #include "storm-parsers/parser/AutoParser.h"
 #include "storm-parsers/parser/PrismParser.h"
-#include "storm/storage/expressions/ExpressionManager.h"
-#include "storm/api/builder.h"
-
-#include "storm-pars/analysis/Order.h"
-#include "storm-pars/analysis/OrderExtender.h"
-#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
 
-#include "storm-pars/api/storm-pars.h"
+#include "storm/api/builder.h"
 #include "storm/api/storm.h"
+#include "storm/logic/Formulas.h"
+#include "storm/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
+#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
+#include "storm/models/sparse/StandardRewardModel.h"
 
-#include "storm-parsers/api/storm-parsers.h"
+#include "test/storm_gtest.h"
 
-TEST(OrderExtenderTest, Brp_with_bisimulation) {
+TEST(OrderExtenderTest, Brp_with_bisimulation_on_model) {
     std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
     std::string formulaAsString = "P=? [F s=4 & i=N ]";
     std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@@ -31,8 +26,7 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
     std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
 
@@ -42,19 +36,23 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
         bisimType = storm::storage::BisimulationType::Weak;
     }
 
-    dtmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 99ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 195ull);
+    ASSERT_EQ(model->getNumberOfStates(), 99);
+    ASSERT_EQ(model->getNumberOfTransitions(), 195);
 
-    auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
-    auto criticalTuple = extender->toOrder(formulas);
-    EXPECT_EQ(dtmc->getNumberOfStates(), std::get<1>(criticalTuple));
-    EXPECT_EQ(dtmc->getNumberOfStates(), std::get<2>(criticalTuple));
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
+    auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
+
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
+    auto monRes = new storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>;
+    auto criticalTuple = extender.toOrder(region, make_shared<storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>>(*monRes));
+    EXPECT_EQ(model->getNumberOfStates(), std::get<1>(criticalTuple));
+    EXPECT_EQ(model->getNumberOfStates(), std::get<2>(criticalTuple));
 
     auto order = std::get<0>(criticalTuple);
-    for (uint_fast64_t i = 0; i < dtmc->getNumberOfStates(); ++i) {
-        EXPECT_TRUE((*order->getAddedStates())[i]);
+    for (uint_fast64_t i = 0; i < model->getNumberOfStates(); ++i) {
+        EXPECT_TRUE((order->contains(i)));
     }
 
     // Check on some nodes
@@ -65,7 +63,7 @@ TEST(OrderExtenderTest, Brp_with_bisimulation) {
     EXPECT_EQ(storm::analysis::Order::NodeComparison::UNKNOWN, order->compare(7,13));
 }
 
-TEST(OrderExtenderTest, Brp_without_bisimulation) {
+TEST(OrderExtenderTest, Brp_without_bisimulation_on_model) {
     std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
     std::string formulaAsString = "P=? [F s=4 & i=N ]";
     std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
@@ -75,19 +73,315 @@ TEST(OrderExtenderTest, Brp_without_bisimulation) {
     program = storm::utility::prism::preprocess(program, constantsAsString);
     std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
     std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*dtmc);
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
     ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
     model = simplifier.getSimplifiedModel();
-    dtmc = model->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
 
-    ASSERT_EQ(dtmc->getNumberOfStates(), 193ull);
-    ASSERT_EQ(dtmc->getNumberOfTransitions(), 383ull);
+    ASSERT_EQ(model->getNumberOfStates(), 193);
+    ASSERT_EQ(model->getNumberOfTransitions(), 383);
+
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
+    auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
 
-    auto *extender = new storm::analysis::OrderExtender<storm::RationalFunction>(dtmc);
-    auto criticalTuple = extender->toOrder(formulas);
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
+    auto monRes = new storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>;
+    auto criticalTuple = extender.toOrder(region, make_shared<storm::analysis::MonotonicityResult<typename storm::analysis::OrderExtender<storm::RationalFunction, double>::VariableType>>(*monRes));
     EXPECT_EQ(183ul, std::get<1>(criticalTuple));
     EXPECT_EQ(186ul, std::get<2>(criticalTuple));
 }
 
+TEST(OrderExtenderTest, Brp_with_bisimulation_on_matrix) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+    std::string formulaAsString = "P=? [F s=4 & i=N ]";
+    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Apply bisimulation
+    storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+    if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+        bisimType = storm::storage::BisimulationType::Weak;
+    }
+
+    model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+    ASSERT_EQ(model->getNumberOfStates(), 99);
+    ASSERT_EQ(model->getNumberOfTransitions(), 195);
+
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
+    auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
+    auto res = extender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
+    EXPECT_TRUE(order->getDoneBuilding());
+
+    // Check on some nodes
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,5));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(5,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(94,5));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::UNKNOWN, order->compare(7,13));
+}
+
+TEST(OrderExtenderTest, Brp_without_bisimulation_on_matrix) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+    std::string formulaAsString = "P=? [F s=4 & i=N ]";
+    std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+    // Program and formula
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    ASSERT_EQ(model->getNumberOfStates(), 193);
+    ASSERT_EQ(model->getNumberOfTransitions(), 383);
+
+    auto vars = storm::models::sparse::getProbabilityParameters(*model);
+    auto region = storm::api::parseRegion<storm::RationalFunction>("0.00001 <= pK <= 0.999999, 0.00001 <= pL <= 0.999999", vars);
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
+    auto res = extender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    EXPECT_FALSE(order->getDoneBuilding());
+}
 
+TEST(OrderExtenderTest, simple1_on_model) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
+
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
+    auto order = std::get<0>(extender.toOrder(region));
+    EXPECT_EQ(order->getNumberOfAddedStates(), 5);
+    EXPECT_TRUE(order->getDoneBuilding());
+
+    // Check on all states
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
+}
+
+TEST(OrderExtenderTest, simple1_on_matrix) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.9", modelParameters);
+
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+
+    // OrderExtender
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
+    auto res = extender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
+    EXPECT_TRUE(order->getDoneBuilding());
+
+    // Check on all states, as this one automatically handles assumptions (if there is one valid) all are ABOVE
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
+}
+
+TEST(OrderExtenderTest, casestudy1_on_model) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51<=p<=0.9", modelParameters);
+
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(model, formulas[0]);
+    auto order = std::get<0>(extender.toOrder(region));
+
+    EXPECT_EQ(order->getNumberOfAddedStates(), 5);
+    EXPECT_TRUE(order->getDoneBuilding());
+    // Check on all states
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
+}
+
+TEST(OrderExtenderTest, casestudy1_on_matrix) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
+    std::string formulaAsString = "P=? [F s=3 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.51 <= p <= 0.9", modelParameters);
+
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+
+    // OrderExtender
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
+    auto res = extender.extendOrder(nullptr, region);
+    auto order = std::get<0>(res);
+    EXPECT_EQ(order->getNumberOfAddedStates(), model->getNumberOfStates());
+    EXPECT_TRUE(order->getDoneBuilding());
+
+    // Check on all states
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,1));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(3,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,0));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(1,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,2));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(0,4));
+    EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order->compare(2,4));
+}
+
+TEST(OrderExtenderTest, casestudy2_on_matrix) {
+    std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
+    std::string formulaAsString = "P=? [F s=4 ]";
+    std::string constantsAsString = "";
+
+    // model
+    storm::prism::Program program = storm::api::parseProgram(programFile);
+    program = storm::utility::prism::preprocess(program, constantsAsString);
+    std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+    std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+    auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+    ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+    model = simplifier.getSimplifiedModel();
+
+    // Create the region
+    auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+    auto region=storm::api::parseRegion<storm::RationalFunction>("0.1 <= p <= 0.2", modelParameters);
+
+    // For order extender
+    storm::modelchecker::SparsePropositionalModelChecker<storm::models::sparse::Model<storm::RationalFunction>> propositionalChecker(*model);
+    storm::storage::BitVector phiStates;
+    storm::storage::BitVector psiStates;
+    phiStates = storm::storage::BitVector(model->getTransitionMatrix().getRowCount(), true);
+    storm::logic::EventuallyFormula formula = formulas[0]->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula();
+    psiStates = propositionalChecker.check(formula.getSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector();
+    // Get the maybeStates
+    std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(model->getBackwardTransitions(), phiStates, psiStates);
+    storm::storage::BitVector topStates = statesWithProbability01.second;
+    storm::storage::BitVector bottomStates = statesWithProbability01.first;
+
+    // OrderExtender
+    auto extender = storm::analysis::OrderExtender<storm::RationalFunction, double>(&topStates, &bottomStates, model->getTransitionMatrix());
+    auto res = extender.extendOrder(nullptr, region);
+    EXPECT_TRUE(std::get<0>(res)->getDoneBuilding());
+}
diff --git a/src/test/storm-pars/analysis/OrderTest.cpp b/src/test/storm-pars/analysis/OrderTest.cpp
index f4481d34d..1026c9bc7 100644
--- a/src/test/storm-pars/analysis/OrderTest.cpp
+++ b/src/test/storm-pars/analysis/OrderTest.cpp
@@ -1,7 +1,7 @@
+#include <storm/storage/StronglyConnectedComponentDecomposition.h>
 #include "test/storm_gtest.h"
 #include "storm-config.h"
-#include "test/storm_gtest.h"
-#include "storm-pars/analysis/Order.h"
+#include "storm-pars/api/analysis.h"
 #include "storm/storage/BitVector.h"
 
 TEST(OrderTest, Simple) {
@@ -10,10 +10,15 @@ TEST(OrderTest, Simple) {
     above.set(0);
     auto below = storm::storage::BitVector(numberOfStates);
     below.set(1);
-    auto initialMiddle = storm::storage::BitVector(numberOfStates);
-    std::vector<uint_fast64_t> statesSorted;
-
-    auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
+    storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
+    matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
+    matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto matrix= matrixBuilder.build();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
+    auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
     EXPECT_EQ(storm::analysis::Order::NodeComparison::ABOVE, order.compare(0,1));
     EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,0));
     EXPECT_EQ(nullptr, order.getNode(2));
@@ -77,10 +82,15 @@ TEST(OrderTest, copy_order) {
     above.set(0);
     auto below = storm::storage::BitVector(numberOfStates);
     below.set(1);
-    auto initialMiddle = storm::storage::BitVector(numberOfStates);
-    std::vector<uint_fast64_t> statesSorted;
-
-    auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
+    storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
+    matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
+    matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto matrix= matrixBuilder.build();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
+    auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
     order.add(2);
     order.add(3);
     order.addToNode(4, order.getNode(2));
@@ -143,10 +153,15 @@ TEST(OrderTest, merge_nodes) {
     above.set(0);
     auto below = storm::storage::BitVector(numberOfStates);
     below.set(1);
-    auto initialMiddle = storm::storage::BitVector(numberOfStates);
-    std::vector<uint_fast64_t> statesSorted;
-
-    auto order = storm::analysis::Order(&above, &below, &initialMiddle, numberOfStates, &statesSorted);
+    storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
+    matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
+    matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto matrix= matrixBuilder.build();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
+    auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
     order.add(2);
     order.add(3);
     order.addToNode(4, order.getNode(2));
@@ -171,3 +186,57 @@ TEST(OrderTest, merge_nodes) {
     EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,4));
     EXPECT_EQ(storm::analysis::Order::NodeComparison::BELOW, order.compare(1,5));
 }
+
+TEST(OrderTest, sort_states) {
+    auto numberOfStates = 7;
+    auto above = storm::storage::BitVector(numberOfStates);
+    above.set(0);
+    auto below = storm::storage::BitVector(numberOfStates);
+    below.set(1);
+    storm::storage::SparseMatrixBuilder<storm::RationalFunction> matrixBuilder(2,2,2);
+    matrixBuilder.addNextValue(0,0,storm::RationalFunction(1));
+    matrixBuilder.addNextValue(1,1,storm::RationalFunction(1));
+    storm::storage::StronglyConnectedComponentDecompositionOptions options;
+    options.forceTopologicalSort();
+    auto matrix= matrixBuilder.build();
+    auto decomposition = storm::storage::StronglyConnectedComponentDecomposition<storm::RationalFunction>(matrix, options);
+    auto statesSorted = storm::utility::graph::getTopologicalSort(matrix);
+    auto order = storm::analysis::Order(&above, &below, numberOfStates, decomposition, statesSorted);
+    order.add(2);
+    order.add(3);
+    order.addToNode(4, order.getNode(2));
+    order.addBetween(5, order.getNode(0), order.getNode(3));
+    order.addBetween(6, order.getNode(5), order.getNode(3));
+
+    std::vector<uint_fast64_t> statesToSort = std::vector<uint_fast64_t> {0,1,5,6};
+    auto sortedStates = order.sortStates(&statesToSort);
+    EXPECT_EQ(sortedStates.size(), 4);
+
+    auto itr = sortedStates.begin();
+    EXPECT_EQ(*itr, 0);
+    EXPECT_EQ(*(++itr), 5);
+    EXPECT_EQ(*(++itr), 6);
+    EXPECT_EQ(*(++itr), 1);
+
+    statesToSort = std::vector<uint_fast64_t> {0,1,5,6,2};
+    sortedStates = order.sortStates(&statesToSort);
+    EXPECT_EQ(sortedStates.size(), 5);
+
+    itr = sortedStates.begin();
+    EXPECT_EQ(*itr, 0);
+    EXPECT_EQ(*(++itr), 5);
+    EXPECT_EQ(*(++itr), 6);
+    EXPECT_EQ(*(++itr), 1);
+    EXPECT_EQ(*(++itr), 7);
+
+    statesToSort = std::vector<uint_fast64_t> {0,2,1,5,6};
+    sortedStates = order.sortStates(&statesToSort);
+    EXPECT_EQ(sortedStates.size(), 5);
+
+    itr = sortedStates.begin();
+    EXPECT_EQ(*itr, 0);
+    EXPECT_EQ(*(++itr), 2);
+    EXPECT_EQ(*(++itr), 1);
+    EXPECT_EQ(*(++itr), 7);
+    EXPECT_EQ(*(++itr), 7);
+}
diff --git a/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp b/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
index b8b914e50..0dfd63fe7 100644
--- a/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
+++ b/src/test/storm-pars/modelchecker/SparseDtmcParameterLiftingTest.cpp
@@ -12,6 +12,10 @@
 
 #include "storm/environment/solver/MinMaxSolverEnvironment.h"
 #include "storm/storage/jani/Property.h"
+#include "storm-pars/transformer/SparseParametricDtmcSimplifier.h"
+#include "storm/solver/stateelimination/NondeterministicModelStateEliminator.h"
+#include "storm/storage/StronglyConnectedComponentDecomposition.h"
+
 
 
 namespace {
@@ -25,7 +29,7 @@ namespace {
             return env;
         }
     };
-    
+
     class DoubleSVIEnvironment {
     public:
         typedef double ValueType;
@@ -36,7 +40,7 @@ namespace {
             return env;
         }
     };
-    
+
     class RationalPiEnvironment {
     public:
         typedef storm::RationalNumber ValueType;
@@ -57,44 +61,42 @@ namespace {
     private:
         storm::Environment _environment;
     };
-  
+
     typedef ::testing::Types<
             DoubleViEnvironment,
             DoubleSVIEnvironment,
             RationalPiEnvironment
     > TestingTypes;
-    
+
    TYPED_TEST_SUITE(SparseDtmcParameterLiftingTest, TestingTypes,);
 
-    
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob) {
         typedef typename TestFixture::ValueType ValueType;
 
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
         std::string formulaAsString = "P<=0.84 [F s=5 ]";
         std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
-    
+
         // Program and formula
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
-    
     }
 
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_no_simplification) {
@@ -124,125 +126,122 @@ namespace {
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
-
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew) {
         typedef typename TestFixture::ValueType ValueType;
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
         std::string formulaAsString = "R>2.5 [F ((s=5) | (s=0&srep=3)) ]";
         std::string constantsAsString = "pL=0.9,TOAck=0.5";
-        
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.875,0.75<=TOMsg<=0.95", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=pK<=0.9,0.5<=TOMsg<=0.95", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.3,0.2<=TOMsg<=0.3", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-        
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew_Bounded) {
         typedef typename TestFixture::ValueType ValueType;
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
         std::string formulaAsString = "R>2.5 [ C<=300]";
         std::string constantsAsString = "pL=0.9,TOAck=0.5";
-        
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.875,0.75<=TOMsg<=0.95", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=pK<=0.9,0.5<=TOMsg<=0.95", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.3,0.2<=TOMsg<=0.3", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-        
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_exactValidation) {
         typedef typename TestFixture::ValueType ValueType;
         if (!std::is_same<ValueType, storm::RationalNumber>::value) {
-    
+
             std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
             std::string formulaAsString = "P<=0.84 [F s=5 ]";
             std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
-        
+
             // Program and formula
             storm::prism::Program program = storm::api::parseProgram(programFile);
             program = storm::utility::prism::preprocess(program, constantsAsString);
             std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
             std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-            
+
             auto regionChecker = storm::api::initializeValidatingRegionModelChecker<storm::RationalFunction, ValueType, storm::RationalNumber>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
             auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
             auto rewParameters = storm::models::sparse::getRewardParameters(*model);
             modelParameters.insert(rewParameters.begin(), rewParameters.end());
-            
+
             //start testing
             auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
             auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
             auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
-        
+
             EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         }
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew_exactValidation) {
         typedef typename TestFixture::ValueType ValueType;
         if (!std::is_same<ValueType, storm::RationalNumber>::value) {
             std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
             std::string formulaAsString = "R>2.5 [F ((s=5) | (s=0&srep=3)) ]";
             std::string constantsAsString = "pL=0.9,TOAck=0.5";
-            
+
             storm::prism::Program program = storm::api::parseProgram(programFile);
             program = storm::utility::prism::preprocess(program, constantsAsString);
             std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
             std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-            
+
             auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
             auto rewParameters = storm::models::sparse::getRewardParameters(*model);
             modelParameters.insert(rewParameters.begin(), rewParameters.end());
-            
+
             auto regionChecker = storm::api::initializeValidatingRegionModelChecker<storm::RationalFunction, ValueType, storm::RationalNumber>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
             //start testing
             auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.875,0.75<=TOMsg<=0.95", modelParameters);
             auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=pK<=0.9,0.5<=TOMsg<=0.95", modelParameters);
             auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.3,0.2<=TOMsg<=0.3", modelParameters);
-        
+
             EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         }
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew_Bounded_exactValidation) {
         typedef typename TestFixture::ValueType ValueType;
 
@@ -250,32 +249,32 @@ namespace {
             std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
             std::string formulaAsString = "R>2.5 [ C<=300]";
             std::string constantsAsString = "pL=0.9,TOAck=0.5";
-            
+
             storm::prism::Program program = storm::api::parseProgram(programFile);
             program = storm::utility::prism::preprocess(program, constantsAsString);
             std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
             std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-            
+
             auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
             auto rewParameters = storm::models::sparse::getRewardParameters(*model);
             modelParameters.insert(rewParameters.begin(), rewParameters.end());
-            
+
             auto regionChecker = storm::api::initializeValidatingRegionModelChecker<storm::RationalFunction, ValueType, storm::RationalNumber>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-            
+
             //start testing
             auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.875,0.75<=TOMsg<=0.95", modelParameters);
             auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=pK<=0.9,0.5<=TOMsg<=0.95", modelParameters);
             auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.3,0.2<=TOMsg<=0.3", modelParameters);
-        
+
             EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
             EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         }
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew_Infty) {
         typedef typename TestFixture::ValueType ValueType;
-        
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
         std::string formulaAsString = "R>2.5 [F (s=0&srep=3) ]";
         std::string constantsAsString = "";
@@ -283,23 +282,22 @@ namespace {
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-    
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.9,0.6<=pL<=0.85,0.9<=TOMsg<=0.95,0.85<=TOAck<=0.9", modelParameters);
-        
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-    
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Rew_4Par) {
         typedef typename TestFixture::ValueType ValueType;
-        
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp_rewards16_2.pm";
         std::string formulaAsString = "R>2.5 [F ((s=5) | (s=0&srep=3)) ]";
         std::string constantsAsString = ""; //!! this model will have 4 parameters
@@ -307,139 +305,689 @@ namespace {
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pK<=0.9,0.6<=pL<=0.85,0.9<=TOMsg<=0.95,0.85<=TOAck<=0.9", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.7,0.2<=pL<=0.8,0.15<=TOMsg<=0.65,0.3<=TOAck<=0.9", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pK<=0.4,0.2<=pL<=0.3,0.15<=TOMsg<=0.3,0.1<=TOAck<=0.2", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-        
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Crowds_Prob) {
         typedef typename TestFixture::ValueType ValueType;
-        
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/crowds3_5.pm";
         std::string formulaAsString = "P<0.5 [F \"observe0Greater1\" ]";
         std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
-    
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-       
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-    
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=PF<=0.75,0.15<=badC<=0.2", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.75<=PF<=0.8,0.2<=badC<=0.3", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=PF<=0.95,0.2<=badC<=0.2", modelParameters);
         auto allVioHardRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=PF<=0.95,0.2<=badC<=0.9", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::CenterViolated, regionChecker->analyzeRegion(this->env(), allVioHardRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-    
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Crowds_Prob_stepBounded) {
         typedef typename TestFixture::ValueType ValueType;
-        
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/crowds3_5.pm";
         std::string formulaAsString = "P<0.5 [F<=300 \"observe0Greater1\" ]";
         std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
-    
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-       
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-    
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=PF<=0.75,0.15<=badC<=0.2", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.75<=PF<=0.8,0.2<=badC<=0.3", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=PF<=0.95,0.2<=badC<=0.2", modelParameters);
         auto allVioHardRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=PF<=0.95,0.2<=badC<=0.9", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::CenterViolated, regionChecker->analyzeRegion(this->env(), allVioHardRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-    
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Crowds_Prob_1Par) {
          typedef typename TestFixture::ValueType ValueType;
-       
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/crowds3_5.pm";
         std::string formulaAsString = "P>0.75 [F \"observe0Greater1\" ]";
         std::string constantsAsString = "badC=0.3"; //e.g. pL=0.9,TOACK=0.5
-    
-        
+
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-        
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.9<=PF<=0.99", modelParameters);
         auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=PF<=0.9", modelParameters);
         auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.01<=PF<=0.8", modelParameters);
-    
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
         EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-        
     }
-    
+
     TYPED_TEST(SparseDtmcParameterLiftingTest, Crowds_Prob_Const) {
         typedef typename TestFixture::ValueType ValueType;
-        
+
         std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/crowds3_5.pm";
         std::string formulaAsString = "P>0.6 [F \"observe0Greater1\" ]";
         std::string constantsAsString = "PF=0.9,badC=0.2";
-        
+
         storm::prism::Program program = storm::api::parseProgram(programFile);
         program = storm::utility::prism::preprocess(program, constantsAsString);
         std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
         std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
-        
+
         auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
         auto rewParameters = storm::models::sparse::getRewardParameters(*model);
         modelParameters.insert(rewParameters.begin(), rewParameters.end());
-        
+
         auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
-    
+
         //start testing
         auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("", modelParameters);
-        
+
+        EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, ZeroConf) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/zeroconf4.pm";
+        std::string formulaAsString = "P>0.5 [F s=5 ]";
+        std::string constantsAsString = " n = 4"; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true));
+
+        //start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=pL<=0.95,0.8<=pK<=0.95", modelParameters);
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=pL<=0.9,0.6<=pK<=0.9", modelParameters);
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.7,0.1<=pK<=0.7", modelParameters);
+
         EXPECT_EQ(storm::modelchecker::RegionResult::AllSat, regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true));
-    
+        EXPECT_EQ(storm::modelchecker::RegionResult::ExistsBoth, regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
+        EXPECT_EQ(storm::modelchecker::RegionResult::AllViolated, regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_Mon_LEQ) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+        std::string formulaAsString = "P<=0.84 [F s=5 ]";
+        std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Simplify model
+        std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions;
+        auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+        ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+        model = simplifier.getSimplifiedModel()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+        formulas[0] = simplifier.getSimplifiedFormula();
+
+        // Apply bisimulation
+        storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+        if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+            bisimType = storm::storage::BisimulationType::Weak;
+        }
+        model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Reachability order, as it is already done building we don't need to recreate the order for each region
+        auto monHelper = new storm::analysis::MonotonicityHelper<storm::RationalFunction, ValueType>(model, formulas, regions);
+        auto order = monHelper->checkMonotonicityInBuild(std::cout).begin()->first;
+        ASSERT_EQ(order->getNumberOfAddedStates(), model->getTransitionMatrix().getColumnCount());
+        ASSERT_TRUE(order->getDoneBuilding());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        //start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        auto expectedResult = regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_Mon_GEQ) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2.pm";
+        std::string formulaAsString = "P>=0.84 [F s=5 ]";
+        std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Simplify model
+        std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions;
+        auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+        ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+        model = simplifier.getSimplifiedModel()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+        formulas[0] = simplifier.getSimplifiedFormula();
+
+        // Apply bisimulation
+        storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+        if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+            bisimType = storm::storage::BisimulationType::Weak;
+        }
+        model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Reachability order, as it is already done building we don't need to recreate the order for each region
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, storm::api::parseRegion<storm::RationalFunction>("0.01<=pL<=0.99,0.01<=pK<=0.99", modelParameters));
+        auto order = std::get<0>(res);
+        ASSERT_EQ(order->getNumberOfAddedStates(), model->getTransitionMatrix().getColumnCount());
+        ASSERT_TRUE(order->getDoneBuilding());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        //start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
+        auto expectedResult = regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult =regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult,regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_Mon_LEQ_Incr) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2_mon_incr.pm";
+        std::string formulaAsString = "P<=0.84 [F s=5 ]";
+        std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Simplify model
+        std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions;
+        auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+        ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+        model = simplifier.getSimplifiedModel()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+        formulas[0] = simplifier.getSimplifiedFormula();
+
+        // Apply bisimulation
+        storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+        if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+            bisimType = storm::storage::BisimulationType::Weak;
+        }
+        model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Reachability order, as it is already done building we don't need to recreate the order for each region
+        auto monHelper = new storm::analysis::MonotonicityHelper<storm::RationalFunction, ValueType>(model, formulas, regions);
+        auto order = monHelper->checkMonotonicityInBuild(std::cout).begin()->first;
+        ASSERT_EQ(order->getNumberOfAddedStates(), model->getTransitionMatrix().getColumnCount());
+        ASSERT_TRUE(order->getDoneBuilding());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        //start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        auto expectedResult = regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Brp_Prob_Mon_GEQ_Incr) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/brp16_2_mon_incr.pm";
+        std::string formulaAsString = "P>=0.84 [F s=5 ]";
+        std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Simplify model
+        std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions;
+        auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+        ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+        model = simplifier.getSimplifiedModel()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+        formulas[0] = simplifier.getSimplifiedFormula();
+
+        // Apply bisimulation
+        storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+        if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+            bisimType = storm::storage::BisimulationType::Weak;
+        }
+        model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Reachability order, as it is already done building we don't need to recreate the order for each region
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, storm::api::parseRegion<storm::RationalFunction>("0.01<=pL<=0.99,0.01<=pK<=0.99", modelParameters));
+        auto order = std::get<0>(res);
+
+        ASSERT_EQ(order->getNumberOfAddedStates(), model->getTransitionMatrix().getColumnCount());
+        ASSERT_TRUE(order->getDoneBuilding());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        //start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=pL<=0.73,0.2<=pK<=0.715", modelParameters);
+        auto expectedResult = regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=pL<=0.65,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult = regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=pL<=0.9,0.75<=pK<=0.95", modelParameters);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        expectedResult =regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true);
+        EXPECT_EQ(expectedResult, regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(expectedResult,regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Parametric_Die_Mon) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/parametric_die_2.pm";
+        std::string formulaAsString = "P <=0.5 [F s=7 & d=2 ]";
+        std::string constantsAsString = ""; //e.g. pL=0.9,TOACK=0.5
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Simplify model
+        std::vector<storm::storage::ParameterRegion<storm::RationalFunction>> regions;
+        auto simplifier = storm::transformer::SparseParametricDtmcSimplifier<storm::models::sparse::Dtmc<storm::RationalFunction>>(*model);
+        ASSERT_TRUE(simplifier.simplify(*(formulas[0])));
+        model = simplifier.getSimplifiedModel()->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+        formulas[0] = simplifier.getSimplifiedFormula();
+
+        // Apply bisimulation
+        storm::storage::BisimulationType bisimType = storm::storage::BisimulationType::Strong;
+        if (storm::settings::getModule<storm::settings::modules::BisimulationSettings>().isWeakBisimulationSet()) {
+            bisimType = storm::storage::BisimulationType::Weak;
+        }
+        model = storm::api::performBisimulationMinimization<storm::RationalFunction>(model, formulas, bisimType)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        // Start testing
+        auto allSatRegion = storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.2,0.8<=q<=0.9", modelParameters);
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, allSatRegion);
+        auto order = std::get<0>(res);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion = storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9,0.1<=q<=0.9", modelParameters);
+        res = o->extendOrder(nullptr, exBothRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion = storm::api::parseRegion<storm::RationalFunction>("0.8<=p<=0.9,0.1<=q<=0.2", modelParameters);
+        res = o->extendOrder(nullptr, allVioRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Simple1_Mon) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/simple1.pm";
+        std::string formulaAsString = "P<0.75 [F s=3 ]";
+        std::string constantsAsString = "";
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false);
+
+        // Start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=p<=0.6", modelParameters);
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, allSatRegion);
+        auto order = std::get<0>(res);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.9", modelParameters);
+        res = o->extendOrder(nullptr, exBothRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.05<=p<=0.1", modelParameters);
+        res = o->extendOrder(nullptr, allVioRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Casestudy1_Mon) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy1.pm";
+        std::string formulaAsString = "P<0.5 [F s=3 ]";
+        std::string constantsAsString = "";
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false);
+
+        // Start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.5", modelParameters);
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, allSatRegion);
+        auto order = std::get<0>(res);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=p<=0.8", modelParameters);
+        res = o->extendOrder(nullptr, exBothRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.7<=p<=0.9", modelParameters);
+        res = o->extendOrder(nullptr, allVioRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Casestudy2_Mon) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy2.pm";
+        std::string formulaAsString = "P<0.5 [F s=4 ]";
+        std::string constantsAsString = "";
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false);
+
+        // Start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.4", modelParameters);
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, allSatRegion);
+        auto order = std::get<0>(res);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.4<=p<=0.9", modelParameters);
+        res = o->extendOrder(nullptr, exBothRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.8<=p<=0.9", modelParameters);
+        res = o->extendOrder(nullptr, allVioRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown,storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+    }
+
+
+    TYPED_TEST(SparseDtmcParameterLiftingTest, Casestudy3_Mon) {
+        typedef typename TestFixture::ValueType ValueType;
+
+        std::string programFile = STORM_TEST_RESOURCES_DIR "/pdtmc/casestudy3.pm";
+        std::string formulaAsString = "P<0.5 [F s=3 ]";
+        std::string constantsAsString = "";
+
+        // Program and formula
+        storm::prism::Program program = storm::api::parseProgram(programFile);
+        program = storm::utility::prism::preprocess(program, constantsAsString);
+        std::vector<std::shared_ptr<const storm::logic::Formula>> formulas = storm::api::extractFormulasFromProperties(storm::api::parsePropertiesForPrismProgram(formulaAsString, program));
+        std::shared_ptr<storm::models::sparse::Dtmc<storm::RationalFunction>> model = storm::api::buildSparseModel<storm::RationalFunction>(program, formulas)->as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
+
+        // Model parameters
+        auto modelParameters = storm::models::sparse::getProbabilityParameters(*model);
+        auto rewParameters = storm::models::sparse::getRewardParameters(*model);
+        modelParameters.insert(rewParameters.begin(), rewParameters.end());
+
+        // Modelcheckers
+        auto regionCheckerMon = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false, true);
+        auto regionChecker = storm::api::initializeParameterLiftingRegionModelChecker<storm::RationalFunction, ValueType>(this->env(), model, storm::api::createTask<storm::RationalFunction>(formulas[0], true), false, false);
+
+        // Start testing
+        auto allSatRegion=storm::api::parseRegion<storm::RationalFunction>("0.6<=p<=0.9", modelParameters);
+        auto o = new storm::analysis::OrderExtender<storm::RationalFunction, ValueType>(model, formulas[0]);
+        auto res = o->extendOrder(nullptr, allSatRegion);
+        auto order = std::get<0>(res);
+        auto monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        ASSERT_TRUE(order->getDoneBuilding());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allSatRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto exBothRegion=storm::api::parseRegion<storm::RationalFunction>("0.3<=p<=0.7", modelParameters);
+        res = o->extendOrder(nullptr, exBothRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        ASSERT_TRUE(order->getDoneBuilding());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), exBothRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+
+        auto allVioRegion=storm::api::parseRegion<storm::RationalFunction>("0.1<=p<=0.4", modelParameters);
+        res = o->extendOrder(nullptr, allVioRegion);
+        order = std::get<0>(res);
+        monRes = std::make_shared<storm::analysis::LocalMonotonicityResult<storm::RationalFunctionVariable>>(order->getNumberOfStates());
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
+        // Twice, as the monRes will be initialized now
+        EXPECT_EQ(regionChecker->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true), regionCheckerMon->analyzeRegion(this->env(), allVioRegion, storm::modelchecker::RegionResultHypothesis::Unknown, storm::modelchecker::RegionResult::Unknown, true, order, monRes));
     }
 }
 #endif