/**TODO:  Add copyright*/

#define BOOST_TEST_MODULE LossFunction test suite
#include <boost/test/included/unit_test.hpp>
#include <SmartPeak/ml/LossFunction.h>

#include <iostream>

using namespace SmartPeak;
using namespace std;

BOOST_AUTO_TEST_SUITE(lossFunction)

/**
  ManhattanDistanceLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorEuclideanDistanceOp)
{
  ManhattanDistanceLossOp<double>* ptrReLU = nullptr;
  ManhattanDistanceLossOp<double>* nullPointerReLU = nullptr;
  BOOST_CHECK_EQUAL(ptrReLU, nullPointerReLU);
}

BOOST_AUTO_TEST_CASE(destructorEuclideanDistanceOp)
{
  ManhattanDistanceLossOp<double>* ptrReLU = nullptr;
	ptrReLU = new ManhattanDistanceLossOp<double>();
  delete ptrReLU;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersEuclideanDistanceOp)
{
  ManhattanDistanceLossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "ManhattanDistanceLossOp");
}

/**
  ManhattanDistanceLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorEuclideanDistanceGradOp)
{
  ManhattanDistanceLossGradOp<double>* ptrReLU = nullptr;
  ManhattanDistanceLossGradOp<double>* nullPointerReLU = nullptr;
  BOOST_CHECK_EQUAL(ptrReLU, nullPointerReLU);
}

BOOST_AUTO_TEST_CASE(destructorEuclideanDistanceGradOp)
{
  ManhattanDistanceLossGradOp<double>* ptrReLU = nullptr;
	ptrReLU = new ManhattanDistanceLossGradOp<double>();
  delete ptrReLU;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersEuclideanDistanceGradOp)
{
  ManhattanDistanceLossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "ManhattanDistanceLossGradOp");
}

/**
  L2NormLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorL2NormOp)
{
  L2NormLossOp<double>* ptrL2Norm = nullptr;
  L2NormLossOp<double>* nullPointerL2Norm = nullptr;
  BOOST_CHECK_EQUAL(ptrL2Norm, nullPointerL2Norm);
}

BOOST_AUTO_TEST_CASE(destructorL2NormOp)
{
  L2NormLossOp<double>* ptrL2Norm = nullptr;
	ptrL2Norm = new L2NormLossOp<double>();
  delete ptrL2Norm;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersL2NormOp)
{
  L2NormLossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "L2NormLossOp");
}

/**
  L2NormLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorL2NormGradOp)
{
  L2NormLossGradOp<double>* ptrL2Norm = nullptr;
  L2NormLossGradOp<double>* nullPointerL2Norm = nullptr;
  BOOST_CHECK_EQUAL(ptrL2Norm, nullPointerL2Norm);
}

BOOST_AUTO_TEST_CASE(destructorL2NormGradOp)
{
  L2NormLossGradOp<double>* ptrL2Norm = nullptr;
	ptrL2Norm = new L2NormLossGradOp<double>();
  delete ptrL2Norm;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersL2NormGradOp)
{
  L2NormLossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "L2NormLossGradOp");
}

/**
  CrossEntropyOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorCrossEntropyOp)
{
  BCELossOp<double>* ptrCrossEntropy = nullptr;
  BCELossOp<double>* nullPointerCrossEntropy = nullptr;
  BOOST_CHECK_EQUAL(ptrCrossEntropy, nullPointerCrossEntropy);
}

BOOST_AUTO_TEST_CASE(destructorCrossEntropyOp)
{
  BCELossOp<double>* ptrCrossEntropy = nullptr;
	ptrCrossEntropy = new BCELossOp<double>();
  delete ptrCrossEntropy;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersBCEOp)
{
  BCELossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "BCELossOp");
}

/**
  CrossEntropyGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorCrossEntropyGradOp)
{
  BCELossGradOp<double>* ptrCrossEntropy = nullptr;
  BCELossGradOp<double>* nullPointerCrossEntropy = nullptr;
  BOOST_CHECK_EQUAL(ptrCrossEntropy, nullPointerCrossEntropy);
}

BOOST_AUTO_TEST_CASE(destructorCrossEntropyGradOp)
{
  BCELossGradOp<double>* ptrCrossEntropy = nullptr;
	ptrCrossEntropy = new BCELossGradOp<double>();
  delete ptrCrossEntropy;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersBCEGradOp)
{
  BCELossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "BCELossGradOp");
}

/**
  NegativeLogLikelihoodLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorNegativeLogLikelihoodOp)
{
  NegativeLogLikelihoodLossOp<double>* ptrNegativeLogLikelihood = nullptr;
  NegativeLogLikelihoodLossOp<double>* nullPointerNegativeLogLikelihood = nullptr;
  BOOST_CHECK_EQUAL(ptrNegativeLogLikelihood, nullPointerNegativeLogLikelihood);
}

BOOST_AUTO_TEST_CASE(destructorNegativeLogLikelihoodOp)
{
  NegativeLogLikelihoodLossOp<double>* ptrNegativeLogLikelihood = nullptr;
	ptrNegativeLogLikelihood = new NegativeLogLikelihoodLossOp<double>();
  delete ptrNegativeLogLikelihood;
}

/**
  NegativeLogLikelihoodLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorNegativeLogLikelihoodGradOp)
{
  NegativeLogLikelihoodLossGradOp<double>* ptrNegativeLogLikelihood = nullptr;
  NegativeLogLikelihoodLossGradOp<double>* nullPointerNegativeLogLikelihood = nullptr;
  BOOST_CHECK_EQUAL(ptrNegativeLogLikelihood, nullPointerNegativeLogLikelihood);
}

BOOST_AUTO_TEST_CASE(destructorNegativeLogLikelihoodGradOp)
{
  NegativeLogLikelihoodLossGradOp<double>* ptrNegativeLogLikelihood = nullptr;
	ptrNegativeLogLikelihood = new NegativeLogLikelihoodLossGradOp<double>();
  delete ptrNegativeLogLikelihood;
}

/**
  MSELossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSEOp)
{
  MSELossOp<double>* ptrMSE = nullptr;
  MSELossOp<double>* nullPointerMSE = nullptr;
  BOOST_CHECK_EQUAL(ptrMSE, nullPointerMSE);
}

BOOST_AUTO_TEST_CASE(destructorMSEOp)
{
  MSELossOp<double>* ptrMSE = nullptr;
	ptrMSE = new MSELossOp<double>();
  delete ptrMSE;
}

/**
  MSELossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSEGradOp)
{
  MSELossGradOp<double>* ptrMSE = nullptr;
  MSELossGradOp<double>* nullPointerMSE = nullptr;
  BOOST_CHECK_EQUAL(ptrMSE, nullPointerMSE);
}

BOOST_AUTO_TEST_CASE(destructorMSEGradOp)
{
  MSELossGradOp<double>* ptrMSE = nullptr;
	ptrMSE = new MSELossGradOp<double>();
  delete ptrMSE;
}

/**
  MAELossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMAEOp)
{
  MAELossOp<double>* ptrMAE = nullptr;
  MAELossOp<double>* nullPointerMAE = nullptr;
  BOOST_CHECK_EQUAL(ptrMAE, nullPointerMAE);
}

BOOST_AUTO_TEST_CASE(destructorMAEOp)
{
  MAELossOp<double>* ptrMAE = nullptr;
  ptrMAE = new MAELossOp<double>();
  delete ptrMAE;
}

/**
  MAELossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMAEGradOp)
{
  MAELossGradOp<double>* ptrMAE = nullptr;
  MAELossGradOp<double>* nullPointerMAE = nullptr;
  BOOST_CHECK_EQUAL(ptrMAE, nullPointerMAE);
}

BOOST_AUTO_TEST_CASE(destructorMAEGradOp)
{
  MAELossGradOp<double>* ptrMAE = nullptr;
  ptrMAE = new MAELossGradOp<double>();
  delete ptrMAE;
}

/**
  MRSELossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMRSEOp)
{
  MRSELossOp<double>* ptrMRSE = nullptr;
  MRSELossOp<double>* nullPointerMRSE = nullptr;
  BOOST_CHECK_EQUAL(ptrMRSE, nullPointerMRSE);
}

BOOST_AUTO_TEST_CASE(destructorMRSEOp)
{
  MRSELossOp<double>* ptrMRSE = nullptr;
  ptrMRSE = new MRSELossOp<double>();
  delete ptrMRSE;
}

/**
  MRSELossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMRSEGradOp)
{
  MRSELossGradOp<double>* ptrMRSE = nullptr;
  MRSELossGradOp<double>* nullPointerMRSE = nullptr;
  BOOST_CHECK_EQUAL(ptrMRSE, nullPointerMRSE);
}

BOOST_AUTO_TEST_CASE(destructorMRSEGradOp)
{
  MRSELossGradOp<double>* ptrMRSE = nullptr;
  ptrMRSE = new MRSELossGradOp<double>();
  delete ptrMRSE;
}

/**
  MLELossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMLEOp)
{
  MLELossOp<double>* ptrMLE = nullptr;
  MLELossOp<double>* nullPointerMLE = nullptr;
  BOOST_CHECK_EQUAL(ptrMLE, nullPointerMLE);
}

BOOST_AUTO_TEST_CASE(destructorMLEOp)
{
  MLELossOp<double>* ptrMLE = nullptr;
  ptrMLE = new MLELossOp<double>();
  delete ptrMLE;
}

/**
  MLELossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMLEGradOp)
{
  MLELossGradOp<double>* ptrMLE = nullptr;
  MLELossGradOp<double>* nullPointerMLE = nullptr;
  BOOST_CHECK_EQUAL(ptrMLE, nullPointerMLE);
}

BOOST_AUTO_TEST_CASE(destructorMLEGradOp)
{
  MLELossGradOp<double>* ptrMLE = nullptr;
  ptrMLE = new MLELossGradOp<double>();
  delete ptrMLE;
}

/**
	KLDivergenceMuLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceMuOp)
{
	KLDivergenceMuLossOp<double>* ptrKLDivergenceMu = nullptr;
	KLDivergenceMuLossOp<double>* nullPointerKLDivergenceMu = nullptr;
	BOOST_CHECK_EQUAL(ptrKLDivergenceMu, nullPointerKLDivergenceMu);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceMuOp)
{
	KLDivergenceMuLossOp<double>* ptrKLDivergenceMu = nullptr;
	ptrKLDivergenceMu = new KLDivergenceMuLossOp<double>();
	delete ptrKLDivergenceMu;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceMuOp)
{
	KLDivergenceMuLossOp<float> operation(1e-3, 1, 5);
	BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceMuLossOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
	KLDivergenceMuLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceMuGradOp)
{
	KLDivergenceMuLossGradOp<double>* ptrKLDivergenceMu = nullptr;
	KLDivergenceMuLossGradOp<double>* nullPointerKLDivergenceMu = nullptr;
	BOOST_CHECK_EQUAL(ptrKLDivergenceMu, nullPointerKLDivergenceMu);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceMuGradOp)
{
	KLDivergenceMuLossGradOp<double>* ptrKLDivergenceMu = nullptr;
	ptrKLDivergenceMu = new KLDivergenceMuLossGradOp<double>();
	delete ptrKLDivergenceMu;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceGradMuOp)
{
	KLDivergenceMuLossGradOp<float> operation(1e-3, 1, 5);
	BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceMuLossGradOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
	KLDivergenceLogVarLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceLogVarOp)
{
	KLDivergenceLogVarLossOp<double>* ptrKLDivergenceLogVar = nullptr;
	KLDivergenceLogVarLossOp<double>* nullPointerKLDivergenceLogVar = nullptr;
	BOOST_CHECK_EQUAL(ptrKLDivergenceLogVar, nullPointerKLDivergenceLogVar);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceLogVarOp)
{
	KLDivergenceLogVarLossOp<double>* ptrKLDivergenceLogVar = nullptr;
	ptrKLDivergenceLogVar = new KLDivergenceLogVarLossOp<double>();
	delete ptrKLDivergenceLogVar;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceLogVarOp)
{
	KLDivergenceLogVarLossOp<float> operation(1e-3, 1, 5);
	BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceLogVarLossOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
	KLDivergenceLogVarLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceLogVarGradOp)
{
	KLDivergenceLogVarLossGradOp<double>* ptrKLDivergenceLogVar = nullptr;
	KLDivergenceLogVarLossGradOp<double>* nullPointerKLDivergenceLogVar = nullptr;
	BOOST_CHECK_EQUAL(ptrKLDivergenceLogVar, nullPointerKLDivergenceLogVar);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceLogVarGradOp)
{
	KLDivergenceLogVarLossGradOp<double>* ptrKLDivergenceLogVar = nullptr;
	ptrKLDivergenceLogVar = new KLDivergenceLogVarLossGradOp<double>();
	delete ptrKLDivergenceLogVar;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceLogVarLossGradOp)
{
	KLDivergenceLogVarLossGradOp<float> operation(1e-3, 1, 5);
	BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceLogVarLossGradOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
BCEWithLogitsLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorBCEWithLogitsOp)
{
	BCEWithLogitsLossOp<double>* ptrBCEWithLogits = nullptr;
	BCEWithLogitsLossOp<double>* nullPointerBCEWithLogits = nullptr;
	BOOST_CHECK_EQUAL(ptrBCEWithLogits, nullPointerBCEWithLogits);
}

BOOST_AUTO_TEST_CASE(destructorBCEWithLogitsOp)
{
	BCEWithLogitsLossOp<double>* ptrBCEWithLogits = nullptr;
	ptrBCEWithLogits = new BCEWithLogitsLossOp<double>();
	delete ptrBCEWithLogits;
}

/**
BCEWithLogitsLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorBCEWithLogitsGradOp)
{
	BCEWithLogitsLossGradOp<double>* ptrBCEWithLogits = nullptr;
	BCEWithLogitsLossGradOp<double>* nullPointerBCEWithLogits = nullptr;
	BOOST_CHECK_EQUAL(ptrBCEWithLogits, nullPointerBCEWithLogits);
}

BOOST_AUTO_TEST_CASE(destructorBCEWithLogitsGradOp)
{
	BCEWithLogitsLossGradOp<double>* ptrBCEWithLogits = nullptr;
	ptrBCEWithLogits = new BCEWithLogitsLossGradOp<double>();
	delete ptrBCEWithLogits;
}

/**
  MSERangeUBLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSERangeUBOp)
{
  MSERangeUBLossOp<double>* ptrMSERangeUB = nullptr;
  MSERangeUBLossOp<double>* nullPointerMSERangeUB = nullptr;
  BOOST_CHECK_EQUAL(ptrMSERangeUB, nullPointerMSERangeUB);
}

BOOST_AUTO_TEST_CASE(destructorMSERangeUBOp)
{
  MSERangeUBLossOp<double>* ptrMSERangeUB = nullptr;
  ptrMSERangeUB = new MSERangeUBLossOp<double>();
  delete ptrMSERangeUB;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMSERangeUBOp)
{
  MSERangeUBLossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MSERangeUBLossOp");
}

/**
  MSERangeUBLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSERangeUBGradOp)
{
  MSERangeUBLossGradOp<double>* ptrMSERangeUB = nullptr;
  MSERangeUBLossGradOp<double>* nullPointerMSERangeUB = nullptr;
  BOOST_CHECK_EQUAL(ptrMSERangeUB, nullPointerMSERangeUB);
}

BOOST_AUTO_TEST_CASE(destructorMSERangeUBGradOp)
{
  MSERangeUBLossGradOp<double>* ptrMSERangeUB = nullptr;
  ptrMSERangeUB = new MSERangeUBLossGradOp<double>();
  delete ptrMSERangeUB;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMSERangeUBGradOp)
{
  MSERangeUBLossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MSERangeUBLossGradOp");
}

/**
  MSERangeLBLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSERangeLBOp)
{
  MSERangeLBLossOp<double>* ptrMSERangeLB = nullptr;
  MSERangeLBLossOp<double>* nullPointerMSERangeLB = nullptr;
  BOOST_CHECK_EQUAL(ptrMSERangeLB, nullPointerMSERangeLB);
}

BOOST_AUTO_TEST_CASE(destructorMSERangeLBOp)
{
  MSERangeLBLossOp<double>* ptrMSERangeLB = nullptr;
  ptrMSERangeLB = new MSERangeLBLossOp<double>();
  delete ptrMSERangeLB;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMSERangeLBOp)
{
  MSERangeLBLossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MSERangeLBLossOp");
}

/**
  MSERangeLBLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMSERangeLBGradOp)
{
  MSERangeLBLossGradOp<double>* ptrMSERangeLB = nullptr;
  MSERangeLBLossGradOp<double>* nullPointerMSERangeLB = nullptr;
  BOOST_CHECK_EQUAL(ptrMSERangeLB, nullPointerMSERangeLB);
}

BOOST_AUTO_TEST_CASE(destructorMSERangeLBGradOp)
{
  MSERangeLBLossGradOp<double>* ptrMSERangeLB = nullptr;
  ptrMSERangeLB = new MSERangeLBLossGradOp<double>();
  delete ptrMSERangeLB;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMSERangeLBGradOp)
{
  MSERangeLBLossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MSERangeLBLossGradOp");
}

/**
  KLDivergenceCatLossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceCatOp)
{
  KLDivergenceCatLossOp<double>* ptrKLDivergenceCat = nullptr;
  KLDivergenceCatLossOp<double>* nullPointerKLDivergenceCat = nullptr;
  BOOST_CHECK_EQUAL(ptrKLDivergenceCat, nullPointerKLDivergenceCat);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceCatOp)
{
  KLDivergenceCatLossOp<double>* ptrKLDivergenceCat = nullptr;
  ptrKLDivergenceCat = new KLDivergenceCatLossOp<double>();
  delete ptrKLDivergenceCat;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceCatOp)
{
  KLDivergenceCatLossOp<float> operation(1e-3, 1, 5);
  BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceCatLossOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
  KLDivergenceCatLossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorKLDivergenceCatGradOp)
{
  KLDivergenceCatLossGradOp<double>* ptrKLDivergenceCat = nullptr;
  KLDivergenceCatLossGradOp<double>* nullPointerKLDivergenceCat = nullptr;
  BOOST_CHECK_EQUAL(ptrKLDivergenceCat, nullPointerKLDivergenceCat);
}

BOOST_AUTO_TEST_CASE(destructorKLDivergenceCatGradOp)
{
  KLDivergenceCatLossGradOp<double>* ptrKLDivergenceCat = nullptr;
  ptrKLDivergenceCat = new KLDivergenceCatLossGradOp<double>();
  delete ptrKLDivergenceCat;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersKLDivergenceCatGradOp)
{
  KLDivergenceCatLossGradOp<float> operation(1e-3, 1, 5);
  BOOST_CHECK_EQUAL(operation.getName(), "KLDivergenceCatLossGradOp");
	BOOST_CHECK_EQUAL(operation.getParameters().size(), 3);
	BOOST_CHECK_CLOSE(operation.getParameters().at(0), 1e-3, 1e-4);
	BOOST_CHECK_EQUAL(operation.getParameters().at(1), 1);
	BOOST_CHECK_EQUAL(operation.getParameters().at(2), 5);
}

/**
  MAPELossOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMAPELossOp)
{
  MAPELossOp<double>* ptrMAPELoss = nullptr;
  MAPELossOp<double>* nullPointerMAPELoss = nullptr;
  BOOST_CHECK_EQUAL(ptrMAPELoss, nullPointerMAPELoss);
}

BOOST_AUTO_TEST_CASE(destructorMAPELossOp)
{
  MAPELossOp<double>* ptrMAPELoss = nullptr;
  ptrMAPELoss = new MAPELossOp<double>();
  delete ptrMAPELoss;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMAPELossOp)
{
  MAPELossOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MAPELossOp");
}

/**
  MAPELossGradOp Tests
*/
BOOST_AUTO_TEST_CASE(constructorMAPELossGradOp)
{
  MAPELossGradOp<double>* ptrMAPELoss = nullptr;
  MAPELossGradOp<double>* nullPointerMAPELoss = nullptr;
  BOOST_CHECK_EQUAL(ptrMAPELoss, nullPointerMAPELoss);
}

