| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -17,6 +17,11 @@ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/settings/modules/CoreSettings.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/logic/Formulas.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/exceptions/UncheckedRequirementException.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/solver/Multiplier.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/storage/SparseMatrix.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/utility/graph.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/storage/MaximalEndComponentDecomposition.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					#include "storm/modelchecker/rpatl/helper/internal/SoundGameViHelper.h"
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					namespace { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -114,6 +119,7 @@ namespace { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>> getTasks(std::vector<std::shared_ptr<storm::logic::Formula const>> const& formulas) const { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            std::vector<storm::modelchecker::CheckTask<storm::logic::Formula, ValueType>> result; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            for (auto const& f : formulas) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                std::cout << *f << std::endl; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                result.emplace_back(*f); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            return result; | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -345,5 +351,152 @@ namespace { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("1"), this->getQuantitativeResultAtInitialState(model, result), this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    TYPED_TEST(SmgRpatlModelCheckerTest, Deflate) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        typedef double ValueType; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::string formulasString = " <<maxP>> Pmax=? [F (s=2)]"; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto modelFormulas = this->buildModelFormulas(STORM_TEST_RESOURCES_DIR "/smg/example_smg.nm", formulasString); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto model = std::move(modelFormulas.first); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto tasks = this->getTasks(modelFormulas.second); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto checker = this->createModelChecker(model); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::unique_ptr<storm::modelchecker::CheckResult> result; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto transitionMatrix = model->getTransitionMatrix(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto formulas = std::move(modelFormulas.second); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::logic::GameFormula const& gameFormula = formulas[0]->asGameFormula(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::modelchecker::CheckTask<storm::logic::GameFormula, ValueType> checkTask(gameFormula); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::BitVector statesOfCoalition = model->computeStatesOfCoalition(gameFormula.getCoalition()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        statesOfCoalition.complement(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // TODO Fabian: get optimization direction
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::BitVector psiStates = checker->check(this->env(), gameFormula.getSubformula().asProbabilityOperatorFormula().getSubformula().asEventuallyFormula().getSubformula().asStateFormula())->asExplicitQualitativeCheckResult().getTruthValuesVector(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::BitVector phiStates(model->getNumberOfStates(), true); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::SparseMatrix<ValueType> backwardTransitions = model->getBackwardTransitions(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::OptimizationDirection optimizationDirection = storm::OptimizationDirection::Maximize; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto minimizerStates = optimizationDirection == storm::OptimizationDirection::Maximize ? statesOfCoalition : ~statesOfCoalition; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::modelchecker::helper::internal::SoundGameViHelper<ValueType> viHelper(transitionMatrix, backwardTransitions, statesOfCoalition, psiStates, optimizationDirection); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.prepareSolversAndMultipliers(this->env()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> xL = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        for (size_t i = 0; i < xL.size(); i++) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            if (psiStates[i]) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                xL[i] = 1; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> xU = std::vector<ValueType>(transitionMatrix.getRowGroupCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::BitVector probGreater0 = storm::utility::graph::performProbGreater0(backwardTransitions, phiStates, psiStates); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto xU_begin = xU.begin(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::for_each(xU.begin(), xU.end(), [&probGreater0, &xU_begin](ValueType &it) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                          if (probGreater0[&it - &(*xU_begin)]) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                              it = 1; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					                      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // performValueIteration
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> _x1L = xL; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> _x2L = _x1L; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> _x1U = xU; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> _x2U = _x1U; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // perform first iteration step
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::BitVector reducedMinimizerActions = {storm::storage::BitVector(transitionMatrix.getRowCount(), true)}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> choiceValuesL = std::vector<ValueType>(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper._multiplier->multiply(this->env(), _x2L, nullptr, choiceValuesL); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.reduceChoiceValues(choiceValuesL, &reducedMinimizerActions, _x1L); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // over approximation
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        std::vector<ValueType> choiceValuesU = std::vector<ValueType>(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper._multiplier->multiply(this->env(), _x2U, nullptr, choiceValuesU); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.reduceChoiceValues(choiceValuesU, nullptr, _x1U); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::SparseMatrix<ValueType> restrictedTransMatrix = transitionMatrix.restrictRows(reducedMinimizerActions); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        storm::storage::MaximalEndComponentDecomposition<ValueType> MSEC = storm::storage::MaximalEndComponentDecomposition<ValueType>(restrictedTransMatrix, backwardTransitions); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // reducing the choiceValuesU
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        size_t i = 0; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        auto new_end = std::remove_if(choiceValuesU.begin(), choiceValuesU.end(), [&reducedMinimizerActions, &i](const auto& item) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            bool ret = !(reducedMinimizerActions[i]); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            i++; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            return ret; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        choiceValuesU.erase(new_end, choiceValuesU.end()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // deflating the MSECs
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.deflate(MSEC, restrictedTransMatrix, _x1U, choiceValuesU); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        xL = _x1L; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        xU = _x1U; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0"), xL[0], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.333333"), xL[1], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("1"), xL[2], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0"), xL[3], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.666666"), xU[0], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.666666"), xU[1], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("1"), xU[2], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0"), xU[3], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // second iteration step
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        reducedMinimizerActions = {storm::storage::BitVector(transitionMatrix.getRowCount(), true)}; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        choiceValuesL = std::vector<ValueType>(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper._multiplier->multiply(this->env(), _x1L, nullptr, choiceValuesL); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.reduceChoiceValues(choiceValuesL, &reducedMinimizerActions, _x2L); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // over approximation
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        choiceValuesU = std::vector<ValueType>(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper._multiplier->multiply(this->env(), _x1U, nullptr, choiceValuesU); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.reduceChoiceValues(choiceValuesU, nullptr, _x2U); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        restrictedTransMatrix = transitionMatrix.restrictRows(reducedMinimizerActions); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        MSEC = storm::storage::MaximalEndComponentDecomposition<ValueType>(restrictedTransMatrix, backwardTransitions); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // reducing the choiceValuesU
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        i = 0; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        new_end = std::remove_if(choiceValuesU.begin(), choiceValuesU.end(), [&reducedMinimizerActions, &i](const auto& item) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            bool ret = !(reducedMinimizerActions[i]); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            i++; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            return ret; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        choiceValuesU.erase(new_end, choiceValuesU.end()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        // deflating the MSECs
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        viHelper.deflate(MSEC, restrictedTransMatrix, _x2U, choiceValuesU); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        xL = _x2L; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        xU = _x2U; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.333333"), xL[0], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.444444"), xL[1], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("1"), xL[2], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0"), xL[3], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.555555"), xU[0], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0.555555"), xU[1], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("1"), xU[2], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        EXPECT_NEAR(this->parseNumber("0"), xU[3], this->precision()); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    // TODO: create more test cases (files)
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} |