BOOST_AUTO_TEST_CASE(destructorMAPELossGradOp)
{
  MAPELossGradOp<double>* ptrMAPELoss = nullptr;
  ptrMAPELoss = new MAPELossGradOp<double>();
  delete ptrMAPELoss;
}

BOOST_AUTO_TEST_CASE(gettersAndSettersMAPELossGradOp)
{
  MAPELossGradOp<float> operation;
  BOOST_CHECK_EQUAL(operation.getName(), "MAPELossGradOp");
}

BOOST_AUTO_TEST_SUITE_END()


#include <boost/test/unit_test.hpp>

#include <physics/objects/GroundGenerator.h>
#include <physics/World.h>
#include <physics/objects/ObjectsFactory.h>

using namespace Physics;
using namespace Objects;

struct GroundGeneratorTestFixture {
	GroundGeneratorTestFixture() :
		w(b2Vec2(0.0f, 9.8f), 4, 8),
		obj(ObjectsFactory::init(w.getWorld())) {
	}
	World w;
	ObjectsFactory& obj;
};

BOOST_FIXTURE_TEST_SUITE(GroundGeneratorTests, GroundGeneratorTestFixture);

BOOST_AUTO_TEST_CASE(check_ground_creates_right_amount_of_segments)
{
	GroundGenerator gg(100, 1.1f);
	Ground* g = gg.genereteNew(Vector2(0.0f, 0.0f));
	BOOST_CHECK_EQUAL(g->getVertices()[g->getVertices().size() - 1].x, 110.0f);
}

BOOST_AUTO_TEST_SUITE_END();


/**
 *  @copyright Copyright 2018 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  @file JPetSimplePhysSignalReco.cpp
 */

#include "JPetSimplePhysSignalReco/JPetSimplePhysSignalReco.h"
#include "JPetSimplePhysSignalReco/HelperMathFunctions.h"
#include "JPetWriter/JPetWriter.h"

#include <boost/property_tree/json_parser.hpp>
#include <cassert>
#include <math.h>

using namespace boost::numeric::ublas;

JPetSimplePhysSignalReco::JPetSimplePhysSignalReco() : fAlpha(1), fThresholdSel(-1) { readConfigFileAndSetAlphaAndThreshParams("configParams.json"); }

JPetSimplePhysSignalReco::~JPetSimplePhysSignalReco() {}

bool JPetSimplePhysSignalReco::exec() { return true; }

void JPetSimplePhysSignalReco::savePhysSignal(JPetPhysSignal) {}

/**
 * Simple example of creating JPetPhysSignal from JPetRecoSignal
 */
JPetPhysSignal JPetSimplePhysSignalReco::createPhysSignal(JPetRecoSignal& recoSignal)
{
  JPetPhysSignal physSignal;
  physSignal.setPhe(recoSignal.getCharge() * 1.0 + 0.0);
  physSignal.setQualityOfPhe(1.0);
  double time = recoSignal.getRecoTimesAtThreshold().begin()->second;
  JPetRawSignal rawSignal = recoSignal.getRawSignal();

  if (rawSignal.getNumberOfPoints(JPetSigCh::Leading) >= 2 && rawSignal.getNumberOfPoints(JPetSigCh::Trailing) >= 2)
  {
    int iNumPoints = rawSignal.getNumberOfPoints(JPetSigCh::Leading);
    std::vector<JPetSigCh> leadingPoints = rawSignal.getPoints(JPetSigCh::Leading, JPetRawSignal::ByThrValue);
    vector<float> vecTime(iNumPoints);
    vector<float> vecVolt(iNumPoints);
    for (int j = 0; j < iNumPoints; j++)
    {
      vecTime(j) = leadingPoints.at(j).getValue();
      vecVolt(j) = leadingPoints.at(j).getThreshold();
    }
    int alfa = getAlpha();
    float thr_sel = getThresholdSel();
    assert(thr_sel < 0);
    assert(alfa > 0);
    time = static_cast<double>(polynomialFit(vecTime, vecVolt, alfa, thr_sel));
  }
  physSignal.setTime(time);
  physSignal.setQualityOfTime(1.0);
  physSignal.setRecoSignal(recoSignal);
  return physSignal;
}

void JPetSimplePhysSignalReco::readConfigFileAndSetAlphaAndThreshParams(const char* filename)
{
  boost::property_tree::ptree content;
  try
  {
    read_json(filename, content);
    int alpha = content.get<int>("alpha");
    float thresholdSel = content.get<float>("thresholdSel");
    setAlpha(alpha);
    setThresholdSel(thresholdSel);
  }
  catch (const std::runtime_error& error)
  {
    std::string message = "Error opening config file. Error = " + std::string(error.what());
    std::cerr << message << std::endl;
  }
}


/*
# --------------------------------------------------
#
#  Digital-Systems Verifier (DSVerifier)
#
# --------------------------------------------------
#
#  Federal University of Amazonas - UFAM
#
#  Authors: Hussama Ismail - hussamaismail@gmail.com
#           Iury Bessa - iury.bessa@gmail.com
#           Lucas Cordeiro - lucasccordeiro@gmail.com
#           <if you help us, put your name here!> :)
#
# --------------------------------------------------
#
#  Usage:
#
#    ./dsverifier file.c or file.ss
#         --realization DFI
#         --property STABILITY
#         --x-size 10
#         --timeout 3600
#
#  Supported Properties:
#
#     for digital-systems in transfer function:
#        OVERFLOW, LIMIT_CYCLE, ZERO_INPUT_LIMIT_CYCLE,
#        TIMING, ERROR, STABILITY, and MINIMUM_PHASE
#
#     for digital-systems using closed-loop in transfer functions:
#        STABILITY_CLOSED_LOOP, LIMIT_CYCLE_CLOSED_LOOP,
#        and QUANTIZATION_ERROR_CLOSED_LOOP
#
#  Supported Realizations:
#     DFI, DFII, TDFII,
#     DDFI, DDFII, TDDFII.
#
# --------------------------------------------------
*/

#define DSVERIFIER_VERSION "2.0.1"

#include <iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include <cstdlib>
#include <complex>
#include <algorithm>
#include <cmath>
#include <exception>
#include <assert.h>
#include <iomanip>
#include <regex>
#include <fstream>
#include <streambuf>
#include <math.h>

typedef bool _Bool;

void __DSVERIFIER_assume(_Bool expression){
	/* nothing to do */
}

void __DSVERIFIER_assert(_Bool expression){
	if (expression == 0){
		throw 0;
	}
}

#include "bmc/core/definitions.h"
#include "bmc/core/fixed-point.h"
#include "bmc/core/util.h"
#include "bmc/core/delta-operator.h"
#include "bmc/core/initialization.h"

/* eigen dependencies */
#include <Eigen/Eigenvalues>
#include <unsupported/Eigen/Polynomials>
typedef Eigen::PolynomialSolver<double, Eigen::Dynamic>::RootType RootType;
typedef Eigen::PolynomialSolver<double, Eigen::Dynamic>::RootsType RootsType;

/* boost dependencies */
#include <boost/algorithm/string.hpp>

const char * properties [] = { "OVERFLOW", "LIMIT_CYCLE", "ZERO_INPUT_LIMIT_CYCLE", "ERROR", "TIMING", "TIMING_MSP430", "STABILITY", "STABILITY_CLOSED_LOOP", "LIMIT_CYCLE_CLOSED_LOOP", "QUANTIZATION_ERROR_CLOSED_LOOP", "MINIMUM_PHASE", "QUANTISATION_ERROR", "CONTROLLABILITY", "OBSERVABILITY", "LIMIT_CYCLE_STATE_SPACE"};
const char * realizations [] = { "DFI", "DFII", "TDFII", "DDFI", "DDFII", "TDDFII" };
const char * bmcs [] = { "ESBMC", "CBMC" };
const char * connections_mode [] = { "SERIES", "FEEDBACK" };

/* expected parameters */
unsigned int desired_x_size = 0;
std::string desired_filename;
std::string desired_property;
std::string desired_realization;
std::string desired_connection_mode;
std::string desired_timeout;
std::string desired_bmc;
std::string desired_function;
std::string desired_solver;
std::string desired_macro_parameters;
std::string desired_ds_id;

/* state space */
bool stateSpaceVerification = false;
bool closedloop = false;
bool translate = false;
digital_system_state_space _controller;
double desired_quantisation_limit = 0.0;
bool show_counterexample_data = false;

void help () {
	std::cout << std::endl;
	std::cout << "* * *           DSVerifier " << DSVERIFIER_VERSION << "          * * *" << std::endl;
	std::cout << "" << std::endl;
	std::cout << "Usage:                       Purpose:" << std::endl;
	std::cout << "" << std::endl;
	std::cout << "dsverifier [-h] [--help]           show help" << std::endl;
	std::cout << "dsverifier file.c ...              source file name" << std::endl;
	std::cout << "" << std::endl;
	std::cout << "Options:" << std::endl;
	std::cout << "" << std::endl;
	std::cout << "--realization <r>            set the realization for the Digital-System" << std::endl;
	std::cout << "                             (for Digital-Systems: DFI, DFII, TDFII, DDFI, DDFII, and TDDFII)" << std::endl;
	std::cout << "                             (for Digital-Systems in Closed-loop: DFI, DFII, and TDFII)" << std::endl;
	std::cout << "--property <p>               set the property to check in order to find violations" << std::endl;
	std::cout << "                             (for Digital-Systems: OVERFLOW, LIMIT_CYCLE, ZERO_INPUT_LIMIT_CYCLE, ERROR, TIMING, STABILITY, and MINIMUM_PHASE)" << std::endl;
	std::cout << "                             (for Digital-Systems in Closed-loop: STABILITY_CLOSED_LOOP, LIMIT_CYCLE_CLOSED_LOOP, and QUANTIZATION_ERROR_CLOSED_LOOP)" << std::endl;
	std::cout << "--x-size <k>                 set the bound of verification" << std::endl;
	std::cout << "--connection-mode <cm>       set the connection mode for the closed-loop system (SERIES or FEEDBACK)" << std::endl;
	std::cout << "--bmc <b>                    set the BMC back-end for DSVerifier (ESBMC or CBMC, default is CBMC)" << std::endl;
	std::cout << "--solver <s>                 use the specified solver in BMC back-end (e.g., boolector, z3, yices, cvc4, and minisat)" << std::endl;
	std::cout << "--timeout <t>                configure time limit, integer followed by {s,m,h} (for ESBMC only)" << std::endl;
	std::cout << "--tf2ss                      converts a transfer function representation of a given system to an equivalent state-space representation" << std::endl;
	std::cout << "--show-ce-data               shows initial states, inputs, and outputs extracted from counterexample" << std::endl;
	std::cout << "" << std::endl;
	exit(0);
}

void validate_function(std::string data){
	if (data.empty())
	  std::cout << "specify a function name" << std::endl;
	else
	  desired_function = data;
}

void validate_selected_bmc(std::string data){
	int length = (sizeof(bmcs)/sizeof(*bmcs));
	for(int i=0; i<length; i++){
		if (bmcs[i] == data){
			desired_bmc = data;
			break;
		}
	}
	if (desired_bmc.size() == 0){
		std::cout << "invalid bmc: " << data << std::endl;
		exit(1);
	}
}

void validate_selected_connection_mode(std::string data){
	int length = (sizeof(connections_mode)/sizeof(*connections_mode));
	for(int i=0; i<length; i++){
		if (connections_mode[i] == data){
			desired_connection_mode = data;
			break;
		}
	}
	if (desired_connection_mode.size() == 0){
		std::cout << "invalid connection-mode: " << data << std::endl;
		exit(1);
	}
}

void validate_selected_realization(std::string data){
	int length = (sizeof(realizations)/sizeof(*realizations));
	for(int i=0; i<length; i++){
		if (realizations[i] == data){
			desired_realization = data;
			break;
		}
	}
	if (desired_realization.size() == 0){
		std::cout << "invalid realization: " << data << std::endl;
		exit(1);
	}
}

void validate_selected_property(std::string data){
	int length = (sizeof(properties)/sizeof(*properties));
	for(int i=0; i<length; i++){
		if (properties[i] == data){
			desired_property = data;
			break;
		}
	}
	if (desired_property.size() == 0){
		std::cout << "invalid property: " << data << std::endl;
		exit(1);
	}
}

void validate_filename(std::string file){
	if (file == "--help" || file == "-h") {
		help();
	} else if(file.substr(file.size()-3, std::string::npos) != ".ss"){
		std::string::size_type loc = file.find(".c", 0 );
		if( loc == std::string::npos ) {
			std::cout << file << ": failed to figure out type of file" << std::endl;
			exit(1);
		}
	} else {
		stateSpaceVerification = true;
	}
	desired_filename = file;
}

void show_required_argument_message(std::string parameter){
	std::cerr << parameter << " option requires one argument." << std::endl;
	exit(1);
}

void check_required_parameters(){
	if (desired_bmc.size() == 0){
		desired_bmc = "CBMC";
	}
}

void bind_parameters(int argc, char* argv[]){
	if (argc == 1) {
		help();
		exit(1);
	}
	validate_filename(argv[1]);
	for (int i = 2; i < argc; ++i) {
		if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
			help();
		} else if (std::string(argv[i]) == "--realization") {
			if (i + 1 < argc) {
				validate_selected_realization(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--property") {
			if (i + 1 < argc) {
				validate_selected_property(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--x-size") {
			if (i + 1 < argc) {
				desired_x_size = std::atoi(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--connection-mode") {
			if (i + 1 < argc) {
				validate_selected_connection_mode(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--timeout") {
			if (i + 1 < argc) {
				desired_timeout = argv[++i];
			} else {
				show_required_argument_message(argv[i]);
			}
		}else if (std::string(argv[i]) == "--bmc") {
			if (i + 1 < argc) {
				validate_selected_bmc(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		}else if (std::string(argv[i]) == "--function") {
			if (i + 1 < argc) {
				validate_function(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		}else if (std::string(argv[i]) == "--solver") {
			if (i + 1 < argc) {
				desired_solver = argv[++i];
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--limit") {
			if (i + 1 < argc) {
				desired_quantisation_limit = std::stod(argv[++i]);
			} else {
				show_required_argument_message(argv[i]);
			}
		} else if (std::string(argv[i]) == "--closed-loop") {
			closedloop = true;
		} else if (std::string(argv[i]) == "--tf2ss") {
			translate = true;
		} else if (std::string(argv[i]) == "--show-ce-data"){
			show_counterexample_data = true;
		} else {
			/* get macro parameters */
			std::string parameter = argv[i];
			std::string::size_type loc = parameter.find("-D", 0 );
			if( loc != std::string::npos ) {
				desired_macro_parameters += " " + parameter;
				/* check if there is an desired benchmark */
				std::string::size_type find_desired_ds_id = parameter.find("-DDS_ID=", 0 );
				if( find_desired_ds_id != std::string::npos ) {
					std::vector<std::string> parts;
					boost::split(parts, parameter, boost::is_any_of("="));
					desired_ds_id = "DS_ID==" + parts.at(1);
				}
			}else{
				std::cout << "invalid parameter: " << argv[i] << std::endl;
				exit(1);
			}
		}
	}
	check_required_parameters();
}

std::string execute_command_line(std::string command){
	FILE* pipe = popen(command.c_str(), "r");
	if (!pipe) return "ERROR";
	char buffer[128];
	std::string result = "";
	while(!feof(pipe)) {
		if(fgets(buffer, 128, pipe) != NULL){
			std::cout << buffer;
			result += buffer;
		}
	}
	pclose(pipe);
	return result;
}

std::string prepare_bmc_command_line(){
	char * dsverifier_home;
  dsverifier_home = getenv("DSVERIFIER_HOME");
  if (dsverifier_home == NULL){
		std::cout << std::endl << "[ERROR] - You must set DSVERIFIER_HOME environment variable." << std::endl;
		exit(1);
	}
	std::string bmc_path = std::string(dsverifier_home) + "/bmc";
	std::string model_checker_path = std::string(dsverifier_home) + "/model-checker";
	std::string command_line;
	if (desired_bmc == "ESBMC"){
		command_line = model_checker_path + "/esbmc " + desired_filename + " --no-bounds-check --no-pointer-check --no-div-by-zero-check -DBMC=ESBMC -I " + bmc_path;
		if (desired_timeout.size() > 0){
			command_line += " --timeout " + desired_timeout;
		}
	}else if (desired_bmc == "CBMC"){
		command_line =  model_checker_path + "/cbmc " + desired_filename + " --fixedbv -DBMC=CBMC -I " + bmc_path;
	}
	if (desired_function.size() > 0){
		command_line += " --function " + desired_function;
	}
	if (desired_solver.size() > 0){
		command_line += " --" + desired_solver;
	}
	if (desired_realization.size() > 0){
		command_line += " -DREALIZATION=" + desired_realization;
	}
	if (desired_property.size() > 0){
		command_line += " -DPROPERTY=" + desired_property;
	}
	if (desired_connection_mode.size() > 0){
		command_line += " -DCONNECTION_MODE=" + desired_connection_mode;
	}
	if (desired_x_size > 0){
		command_line += " -DX_SIZE=" + std::to_string(desired_x_size);
	}
	command_line += desired_macro_parameters;
	return command_line;
}

std::string prepare_bmc_command_line_ss(){
	std::string command_line;
	if (desired_bmc == "ESBMC"){
		command_line = "esbmc input.c --no-bounds-check --no-pointer-check --no-div-by-zero-check -DBMC=ESBMC";
		if (desired_timeout.size() > 0){
			command_line += " --timeout " + desired_timeout;
		}
	}else if (desired_bmc == "CBMC"){
		command_line = "cbmc --fixedbv input.c -DBMC=CBMC";
	}
	if (desired_solver.size() > 0){
		command_line += " --" + desired_solver;
	}
	if (desired_realization.size() > 0){
		command_line += " -DREALIZATION=" + desired_realization;
	}
	if (desired_property.size() > 0){
		command_line += " -DPROPERTY=" + desired_property;
	}
	if (desired_x_size > 0){
		command_line += " -DK_SIZE=" + std::to_string(desired_x_size);
	}
	command_line += desired_macro_parameters;
	return command_line;
}
digital_system ds;
implementation impl;

/* print array elements */
void cplus_print_fxp_array_elements(const char * name, fxp_t * v, int n){
	printf("%s = {", name);
	int i;
	for(i=0; i < n; i++){
		printf(" %jd ", v[i]);
	}
	printf("}\n");
}

/* print array elements */
void cplus_print_array_elements(const char * name, double * v, int n){
	printf("%s = {", name);
	int i;
	for(i=0; i < n; i++){
		printf(" %.16f ", v[i]);
	}
	printf("}\n");
}

/* print array elements */
void cplus_print_array_elements_ignoring_empty(const char * name, double * v, int n){
	if (n > 0){
		cplus_print_array_elements(name,v,n);
	}
}

int get_fxp_value(std::string exp){
	std::vector<std::string> tokens;
    boost::split(tokens, exp,boost::is_any_of("="));
    return std::atoi(tokens[1].c_str());
};

void extract_regexp_data_for_vector(std::string src, std::regex & regexp, std::vector<double> & vector, unsigned int & factor){
	std::sregex_iterator next(src.begin(), src.end(), regexp);
	std::sregex_iterator end;
	while (next != end) {
		std::smatch match = *next;
		double value = (double) get_fxp_value(match.str()) / (double) factor;
		vector.push_back(value);
		next++;
	}
};

void print_counterexample_data(std::string counterexample){
	std::vector<double> numerator;
	std::vector<double> denominator;
	std::vector<double> inputs;
	std::vector<double> outputs;
	std::vector<double> initial_states;
	unsigned int factor = pow(2, impl.frac_bits);

	std::size_t verification_failed = counterexample.find("VERIFICATION FAILED");
	if (verification_failed == std::string::npos){
		return;
    }

	if (desired_bmc == "ESBMC") {
		std::cout << std::endl << "[INFO] Unfortunately, It was not able to extract the counterexample data for this model checker. :(" << std::endl;
		return;
	}

	try {

		/* process quantized numerator data */
		std::regex num_fxp_regexp("b_fxp\\[[0-9]\\]=-?[0-9]+");
		extract_regexp_data_for_vector(counterexample, num_fxp_regexp, numerator, factor);

		/* process quantized denominator data */
		std::regex den_fxp_regexp("a_fxp\\[[0-9]\\]=-?[0-9]+");
		extract_regexp_data_for_vector(counterexample, den_fxp_regexp, denominator, factor);

		/* process input data */
		std::regex input_regexp(" x\\[[0-9]\\]=-?[0-9]+");
		extract_regexp_data_for_vector(counterexample, input_regexp, inputs, factor);

		/* process output data */
		std::regex output_regexp("y\\[[0-9]\\]=-?[0-9]+");
		extract_regexp_data_for_vector(counterexample, output_regexp, outputs, factor);

		/* process initial states data */
		std::regex initial_states_regexp("y0\\[[0-9]\\]=-?[0-9]+");
		extract_regexp_data_for_vector(counterexample, initial_states_regexp, initial_states, factor);
		if (initial_states.size() == 0){
			std::regex initial_states_regexp_df2("w0\\[[0-9]\\]=-?[0-9]+");
			extract_regexp_data_for_vector(counterexample, initial_states_regexp_df2, initial_states, factor);
		}

		std::cout << std::endl << "Counterexample Data:" << std::endl;
		cplus_print_array_elements_ignoring_empty("  Numerator ", ds.b, ds.b_size);
		cplus_print_array_elements_ignoring_empty("  Denominator ", ds.a, ds.a_size);
    std::cout << "  Sample Time = " << ds.sample_time << std::endl;
		std::cout << "  Implementation = " << "<" << impl.int_bits << "," << impl.frac_bits << ">" << std::endl;
		cplus_print_array_elements_ignoring_empty("  Numerator (fixed-point)", &numerator[0], numerator.size());
		cplus_print_array_elements_ignoring_empty("  Denominator (fixed-point)", &denominator[0], denominator.size());
		std::cout << "  Realization = " << desired_realization << std::endl;
    std::cout << "  Dynamic Range = " << "[ " << impl.min << ", " << impl.max << " ]" << std::endl;
		cplus_print_array_elements_ignoring_empty("  Initial States", &initial_states[0], initial_states.size());
		cplus_print_array_elements_ignoring_empty("  Inputs", &inputs[0], inputs.size());
		cplus_print_array_elements_ignoring_empty("  Outputs", &outputs[inputs.size()], outputs.size() - inputs.size());

	} catch (std::regex_error& e) {
		std::cout << "[ERROR] It was not able to process the counterexample data. :(" << std::endl;
		exit(1);
	}
}

int get_roots_from_polynomial(double polynomial[], int poly_size, std::vector<RootType> & roots){

	unsigned int size = poly_size;

	/* coefficients */
	std::vector<double> coefficients_vector;
	for (int i=0; i< poly_size; i++){
		coefficients_vector.push_back(polynomial[i]);
	}

	/* remove leading zeros */
	std::vector<double>::iterator it=coefficients_vector.begin();
	while(it != coefficients_vector.end()){
		if(*it != 0.0)
			break;
		it=coefficients_vector.erase(it);
	}

	size=coefficients_vector.size();

	/* check if there is any element left on the vector */
	if(!size){
		std::cout << std::endl << "No remaining elements in polynomial vector";
		throw std::runtime_error ("tla");
	}

	/* check the polynomial order */
	if (coefficients_vector.size() >= 3){

		Eigen::VectorXd coefficients(coefficients_vector.size());

		/* copy elements from the list to the array - insert in reverse order */
		unsigned int i=0;
		for(it=coefficients_vector.begin();
				it!=coefficients_vector.end();
				++it, ++i){
			coefficients[size-i-1] = *it;
		}

		/* eigen solver object */
		Eigen::PolynomialSolver<double, Eigen::Dynamic> solver;

		/* solve denominator using QR decomposition */
		solver.compute(coefficients);

		RootsType solver_roots = solver.roots();
		for(unsigned int i=0; i<solver_roots.rows(); ++i){
			roots.push_back(solver_roots[i]);
		}

	} else if (coefficients_vector.size() == 2){

		double root = - coefficients_vector.at(1) / coefficients_vector.at(0);
		roots.push_back(root);

	} else {
		/* nothing to do */
	}

	return 0;
}

bool check_delta_stability_margin(std::vector<RootType> roots){
	std::cout << "checking delta stability margin" << std::endl;
	bool stable = true;
	for(unsigned int i=0; i<roots.size(); i++){
		std::complex<double> eig = roots.at(i);
		eig.real(eig.real() * impl.delta);
		eig.imag(eig.imag() * impl.delta);
		eig.real(eig.real() + 1);
		if ((std::abs(eig) < 1) == false){
			stable = false;
			break;
		}
	}
	return stable;
}

bool check_shift_stability_margin(std::vector<RootType> roots){
	std::cout << "checking shift stability margin" << std::endl;
	bool stable = true;
	for(unsigned int i=0; i<roots.size(); i++){
		std::complex<double> eig = roots.at(i);
		if ((std::abs(eig) < 1) == false){
			stable = false;
			break;
		}
	}
	return stable;
}

void show_delta_not_representable(){
	std::cout << "[EXCEPTION] Does not possible to represent this value in delta using this precision" << std::endl;
}

void show_verification_successful(){
	std::cout << std::endl << "VERIFICATION SUCCESSFUL" << std::endl;
}

void show_verification_failed(){
	std::cout << std::endl << "VERIFICATION FAILED" << std::endl;
}

void show_implementation_parameters(){
	std::cout << std::endl << "implementation int_bits: " << impl.int_bits << std::endl;
	std::cout << "implementation frac_bits: " << impl.frac_bits << std::endl;
	std::cout << "implementation delta: " << impl.delta << std::endl;
}

void check_stability_shift_domain_using_jury(){
	show_implementation_parameters();
	std::cout << std::endl;
	double sa_fxp[ds.a_size];
	cplus_print_array_elements("original denominator", ds.a, ds.a_size);
	fxp_t a_fxp[ds.a_size];
	fxp_double_to_fxp_array(ds.a, a_fxp, ds.a_size);
	fxp_to_double_array(sa_fxp, a_fxp, ds.a_size);
	cplus_print_array_elements("quantized denominator", sa_fxp, ds.a_size);
	bool is_stable = check_stability(sa_fxp, ds.a_size);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

void check_minimum_phase_shift_domain_using_jury(){
	show_implementation_parameters();
	std::cout << std::endl;
	double sb_fxp[ds.b_size];
	cplus_print_array_elements("original numerator", ds.b, ds.b_size);
	fxp_t b_fxp[ds.b_size];
	fxp_double_to_fxp_array(ds.b, b_fxp, ds.b_size);
	fxp_to_double_array(sb_fxp, b_fxp, ds.b_size);
	cplus_print_array_elements("quantized denominator", sb_fxp, ds.b_size);
	bool is_stable = check_stability(sb_fxp, ds.b_size);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

void check_stability_shift_domain_using_eigen(){
	show_implementation_parameters();
	std::cout << std::endl;
	double sa_fxp[ds.a_size];
	cplus_print_array_elements("original denominator", ds.a, ds.a_size);
	fxp_t a_fxp[ds.a_size];
	fxp_double_to_fxp_array(ds.a, a_fxp, ds.a_size);
	fxp_to_double_array(sa_fxp, a_fxp, ds.a_size);
	cplus_print_array_elements("quantized denominator", sa_fxp, ds.a_size);
	std::vector<RootType> poly_roots;
	get_roots_from_polynomial(sa_fxp, ds.a_size, poly_roots);
	bool is_stable = check_shift_stability_margin(poly_roots);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

void check_minimum_phase_shift_domain(){
	show_implementation_parameters();
	std::cout << std::endl;
	double sb_fxp[ds.b_size];
	cplus_print_array_elements("original numerator", ds.b, ds.b_size);
	fxp_t b_fxp[ds.b_size];
	fxp_double_to_fxp_array(ds.b, b_fxp, ds.b_size);
	fxp_to_double_array(sb_fxp, b_fxp, ds.b_size);
	cplus_print_array_elements("quantized numerator", sb_fxp, ds.b_size);
	bool is_stable = check_stability(sb_fxp, ds.b_size);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

void check_stability_delta_domain(){
	show_implementation_parameters();
	std::cout << std::endl;
	double db[ds.b_size];
	double da[ds.a_size];
	fxp_t a_fxp[ds.a_size];
	cplus_print_array_elements("original denominator", ds.a, ds.a_size);
	fxp_double_to_fxp_array(ds.a, a_fxp, ds.a_size);
	get_delta_transfer_function_with_base(ds.b, db, ds.b_size, ds.a, da, ds.a_size, impl.delta);
	cplus_print_array_elements("delta denominator", da, ds.a_size);
	fxp_t da_fxp[ds.a_size];
	try{
		fxp_double_to_fxp_array(da, da_fxp, ds.a_size);
	} catch (int e){
		std::cout << "an fixed-point arithmetic overflow occurs after delta transformation" << std::endl;
		show_verification_failed();
		exit(1);
	}
	if ((da[0] != 0) && (da_fxp[0] == 0)){
		std::cout << std::endl;
		std::cout << "ds.a[0] = "<< std::to_string(da[0]) << " ----> " << std::to_string(da_fxp[0]) << std::endl;
		show_delta_not_representable();
		exit(1);
	}
	double da_qtz[ds.a_size];
	fxp_to_double_array(da_qtz, da_fxp, ds.a_size);
	cplus_print_array_elements("quantized delta denominator", da_qtz, ds.a_size);
	std::vector<RootType> poly_roots;
	get_roots_from_polynomial(da_qtz, ds.a_size, poly_roots);
	bool is_stable = check_delta_stability_margin(poly_roots);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

bool check_if_file_exists (const std::string & name) {
	if (FILE *file = fopen(name.c_str(), "r")) {
		fclose(file);
		return true;
	} else {
		return false;
	}
}

void check_minimum_phase_delta_domain(){
	show_implementation_parameters();
	std::cout << std::endl;
	double db[ds.b_size];
	double da[ds.a_size];
	cplus_print_array_elements("original numerator", ds.b, ds.b_size);
	get_delta_transfer_function_with_base(ds.b, db, ds.b_size, ds.a, da, ds.a_size, impl.delta);
	cplus_print_array_elements("delta numerator", db, ds.b_size);
	fxp_t db_fxp[ds.b_size];
	fxp_double_to_fxp_array(db, db_fxp, ds.b_size);
	if ((db[0] != 0) && (db_fxp[0] == 0)){
		std::cout << std::endl;
		std::cout << "ds.b[0] = "<< std::to_string(db[0]) << " ----> " << std::to_string(db_fxp[0]) << std::endl;
		show_delta_not_representable();
		exit(1);
	}
	double db_qtz[ds.b_size];
	fxp_to_double_array(db_qtz, db_fxp, ds.b_size);
	cplus_print_array_elements("quantized delta numerator", db_qtz, ds.b_size);
	std::vector<RootType> poly_roots;
	get_roots_from_polynomial(db_qtz, ds.b_size, poly_roots);
	bool is_stable = check_delta_stability_margin(poly_roots);
	if (is_stable){
		show_verification_successful();
	}else{
		show_verification_failed();
	}
}

void check_state_space_stability(){

	Eigen::MatrixXd matrixA = Eigen::MatrixXd::Ones(_controller.nStates,_controller.nStates);
	int i, j;
	for(i=0; i<_controller.nStates;i++){
		for(j=0; j<_controller.nStates;j++){
			matrixA(i,j) = _controller.A[i][j]; //fxp_double_to_fxp(A[i][j]);
		}
	}

	std::complex< double > lambda;
	std::complex< double > margem(1,0);
	for(i = 0; i < (matrixA.count() / 2); i++ ){
		lambda = matrixA.eigenvalues()[i];
		//std::cout << "abs(lambda): " << std::abs(lambda) << std::endl;
		double v = std::abs(lambda);
		if( v >= 1 ){
			std::cout << "unstable" << std::endl;
			exit(0);
		}
	}
	std::cout << "stable" << std::endl;
}

void check_file_exists(){
	/* check if the specified file exists */
	if (check_if_file_exists(desired_filename) == false){
		std::cout << "file " << desired_filename << ": failed to open input file" << std::endl;
		exit(1);
	}
}

std::string replace_all_string(std::string str, const std::string& from, const std::string& to) {
	size_t start_pos = 0;
	while((start_pos = str.find(from, start_pos)) != std::string::npos) {
		str.replace(start_pos, from.length(), to);
		start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
	}
	return str;
}

void extract_data_from_file(){

	std::ifstream verification_file(desired_filename);
	bool ds_id_found = false;

	for(std::string current_line; getline( verification_file, current_line );){

		/* removing whitespaces */
		current_line = replace_all_string(current_line, " ", "");
		current_line = replace_all_string(current_line, "\t", "");
		/* check the last comma, and remove it */
		if (current_line.back() == ','){
			current_line.pop_back();
		}

		/* check if there is an desired ds_id and find the region*/
		if (desired_ds_id.size() != 0){
			std::string::size_type find_desired_ds_id = current_line.find(desired_ds_id, 0);
			if (ds_id_found == false){
				if (find_desired_ds_id != std::string::npos){
					ds_id_found = true;
				} else {
					continue; /* go to next line */
				}
			}
		}

		std::string::size_type ds_a = current_line.find(".a=", 0);
		if (ds_a != std::string::npos){
			std::vector<std::string> coefficients;
			boost::split(coefficients, current_line, boost::is_any_of(","));
			for(int i=0; i< coefficients.size(); i++){
				std::string coefficient = coefficients.at(i);
				coefficient = replace_all_string(coefficient, ".a={", "");
				coefficient = replace_all_string(coefficient, "}", "");
				ds.a[i] = std::atof(coefficient.c_str());
				ds.a_size = coefficients.size();
			}
			continue;
		}
		std::string::size_type ds_b = current_line.find(".b=", 0);
		if (ds_b != std::string::npos){
			std::vector<std::string> coefficients;
			boost::split(coefficients, current_line, boost::is_any_of(","));
			for(int i=0; i< coefficients.size(); i++){
				std::string coefficient = coefficients.at(i);
				coefficient = replace_all_string(coefficient, ".b={", "");
				coefficient = replace_all_string(coefficient, "}", "");
				ds.b[i] = std::atof(coefficient.c_str());
				ds.b_size = coefficients.size();
			}
			continue;
		}
    std::string::size_type ds_sample_time = current_line.find(".sample_time", 0);
		if (ds_sample_time != std::string::npos){
			current_line = replace_all_string(current_line, ".sample_time=", "");
			ds.sample_time = std::atof(current_line.c_str());
			continue;
		}
    std::string::size_type impl_int_bits = current_line.find(".int_bits", 0);
		if (impl_int_bits != std::string::npos){
			current_line = replace_all_string(current_line, ".int_bits=", "");
			impl.int_bits = std::atoi(current_line.c_str());
			continue;
		}
		std::string::size_type impl_frac_bits = current_line.find(".frac_bits", 0);
		if (impl_frac_bits != std::string::npos){
			current_line = replace_all_string(current_line, ".frac_bits=", "");
			impl.frac_bits = std::atoi(current_line.c_str());
			continue;
		}
    std::string::size_type impl_min = current_line.find(".min", 0);
		if (impl_min != std::string::npos){
			current_line = replace_all_string(current_line, ".min=", "");
			impl.min = std::atof(current_line.c_str());
			continue;
		}
    std::string::size_type impl_max = current_line.find(".max", 0);
    if (impl_max != std::string::npos){
			current_line = replace_all_string(current_line, ".max=", "");
			impl.max = std::atof(current_line.c_str());
			continue;
		}
		std::string::size_type impl_delta = current_line.find(".delta", 0);
		if (impl_delta != std::string::npos){
			current_line = replace_all_string(current_line, ".delta=", "");
			impl.delta = std::atof(current_line.c_str());
			continue;
		}
	}
}

void extract_data_from_ss_file(){
	std::ifstream verification_file(desired_filename);
	std::string current_line;
	getline( verification_file, current_line );

	/* getting implementation specifications */
	std::string str_bits;
	int i;
	for(i = 0; current_line[i] != '<';i++);
	i++;
	for(; current_line[i] != ','; i++)
		str_bits.push_back(current_line[i]);
	impl.int_bits = std::stoi(str_bits);
	str_bits.clear();
	i++;
	for(; current_line[i] != '>'; i++)
		str_bits.push_back(current_line[i]);
	impl.frac_bits = std::stoi(str_bits);
	str_bits.clear();

	getline( verification_file, current_line ); // states

	for(i = 0; current_line[i] != '=';i++){}
		i++; i++;
		for(; current_line[i] != ';'; i++)
			str_bits.push_back(current_line[i]);
		int states = std::stoi(str_bits);
		str_bits.clear();

	getline( verification_file, current_line ); // inputs

	for(i = 0; current_line[i] != '=';i++){}
		i++; i++;
		for(; current_line[i] != ';'; i++)
			str_bits.push_back(current_line[i]);
		int inputs = std::stoi(str_bits);
		str_bits.clear();

	getline( verification_file, current_line ); // outputs

	for(i = 0; current_line[i] != '=';i++){}
		i++; i++;
		for(; current_line[i] != ';'; i++)
			str_bits.push_back(current_line[i]);
		int outputs = std::stoi(str_bits);
		str_bits.clear();

	/* Updating _controller */
	_controller.nStates = states;
	_controller.nInputs = inputs;
	_controller.nOutputs = outputs;

	/* initialising matrix A */

	getline( verification_file, current_line ); // matrix A
	str_bits.clear();
	for(i = 0; current_line[i] != '[';i++);
	i++;
	int lines = 0;
	int columns = 0;
	for(; current_line[i] != ']'; i++){
		if(current_line[i] != ',' && current_line[i] != ';'){
			str_bits.push_back(current_line[i]);
		} else if(current_line[i] == ';'){
			_controller.A[lines][columns] = std::stod(str_bits);
			lines++;
			columns = 0;
			str_bits.clear();
		} else {
			_controller.A[lines][columns] = std::stod(str_bits);
			columns++;
			str_bits.clear();
		}
	}
	_controller.A[lines][columns] = std::stod(str_bits);
	str_bits.clear();

	/* initialising matrix B */

	getline( verification_file, current_line ); // matrix B
	str_bits.clear();
	for(i = 0; current_line[i] != '[';i++);
	i++;
	lines = 0;
	columns = 0;
	for(; current_line[i] != ']'; i++){
		if(current_line[i] != ',' && current_line[i] != ';'){
			str_bits.push_back(current_line[i]);
		} else if(current_line[i] == ';'){
			_controller.B[lines][columns] = std::stod(str_bits);
			lines++;
			columns = 0;
			str_bits.clear();
		} else {
			_controller.B[lines][columns] = std::stod(str_bits);
			columns++;
			str_bits.clear();
		}
	}
	_controller.B[lines][columns] = std::stod(str_bits);
	str_bits.clear();

	/* initialising matrix C */

	getline( verification_file, current_line ); // matrix C
	str_bits.clear();
	for(i = 0; current_line[i] != '[';i++);
	i++;
	lines = 0;
	columns = 0;
	for(; current_line[i] != ']'; i++){
		if(current_line[i] != ',' && current_line[i] != ';'){
			str_bits.push_back(current_line[i]);
		} else if(current_line[i] == ';'){
			_controller.C[lines][columns] = std::stod(str_bits);
			lines++;
			columns = 0;
			str_bits.clear();
		} else {
			_controller.C[lines][columns] = std::stod(str_bits);
			columns++;
			str_bits.clear();
		}
	}
	_controller.C[lines][columns] = std::stod(str_bits);
	str_bits.clear();

	/* initialising matrix D */

	getline( verification_file, current_line ); // matrix D
	str_bits.clear();
	for(i = 0; current_line[i] != '[';i++);
	i++;
	lines = 0;
	columns = 0;
	for(; current_line[i] != ']'; i++){
		if(current_line[i] != ',' && current_line[i] != ';'){
			str_bits.push_back(current_line[i]);
		} else if(current_line[i] == ';'){
			_controller.D[lines][columns] = std::stod(str_bits);
			lines++;
			columns = 0;
			str_bits.clear();
		} else {
			_controller.D[lines][columns] = std::stod(str_bits);
			columns++;
			str_bits.clear();
		}
	}
	_controller.D[lines][columns] = std::stod(str_bits);
	str_bits.clear();

	/* initialising matrix Inputs */

	getline( verification_file, current_line ); // matrix inputs
	str_bits.clear();
	for(i = 0; current_line[i] != '[';i++);
	i++;
	lines = 0;
	columns = 0;
	for(; current_line[i] != ']'; i++){
		if(current_line[i] != ',' && current_line[i] != ';'){
			str_bits.push_back(current_line[i]);
		} else if(current_line[i] == ';'){
			_controller.inputs[lines][columns] = std::stod(str_bits);
			lines++;
			columns = 0;
			str_bits.clear();
		} else {
			_controller.inputs[lines][columns] = std::stod(str_bits);
			columns++;
			str_bits.clear();
		}
	}
	_controller.inputs[lines][columns] = std::stod(str_bits);
	str_bits.clear();

	if(closedloop){
		getline( verification_file, current_line ); // matrix controller
		str_bits.clear();
		for(i = 0; current_line[i] != '[';i++);
		i++;
		lines = 0;
		columns = 0;
		for(; current_line[i] != ']'; i++){
			if(current_line[i] != ',' && current_line[i] != ';'){
				str_bits.push_back(current_line[i]);
			} else if(current_line[i] == ';'){
				_controller.K[lines][columns] = std::stod(str_bits);
				lines++;
				columns = 0;
				str_bits.clear();
			} else {
				_controller.K[lines][columns] = std::stod(str_bits);
				columns++;
				str_bits.clear();
			}
		}
		_controller.K[lines][columns] = std::stod(str_bits);
	}

	//print_matrix(_controller.K,1,states);
	//print_matrix(_controller.B,states,inputs);
	//print_matrix(_controller.C,outputs,states);
	//print_matrix(_controller.D,outputs,inputs);
}

void state_space_parser(){
	std::string verification_file;

	std::string tmp;
	std::ostringstream cf_value_precision;
	unsigned int i, j;
	cf_value_precision.precision(64);

	verification_file = "#include \"bmc/dsverifier.h\"\n digital_system_state_space _controller;\n implementation impl = {\n .int_bits = ";
	verification_file.append(std::to_string(impl.int_bits));
	verification_file.append(",\n .frac_bits = ");
	verification_file.append(std::to_string(impl.frac_bits));
	verification_file.append("};\n int nStates = ");
	verification_file.append(std::to_string(_controller.nStates));
	verification_file.append(";\n int nInputs = ");
	verification_file.append(std::to_string(_controller.nInputs));
	verification_file.append(";\n int nOutputs = ");
	verification_file.append(std::to_string(_controller.nOutputs));
	verification_file.append(";\n double error_limit = ");
	cf_value_precision  << std::fixed << desired_quantisation_limit;
	verification_file.append(cf_value_precision.str());
	verification_file.append(";\n void initials(){\n");

	for (i=0; i<_controller.nStates; i++) {
		for (j=0; j<_controller.nStates; j++) {
			verification_file.append("\t_controller.A[");
			verification_file.append(std::to_string(i));
			verification_file.append("][");
			verification_file.append(std::to_string(j));
			verification_file.append("] = ");
			cf_value_precision.str(std::string());
			cf_value_precision << std::fixed << _controller.A[i][j];
			verification_file.append(cf_value_precision.str());
			verification_file.append(";\n");
		}
	}

	for (i=0; i<_controller.nStates; i++) {
		for (j=0; j<_controller.nInputs; j++) {
			verification_file.append("\t_controller.B[");
			verification_file.append(std::to_string(i));
			verification_file.append("][");
			verification_file.append(std::to_string(j));
			verification_file.append("] = ");
			cf_value_precision.str(std::string());
			cf_value_precision << std::fixed << _controller.B[i][j];
			verification_file.append(cf_value_precision.str());
			verification_file.append(";\n");
		}
	}

	for (i=0; i<_controller.nOutputs; i++) {
		for (j=0; j<_controller.nStates; j++) {
			verification_file.append("\t_controller.C[");
			verification_file.append(std::to_string(i));
			verification_file.append("][");
			verification_file.append(std::to_string(j));
			verification_file.append("] = ");
			cf_value_precision.str(std::string());
			cf_value_precision << std::fixed << _controller.C[i][j];
			verification_file.append(cf_value_precision.str());
			verification_file.append(";\n");
		}
	}

	for (i=0; i<_controller.nOutputs; i++) {
		for (j=0; j<_controller.nInputs; j++) {
			verification_file.append("\t_controller.D[");
			verification_file.append(std::to_string(i));
			verification_file.append("][");
			verification_file.append(std::to_string(j));
			verification_file.append("] = ");
			cf_value_precision.str(std::string());
			cf_value_precision << std::fixed << _controller.D[i][j];
			verification_file.append(cf_value_precision.str());
			verification_file.append(";\n");
		}
	}

	for (i=0; i<_controller.nInputs; i++) {
		for (j=0; j < 1; j++) {
			verification_file.append("\t_controller.inputs[");
			verification_file.append(std::to_string(i));
			verification_file.append("][");
			verification_file.append(std::to_string(j));
			verification_file.append("] = ");
			cf_value_precision.str(std::string());
			cf_value_precision << std::fixed << _controller.inputs[i][j];
			verification_file.append(cf_value_precision.str());
			verification_file.append(";\n");
		}
	}

	verification_file.append("}");

	std::ofstream myfile ("input.c");
	if (myfile.is_open())
	{
		myfile << verification_file;
		myfile.close();
	}
	else std::cout << "Unable to open file";
}

void closed_loop(){

	double result1[LIMIT][LIMIT];

	int i, j, k;
	for(i=0; i<LIMIT;i++)
		for(j=0; j<LIMIT;j++)
			result1[i][j]=0;

	double_matrix_multiplication(_controller.nStates,_controller.nInputs,1,_controller.nStates,_controller.B,_controller.K,result1);

	double_sub_matrix(_controller.nStates,
			_controller.nStates,
			_controller.A,
			result1,
			_controller.A);

	for(i=0; i<LIMIT;i++)
		for(j=0; j<LIMIT;j++)
			result1[i][j]=0;

	double_matrix_multiplication(_controller.nOutputs,_controller.nInputs,1,_controller.nStates,_controller.D,_controller.K,result1);

	double_sub_matrix(_controller.nOutputs,
			_controller.nStates,
			_controller.C,
			result1,
			_controller.C);
}

/*
 * This function converts a transfer function into a state space
 * It only works for SISO systems
 * */
void tf2ss(){
	unsigned int i, j;

	_controller.nStates = ds.b_size - 1;
	_controller.nInputs = 1;
	_controller.nOutputs = 1;

	for (i=0; i<_controller.nStates - 1; i++) {
		for (j=0; j<_controller.nStates; j++) {
			if(j == i + 1){
				_controller.A[i][j] = 1;
			}else{
				_controller.A[i][j] = 0;
			}
		}
	}

	for (j=0; j<_controller.nStates; j++) {
		_controller.A[_controller.nStates - 1][j] = - ds.b[_controller.nStates - j];
	}

	for (i=0; i<_controller.nStates - 1; i++) {
		for (j=0; j<_controller.nInputs; j++) {
			_controller.B[i][j] = 0;
		}
	}
	_controller.B[_controller.nStates - 1][0] = 1; /* for SISO systems */

	for (i=0; i<_controller.nOutputs; i++) {
		for (j=0; j<_controller.nStates; j++) {
			_controller.C[i][j] = ds.a[_controller.nStates - j] - (ds.b[_controller.nStates - j] * ds.a[0]);
		}
	}

	for (i=0; i<_controller.nOutputs; i++) {
		for (j=0; j<_controller.nInputs; j++) {
			_controller.D[i][j] = ds.a[0];
		}
	}
}

/* main function */
int main(int argc, char* argv[]){

	/* without overflow */
	OVERFLOW_MODE = NONE;

	bind_parameters(argc, argv);

	if(desired_property == "QUANTISATION_ERROR" && desired_quantisation_limit == 0.0)
		show_required_argument_message("QUANTISATION_ERROR");

	check_file_exists();

	std::cout << "Running: Digital Systems Verifier (DSVerifier)" << std::endl;

	if(translate){
		extract_data_from_file();
		tf2ss();
		state_space_parser();
		exit(0);
	}

	if (stateSpaceVerification){
		extract_data_from_ss_file();
		if(closedloop)
			closed_loop();
		if( desired_property == "STABILITY" ){
			std::cout << "Checking stability..." << std::endl;
			check_state_space_stability();
			exit(0);
		} else if( desired_property == "QUANTISATION_ERROR" ) {
			state_space_parser();
			std::string command_line = prepare_bmc_command_line_ss();
			std::cout << "Back-end Verification: " << command_line << std::endl;
			execute_command_line(command_line);
			exit(0);
		} else if( desired_property == "CONTROLLABILITY" ) {
			state_space_parser();
			std::string command_line = prepare_bmc_command_line_ss();
			std::cout << "Back-end Verification: " << command_line << std::endl;
			execute_command_line(command_line);
			exit(0);
		} else if( desired_property == "OBSERVABILITY" ) {
			state_space_parser();
			std::string command_line = prepare_bmc_command_line_ss();
			std::cout << "Back-end Verification: " << command_line << std::endl;
			execute_command_line(command_line);
			exit(0);
		} else if( desired_property == "LIMIT_CYCLE_STATE_SPACE" ) {
			state_space_parser();
			std::string command_line = prepare_bmc_command_line_ss();
			std::cout << "Back-end Verification: " << command_line << std::endl;
			execute_command_line(command_line);
			exit(0);
		} else {
			std::cout << "Nothing to check!" << std::endl;
		}
	} else {
		bool is_delta_realization = (desired_realization == "DDFI" || desired_realization == "DDFII" || desired_realization == "TDDFII");
		bool is_restricted_property = (desired_property == "STABILITY" || desired_property == "MINIMUM_PHASE");
		extract_data_from_file();

		if (!(is_delta_realization && is_restricted_property)){
			std::string command_line = prepare_bmc_command_line();
			std::cout << "Back-end Verification: " << command_line << std::endl;
			std::string counterexample = execute_command_line(command_line);
			if (show_counterexample_data){
				print_counterexample_data(counterexample);
			}
			exit(0);
		}else{
			try{
				initialization();
				if (desired_property == "STABILITY"){
					check_stability_delta_domain();
					exit(0);
				} else if (desired_property == "MINIMUM_PHASE"){
					check_minimum_phase_delta_domain();
					exit(0);
				}
			} catch(std::exception & e){
				std::cout << std::endl << "[INTERNAL ERROR] - An unexpected event occurred " << std::endl;
			}
		}
	}
}


/*
//@HEADER
// ************************************************************************
//
// qr_tpetra_mv_householder_using_eigen_impl.hpp
//                     		  Pressio
//                             Copyright 2019
//    National Technology & Engineering Solutions of Sandia, LLC (NTESS)
//
// Under the terms of Contract DE-NA0003525 with NTESS, the
// U.S. Government retains certain rights in this software.
//
// Pressio is licensed under BSD-3-Clause terms of use:
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact Francesco Rizzi (fnrizzi@sandia.gov)
//
// ************************************************************************
//@HEADER
*/

#ifndef QR_IMPL_TPETRA_QR_TPETRA_MV_HOUSEHOLDER_USING_EIGEN_IMPL_HPP_
#define QR_IMPL_TPETRA_QR_TPETRA_MV_HOUSEHOLDER_USING_EIGEN_IMPL_HPP_

#include <Eigen/OrderingMethods>
#include <Eigen/SparseQR>
#include <Tpetra_Map.hpp>
#include <Tpetra_Vector.hpp>

namespace pressio{ namespace qr{ namespace impl{

template<typename MatrixType, typename R_t>
class TpetraMVHouseholderUsingEigen
{

public:
  using sc_t = typename ::pressio::Traits<MatrixType>::scalar_type;
  using lo_t         = typename ::pressio::Traits<MatrixType>::local_ordinal_type;
  using go_t         = typename ::pressio::Traits<MatrixType>::global_ordinal_type;
  using node_t       = typename ::pressio::Traits<MatrixType>::node_type;
  using Q_type       = Tpetra::MultiVector<sc_t, lo_t, go_t, node_t>;

  using eig_dyn_mat	= Eigen::Matrix<sc_t, -1, -1>;
  using help_impl_t	= QRHouseholderDenseEigenMatrix<eig_dyn_mat, R_t>;
  help_impl_t myImpl_	= {};

public:
  TpetraMVHouseholderUsingEigen() = default;
  ~TpetraMVHouseholderUsingEigen() = default;

  template < typename vector_in_t, typename vector_out_t>
  void applyQTranspose(const vector_in_t & vecIn, vector_out_t & vecOut) const
  {
    constexpr auto beta  = ::pressio::utils::Constants<sc_t>::zero();
    constexpr auto alpha = ::pressio::utils::Constants<sc_t>::one();
    ::pressio::ops::product(::pressio::transpose(), alpha, *this->Qmat_, vecIn, beta, vecOut);
  }

  template <typename VectorType>
  void doLinSolve(const VectorType & rhs, VectorType & y)const{
    myImpl_.template doLinSolve<VectorType>(rhs, y);
  }

  const Q_type & QFactor() const {
    return *this->Qmat_;
  }

  void computeThinOutOfPlace(const MatrixType & Ain)
  {
    auto & A = const_cast<MatrixType &>(Ain);

    auto rows = ::pressio::ops::extent(A,0);
    auto cols = ::pressio::ops::extent(A,1);
    auto ArowMap = A.getMap();
    Teuchos::RCP<const Teuchos::Comm<int> > comm =
      Teuchos::rcp (new Teuchos::MpiComm<int> (MPI_COMM_SELF));

    // convert it to replicated eptra matrix
    using local_map_t = Tpetra::Map<lo_t, go_t, node_t>;
    using rcp_local_map_t = Teuchos::RCP<const local_map_t>;
    rcp_local_map_t rcp_local_map = Teuchos::rcp( new local_map_t(rows, 0, comm) );

    using import_t = Tpetra::Import<lo_t, go_t, node_t>;
    import_t importer(ArowMap, rcp_local_map);
    MatrixType A2(rcp_local_map, cols);
    A2.doImport(A, importer, Tpetra::INSERT);

    // store it into an Eigen matrix
    Eigen::Matrix<sc_t, -1, -1> eA2W(rows,cols);
    for (int j=0;j<cols;j++)
    {
      auto colData = A2.getData(j);
      for (int i=0;i<rows;i++){
    	 eA2W(i,j) = colData[i];
      }
    }

    myImpl_.computeThinOutOfPlace(eA2W);

    // store Q into replicated Tpetra::Multivector
    const auto & Q2 = myImpl_.QFactor();
    Q_type locQ( rcp_local_map, Q2.cols() );
    // auto trilD = locQ.data();
    locQ.template sync<Kokkos::HostSpace>();

    auto v2d = locQ.template getLocalView<Kokkos::HostSpace>();
    auto c0 = Kokkos::subview(v2d, Kokkos::ALL(), 0);
    // //we are going to change the host view
    locQ.template modify<Kokkos::HostSpace>();
    for (int i=0;i<Q2.rows();i++)
      for (int j=0;j<Q2.cols();j++)
    	v2d(i,j) = Q2(i,j);

    // import from local to distributed
    Qmat_ = std::make_shared<Q_type>(ArowMap, Q2.cols());
    import_t importer2(rcp_local_map, ArowMap);
    Qmat_->doImport(locQ, importer2, Tpetra::INSERT);
  }

private:
  // todo: these must be moved somewhere else
  mutable std::shared_ptr<Q_type> Qmat_	= nullptr;
  mutable std::shared_ptr<R_t> Rmat_	= nullptr;

};//end class

}}} // end namespace pressio::qr::impl
#endif  // QR_IMPL_TPETRA_QR_TPETRA_MV_HOUSEHOLDER_USING_EIGEN_IMPL_HPP_


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2019 Tinko Bartels, Berlin, Germany.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_HULL_REJECTION_HPP
#define BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_HULL_REJECTION_HPP

#include <random>
#include <vector>

#include <boost/geometry/algorithms/equals.hpp>
#include <boost/geometry/algorithms/transform.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/algorithms/convex_hull.hpp>

#include <boost/geometry/extensions/random/strategies/agnostic/uniform_convex_fan.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace uniform_point_distribution {

// The following strategy is suitable for geometries for which
// a Triangle sampling strategy can be provided
// a SideStrategy that compues the triangle area can be provided.
template
<
    typename Point,
    typename DomainGeometry,
    typename TriangleStrategy,
    typename SideStrategy   //Actually, we need a triangle area strategy here.
>
struct uniform_convex_hull_rejection
{
private:
    typedef typename point_type<DomainGeometry>::type domain_point_type;
    typedef boost::geometry::model::ring<domain_point_type> ring;
    ring hull;
    uniform_convex_fan<Point, ring, TriangleStrategy, SideStrategy>
        m_strategy;
public:
    uniform_convex_hull_rejection(DomainGeometry const& g) : m_strategy(hull)
    {
        boost::geometry::convex_hull(g, hull);
        m_strategy =
            uniform_convex_fan
                <
                    Point,
                    ring,
                    TriangleStrategy,
                    SideStrategy
                >(hull);
    }
    bool equals(DomainGeometry const& l_domain,
                DomainGeometry const& r_domain,
                uniform_convex_hull_rejection const& r_strategy) const
    {
        return boost::geometry::equals(l_domain, r_domain)
            && m_strategy.equals(l_domain, r_domain, r_strategy.m_strategy);
    }
    template<typename Gen>
    Point apply(Gen& g, DomainGeometry const& d)
    {
        Point p;
        do{
            p = m_strategy.apply(g, hull);
        }while( !boost::geometry::within(p, d) );
        return p;
    }
    void reset(DomainGeometry const&) {};
};

}} // namespace strategy::uniform_point_distribution

}} // namespace boost::geometry

#endif //  BOOST_GEOMETRY_EXTENSIONS_RANDOM_STRATEGIES_AGNOSTIC_UNIFORM_CONVEX_HULL_REJECTION_HPP


//============================================================================
// Name        : IntervalAnalyzer.cpp
// Author      : Tomasz Hoffmann
// Version     :
// Copyright   : TH
// Description : Analyzer for interval results
//============================================================================

#include <iostream>
#include <iomanip>
#include <fstream>
#include <time.h>
#include <string>
#include <vector>
#include <limits.h>
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/exception/diagnostic_information.hpp>

using namespace std;

namespace alg = boost::algorithm;
namespace fs = boost::filesystem;

int main(int ac, char *av[])
{
	long double eps = std::numeric_limits<long double>::epsilon();
	try
	{

		if (ac != 7)
		{
			cout << "Interval Analyzer" << endl;
			cout << "Usage: \n" << fs::basename(av[0]) << " <l_filename>"
					<< " <r_filename>" << " <e_filename>" << " <f_filename>"
					<< " <oe_filename" << " <of_filename>" << endl << endl;
			cout << "where: \n" << "l_ - left ends of interval solution \n"
					<< "r_ - right ends of interval solution \n"
					<< "e_ - exact solution (if known) \n"
					<< "f_ - fp arithmetic solutions \n"
					<< "oe_ - exact distribution output file name \n"
					<< "of_ - fp distribution output file name \n" << endl;
			return 0;
		}
		ifstream l_file(av[1]);
		ifstream r_file(av[2]);
		ifstream e_file(av[3]);
		ifstream f_file(av[4]);
		fstream oe_file(av[5], fstream::out);
		fstream of_file(av[6], fstream::out);

		//string fname = av[4];
		//		o_file.open(fname.c_str(), fstream::out);

		int dprec = std::numeric_limits<long double>::digits10;

		bool allopen = true;
		allopen &= l_file.is_open();
		allopen &= r_file.is_open();
		allopen &= e_file.is_open();
		allopen &= f_file.is_open();

		if (!allopen)
		{
			cout << "One or more files cannot be open! \n";
			return 0;
		}
		else
		{
			string line;
			vector<string> v;
			vector<long double> lv;
			vector<long double> rv;
			vector<long double> ev;
			vector<long double> fv;

			while ((!l_file.eof()) && (!r_file.eof()) && (!e_file.eof())
					&& (!f_file.eof()))
			{
				getline(l_file, line);
				alg::split(v, line, alg::is_any_of(";"));
				vector<string>::iterator it;
				if (v.size() < 2)
					break;
//				cout << "Start left..." << endl;
				for (it = v.begin(); it != v.end(); ++it)
				{
					lv.push_back(boost::lexical_cast<long double>(*it));
				}
//				cout << "Read left done." << endl;

				getline(r_file, line);
//				cout << "Start right..." << endl;
				alg::split(v, line, alg::is_any_of(";"));
				if (v.size() < 2)
					break;
				for (it = v.begin(); it != v.end(); ++it)
				{
					rv.push_back(boost::lexical_cast<long double>(*it));
				}
//				cout << "Read right done." << endl;

				getline(e_file, line);
//				cout << "Start exact..." << endl;
				if (v.size() < 2)
					break;
				alg::split(v, line, alg::is_any_of(";"));
				for (it = v.begin(); it != v.end(); ++it)
				{
//					cout << (*it);
					ev.push_back(boost::lexical_cast<long double>(*it));
				}
//				cout << "Read exact done." << endl;

				getline(f_file, line);
				alg::split(v, line, alg::is_any_of(";"));
				for (it = v.begin(); it != v.end(); ++it)
				{
					fv.push_back(boost::lexical_cast<long double>(*it));
				}
//				cout << "Read floating-point done." << endl;

				if ((lv.size() == rv.size()) && (rv.size() == ev.size())
						&& (ev.size() == fv.size()))
				{
					long double l, r, e, f, w1, w, m;

					vector<long double> res;
					//caluclate exact solutions distribution
					for (size_t i = 0; i < lv.size(); ++i)
					{
						l = lv.at(i);
						r = rv.at(i);
						e = ev.at(i);
						w = r - l;
						m = (r + l) / 2;
						if ((w < eps) || (e == r) || (e == l))
						{
							res.push_back(0.5);
							continue;
						}
						w1 = abs(e - m);
						if ((w1 / w) > 0.5)
						{
							cout << " \n Wrong element: "
									<< std::setprecision(dprec) << e;
							cout << std::setprecision(dprec) << "\n [ " << l
									<< " ; " << r << " ] \n";
							cout << std::setprecision(dprec) << "m = " << m
									<< " \n";
						}
						res.push_back(w1 / w);
					}

					stringstream ss;
					vector<long double>::iterator it;
					for (it = res.begin(); it != res.end(); ++it)
					{
						ss << std::setprecision(dprec) << (*it) << ";";
					}
					ss << endl;
					oe_file << ss.str();

					//caluclate floating-pint solutions distribution
					res.clear();
					for (size_t i = 0; i < lv.size(); ++i)
					{
						l = lv.at(i);
						r = rv.at(i);
						f = fv.at(i);
						w = r - l;
						m = (r + l) / 2;
						if ((w < eps) || (f == r) || (f == l))
						{
							res.push_back(0.5);
							continue;
						}
						w1 = abs(f - m);
						if ((w1 / w) > 0.5)
						{
							cout << " \n Wrong element: "
									<< std::setprecision(dprec) << e;
							cout << std::setprecision(dprec) << "\n [ " << l
									<< " ; " << r << " ] \n";
							cout << std::setprecision(dprec) << "m = " << m
									<< " \n";
						}
						res.push_back(w1 / w);
					}

					ss.str(string());
					for (it = res.begin(); it != res.end(); ++it)
					{
						ss << std::setprecision(dprec) << (*it) << ";";
					}
					ss << endl;
					of_file << ss.str();

					//clear input vectors for current row
					lv.clear();
					rv.clear();
					ev.clear();
					fv.clear();
				}
			}

		}
		l_file.close();
		r_file.close();
		e_file.close();
		oe_file.close();
	} catch (boost::exception const& ex)
	{
		cout
				<< "Execution problem "
						+ boost::current_exception_diagnostic_information();
	}
	return 0;
}


#ifndef LOGICAL_EXPRESSION_HPP
#define LOGICAL_EXPRESSION_HPP

#include <iostream>
#include <string>
#include <sstream>
#include <utility>
#include <iterator>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <boost/regex.hpp>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
#include <boost/call_traits.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/io/ios_state.hpp>
#include <boost/optional.hpp>
//#include <boost/logic/tribool.hpp>

//
// namespace for Logical Expression
//
namespace logical_expr {

using namespace std;

// Don't care
/*thread_local*/static const boost::optional<bool> dont_care = boost::none;
// static const boost::logic::tribool dont_care = indeterminated; better than optional<bool>

// expr_mode is not used now.
enum expr_mode { alphabet_expr, verilog_expr, truth_table };

// argument generating iterator for logical_function
class arg_gen_iterator {
public:
    typedef boost::dynamic_bitset<> value_type;
    typedef arg_gen_iterator this_type;
    arg_gen_iterator(int width, int val)
        : width_(width), current_val_(val), value_(width, val) {}
    this_type& operator++() {
        value_type tmp(width_, current_val_ + 1);
        value_.swap(tmp);   // never throw any exceptions
        ++current_val_;
        return *this;
    }
    this_type operator++(int)
        { this_type before = *this; ++*this; return before; }
    this_type& operator--() {
        value_type tmp(width_, current_val_ - 1);
        value_.swap(tmp);
        --current_val_;
        return *this;
    }
    this_type operator--(int)
        { this_type before = *this; --*this; return before; }
    bool operator<(const this_type &it) const
        { return (it.width_ == width_ && current_val_ < it.current_val_); }
    bool operator>(const this_type &it) const
        { return (it.width_ == width_ && current_val_ > it.current_val_); }
    bool operator==(const this_type &it) const
        { return (it.width_ == width_ && current_val_ == it.current_val_); }
    bool operator!=(const this_type &it) const
        { return !(*this == it); }
    const value_type& operator*() const { return value_; }
private:
    const int width_;
    int current_val_;
    value_type value_;
};

// Argument generator for logical_function using Iterator (default: arg_gen_iterator)
template<typename Iterator = arg_gen_iterator>
class arg_generator {
public:
    arg_generator(int nbegin, int nend, int width)
        : begin_(width, nbegin), end_(width, nend) {}
    const Iterator& begin() const { return begin_; }
    const Iterator&  end()  const { return end_; }
private:
    const Iterator begin_, end_;
};

//
// * String to be parsed has to be in the following form:
// * ${Function-Name}(Variables-divided-by-',' ...) = ${TERMS} + ...
// * White spaces will be ignored
// * Default character to invert a variable is '~' (first template parameter)
// See README for more information about parsing
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
template<char inverter = '~', bool escape = true, expr_mode mode = alphabet_expr>
class function_parser {
public:
    typedef std::pair<string, vector<string>> result_type;
    function_parser() {}
    explicit function_parser(const string &expr, const char first) : expr_(expr), first_char_(first) {}
    ~function_parser() {}

    void set_expression(const string &expr) { expr_ = expr; }
    const string& get_expression() const { return expr_; }
    const string& function_name() const { return func_name_; }

    result_type parse() {
        auto untokenized = scanner();
        auto token = tokenizer(untokenized);
        boost::optional<char> undecl = use_undeclared_vars(token.second, token.first);
        if( undecl )
            throw std::runtime_error(
                (boost::format("expr: Using undeclared variable, %c") % *undecl).str()
            );
        if( !is_sequence(token.first, first_char_) )
            throw std::runtime_error("expr: used variables are not sequence");
        return std::move(token);
    }

    vector<string> scanner() {
        if( expr_.empty() )
            throw std::runtime_error("expr: Expression is empty, aborted");
        string expr_with_nospaces = boost::regex_replace(expr_, boost::regex("\\s"), "");
        boost::regex reg(
            (boost::format("([A-Za-z_-]+)\\((((\\s*[A-Za-z],)*)([A-Za-z]))\\)=(((%1%?[A-Za-z])+\\+)*((%1%?[A-Za-z])+))$")
                % (escape ? string{'\\', inverter} : string{inverter})).str(),
             boost::regex::perl
        );
        boost::smatch result;
        if( !boost::regex_match(expr_with_nospaces, result, reg) )
            throw std::runtime_error("expr: Input string does not match the correct form");
        func_name_ = result[1];
//                            decl-vars  decl-terms
        return vector<string>{result[2], result[6]};
    }

    static result_type tokenizer(const vector<string> &untokenized) {
        std::vector<string> vars, terms;
        typedef boost::char_separator<char> char_separator;
        boost::tokenizer<char_separator>
            var_tokenizer(untokenized[0], char_separator(",")), term_tokenizer(untokenized[1], char_separator("+"));
        ostringstream oss;
        for( auto token : var_tokenizer )   oss << token;
        for( auto token : term_tokenizer )  terms.push_back(token);
        return std::make_pair(oss.str(), terms);
    }

    static bool is_sequence(const string &vars, char first_char) {
        if( vars[0] != first_char )
            throw std::runtime_error("expr: declare terms which starts with not specified char");
        auto previous = vars.begin();
        for( auto it = ++vars.begin(); it != vars.end(); ++it ) {
            if( *it != static_cast<char>(*previous + 1) )
                return false;
            previous = it;
        }
        return true;
    }

    static boost::optional<char> use_undeclared_vars(const vector<string> &terms, const string &vars) {
        for( auto term : terms )
          for( auto used_var : term )
            if( used_var != inverter && vars.find(used_var, 0) == string::npos )
              return (used_var);
        return boost::none;
    }

private:
    string expr_, func_name_;
    char first_char_;
};

//
// Term properties
//
// Type Requirements:
//  [*] typedef value type
//  [*] Have set() and get() member functions
//  [*] Default constructible
//  [*] Has swap() member fuction that never throws any exceptions for expcetion-safe
//

// Term Property template class for POD types
template<typename T, T DefaultValue>
class term_property_pod {
public:
    typedef T value_type;
    term_property_pod() : value_(DefaultValue) {}
    typename boost::call_traits<T>::param_type get() const { return value_; }
    void set(typename boost::call_traits<T>::param_type value) { value_ = value; }
    void swap(const term_property_pod<T, DefaultValue> &property) noexcept(true)
        { std::swap(value_, property.value_); }
private:
    value_type value_;
};

typedef term_property_pod<int, 0> term_no_property;
typedef term_no_property term_dummy;
typedef term_property_pod<char, ' '> term_name;
typedef term_property_pod<bool, false> term_mark;
typedef term_property_pod<unsigned int, 0> term_number;
//

//
// class: logical term
//
template<typename Property_ = term_dummy>
class logical_term {
public:
    typedef boost::optional<bool> value_type;
    typedef boost::dynamic_bitset<> arg_type;
    typedef logical_term<Property_> this_type;
    typedef std::size_t size_t;
    typedef Property_ property_type;

    logical_term() {}
    logical_term(int bitsize, const value_type &init = logical_expr::dont_care)
        : term_(bitsize, init) {}
    template<typename Property>
    explicit logical_term(const logical_term<Property> &term)
        { construct_from(term); }
    explicit logical_term(const arg_type &arg) {
        for( int i = arg.size() - 1; 0 <= i; --i )
            term_.push_back(arg[i]);
    }

    template<typename Property>
    void construct_from(const logical_term<Property> &term)
        { term_ = term.term_; }

    template<typename Property>
    void swap(logical_term<Property> &term) noexcept(true) {
        term_.swap(term.term_);
        property_.swap(term.property_);
    }

    size_t size() const
        { return term_.size(); }

    bool size_check(const arg_type &arg) const
        { return (size() == arg.size()); }

    bool is_same(const this_type &term) const
        { return (term.term_ == term_); }

    template<typename Property>
    bool size_check(const logical_term<Property> &term) const
        { return ( size() == term.size() ); }

    size_t num_of_value(bool value) const {
        return static_cast<size_t>(std::count_if(term_.begin(), term_.end(),
                [value](const value_type &b){ return (b != dont_care && *b == value); }));
    }

    size_t diff_size(const this_type &term) const {
        if( !size_check(term) )
            throw std::runtime_error(size_error_msg);
        size_t diff_count = 0;
        for( int i = 0; i < size(); ++i )
            if( term_[i] != term[i] )
                ++diff_count;
        return diff_count;
    }

    bool calculate(const arg_type &arg) const {
        if( !size_check(arg) )
            throw std::runtime_error(size_error_msg);
        bool ret = true;
        for( int i = 0; i < term_.size(); ++i )
            ret = ret && (term_[i] == dont_care ? true : arg[term_.size()-1-i] == term_[i]);
        return ret;
    }

    bool operator()(const arg_type &arg) const
        { return calculate(arg); }

    value_type& operator[](int index)
        { return term_[index]; }
    const value_type& operator[](int index) const
        { return term_[index]; }

    template<typename Property>
    bool operator==(const logical_term<Property> &term) const {
        if( !size_check(term) ) return false;
        arg_generator<> gen(0, std::pow(2, size()), size());
        for( auto it = gen.begin(); it != gen.end(); ++it )
            if( calculate(*it) != term.calculate(*it) )
                return false;
        return true;
    }

    template<typename Property>
    friend typename logical_term<Property>::property_type::value_type
        property_get(const logical_term<Property>& term);

    template<typename Property>
    friend void property_set(logical_term<Property>& term,
        const typename logical_term<Property>::property_type::value_type &arg);

    template<typename Property>
    friend std::ostream& operator<<(std::ostream &os, const logical_expr::logical_term<Property> &bf) {
        boost::io::ios_flags_saver ifs(os);
        for( auto b : bf.term_ ) {
            if( b ) os << std::noboolalpha << *b;
            else    os << 'x';
        }
        return os;
    }

private:
    static const std::string size_error_msg;
    vector<value_type> term_;
    property_type property_;
};
template<typename Property>
const string logical_term<Property>::size_error_msg = "target two operands are not same size";

// Create a logical_term with Property parsed from expr
template<typename Property = term_no_property, char Inverter = '^'>
logical_term<Property>
parse_logical_term(const string &expr, int bitsize, char const first_char = 'A') {
    logical_term<Property> term(bitsize);
    for( auto it = expr.begin(); it != expr.end(); ++it ) {
        bool value = true;
        if( *it == Inverter ) {
            ++it; value = false;
        }
        term[*it - first_char] = value;
    }
    return term;
}

//
// Setter and getter functions of term property
//
template<typename Property>
typename logical_term<Property>::property_type::value_type
    property_get(const logical_term<Property>& term)
{ return term.property_.get(); }

template<typename Property>
void property_set(logical_term<Property>& term,
    const typename logical_term<Property>::property_type::value_type &arg)
{ term.property_.set(arg); }

//
// Minimize the different 1bit of term a and b
//
template<typename Property>
logical_term<Property> onebit_minimize(const logical_term<Property> &a, const logical_term<Property> &b)
{
    if( a.size() != b.size() || 1 < a.diff_size(b) )
        throw std::runtime_error("tried to minimize a term which has more than 1bit different bits");
    logical_term<Property> term(a);
    for( int i = 0; i < term.size(); ++i )
        if( term[i] != b[i] )
            term[i] = dont_care;
    return std::move(term);
}

// Return minimized term which has pval as its property value
template<typename Property>
logical_term<Property> onebit_minimize(
        const logical_term<Property> &a,
        const logical_term<Property> &b,
        const typename logical_term<Property>::property_type::value_type &pval
    )
{
    logical_term<Property> term = onebit_minimize(a, b);
    property_set(term, pval);
    return std::move(term);
}

//
// class: logical function
//
template<typename TermType>
class logical_function {
public:
    typedef std::size_t size_t;
    typedef TermType value_type;
    typedef boost::dynamic_bitset<> arg_type;
    typedef typename vector<value_type>::iterator iterator;
    typedef typename vector<value_type>::const_iterator const_iterator;
    typedef logical_function<TermType> this_type;

    logical_function() {}
    explicit logical_function(const TermType &term) { add(term); }
    ~logical_function() {}

    iterator begin()             { return func_.begin(); }
    const_iterator begin() const { return func_.begin(); }
    iterator end()               { return func_.end(); }
    const_iterator end()   const { return func_.end(); }

    void swap(logical_function<TermType> &func) noexcept(true)
        { func_.swap(func.func_); }

    int size() const
        { return func_.size(); }
    size_t term_size() const {
        if( func_.empty() ) return 0;
        return func_[0].size();
    }
    void add(const TermType &term)
        { func_.push_back(term); }

    void add(const this_type &func) {
        vector<TermType> tmp(func_);
        for( auto term : func )
            tmp.push_back(term);
        func_ = std::move(tmp);
    }

    void clear()
        { func_.clear(); }

    bool calculate(const arg_type &arg) const {
        bool ret = false;
        for( auto term : func_ )
            ret = ret || term(arg);
        return ret;
    }

    bool is_same(const this_type &func) const {
        if( size() != func.size() )
            return false;
        for( const value_type &term : func_ )
            if( std::find_if(func.begin(), func.end(),
                [&](const value_type &t){ return t.is_same(term); })
                    == func.end() )
                return false;
        return true;
    }

    bool operator()(const arg_type &arg) const
        { return calculate(arg); }
    value_type& operator[](int index)
        { return func_[index]; }
    const value_type& operator[](int index) const
        { return func_[index]; }

    // The expression like "term + term = func" is not allowed
    const logical_function operator+(const TermType &term) {
        logical_function ret(*this);
        ret += term;
        return ret;
    }

    const logical_function operator+(const logical_function<TermType> &func) {
        logical_function ret(*this);
        ret += func;
        return ret;
    }

    logical_function& operator+=(const TermType &term)
        { add(term); return *this; }
    logical_function& operator+=(const logical_function &func)
        { add(func); return *this; }
    friend ostream& operator<<(ostream &os, const logical_function &bf) {
        for( auto term : bf.func_ )
            os << term << " ";
        return os;
    }

    template<typename Property>
    bool operator==(const logical_function<logical_term<Property>> &func) const {
        arg_generator<> gen(0, std::pow(2, func.term_size()), func.term_size());
        for( auto it = gen.begin(); it != gen.end(); ++it )
            if( calculate(*it) != func.calculate(*it) )
                return false;
        return true;
    }

private:
    vector<TermType> func_;
};

}   // namespace logical_expr

template<typename Property>
logical_expr::logical_function<logical_expr::logical_term<Property>> operator+
    (const logical_expr::logical_term<Property> &first, const logical_expr::logical_term<Property> &second) {
    logical_expr::logical_function<logical_expr::logical_term<Property>> ret(first);
    ret += second;
    return ret;
}

#endif  // LOGICAL_EXPRESSION_HPP