//==============================================================================
//         Copyright 2015 - J.T. Lapreste
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2015 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_ARITHMETIC_FUNCTIONS_SIMD_COMMON_ROUND_HPP_INCLUDED
#define BOOST_SIMD_ARITHMETIC_FUNCTIONS_SIMD_COMMON_ROUND_HPP_INCLUDED

#include <boost/simd/arithmetic/functions/round.hpp>
#include <boost/simd/include/functions/simd/tenpower.hpp>
#include <boost/simd/include/functions/simd/copysign.hpp>
#include <boost/simd/include/functions/simd/seldec.hpp>
#include <boost/simd/include/functions/simd/is_greater.hpp>
#include <boost/simd/include/functions/simd/abs.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/functions/simd/multiplies.hpp>
#include <boost/simd/include/functions/simd/divides.hpp>
#include <boost/simd/include/functions/simd/ceil.hpp>
#include <boost/simd/include/functions/splat.hpp>
#include <boost/simd/include/constants/half.hpp>
#include <boost/dispatch/attributes.hpp>
#include <boost/mpl/equal_to.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT          ( round_, tag::cpu_
                                    , (A0)(X)
                                    , ((simd_<integer_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(1)
    {
      return a0;
    }
  };

  BOOST_DISPATCH_IMPLEMENT          ( round_, boost::simd::tag::simd_
                                    , (A0)(X)
                                    , ((simd_<floating_<A0>,X>))
                                    )
  {
    typedef A0 result_type;
    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(1)
    {
      result_type absa0 = boost::simd::abs(a0);
      result_type c =  boost::simd::ceil(absa0);
      return copysign(seldec(gt(c-Half<result_type>(), absa0), c), a0);
    }
  };

  BOOST_DISPATCH_IMPLEMENT_IF( round_, boost::simd::tag::simd_
                             , (A0)(X)(A1)
                             , (boost::mpl::equal_to < boost::simd::meta::cardinal_of<A0>
                                                     , boost::simd::meta::cardinal_of<A1>
                                >)
                             , ((simd_< floating_<A0>,X>))
                               ((simd_< integer_<A1>,X>))
                             )
  {
    typedef A0 result_type;

    BOOST_FORCEINLINE BOOST_SIMD_FUNCTOR_CALL(2)
    {
      A0 fac = tenpower(a1);
      A0 tmp1 = round(a0*fac)/fac;
      return if_else(is_ltz(a1), round(tmp1), tmp1);
    }
  };

  BOOST_DISPATCH_IMPLEMENT( round_, boost::simd::tag::cpu_
                          , (A0)(X)(A1)
                          , ((simd_< floating_<A0>,X>))
                            (scalar_< unsigned_<A1>>)
                          )
  {
    typedef A0 result_type;
    typedef typename dispatch::meta::as_integer<A0, unsigned>::type itype_t;
    BOOST_FORCEINLINE result_type operator()(A0 const & a0,  A1 const & a1) const
    {
      return round(a0, splat<itype_t>(a1));
    }
  };

  BOOST_DISPATCH_IMPLEMENT( round_, boost::simd::tag::cpu_
                          , (A0)(X)(A1)
                          , ((simd_< floating_<A0>,X>))
                            (scalar_< integer_<A1>>)
                          )
  {
    typedef A0 result_type;
    typedef typename dispatch::meta::as_integer<A0, signed>::type itype_t;
    BOOST_FORCEINLINE result_type operator()(A0 const & a0,  A1 const & a1) const
    {
      return round(a0, splat<itype_t>(a1));
    }
  };

} } }

#endif


#include <expression/dualtime/FixedPointBDFDualTimeIntegrator.h>
#include <expression/dualtime/VariableImplicitBDFDualTimeIntegrator.h>
#include <expression/dualtime/BlockImplicitBDFDualTimeIntegrator.h>

#include <spatialops/structured/Grid.h>
#include <spatialops/structured/FVStaggered.h>
#include <spatialops/structured/FieldComparisons.h>

#include <test/TestHelper.h>

#include "TwoEquationLinearRhs.h"
#include <expression/Functions.h>
#include <expression/ExpressionTree.h>
#include <expression/ExprPatch.h>

#include <boost/program_options.hpp>

#include <iostream>
#include <fstream>

using std::cout;
using std::endl;

namespace po = boost::program_options;
namespace so = SpatialOps;

typedef so::SVolField FieldT;

enum IntegratorType{
  FIXED_POINT,
  VARIABLE_IMPLICIT,
  BLOCK_IMPLICIT
};

bool test( const IntegratorType integratorType )
{

  /* ---------------------------------------------------------------
   * domain, operators, field managers, etc setup
   * ---------------------------------------------------------------
   */
  Expr::ExprPatch patch(1);
  SpatialOps::OperatorDatabase sodb;
  Expr::FieldManagerList& fml = patch.field_manager_list();

  /* ---------------------------------------------------------------
   * variable and rhs names must be made before the integrator
   * for block-implicit's chain rule assembly
   * ---------------------------------------------------------------
   */
  std::string phiName = "phi";
  std::string psiName = "psi";
  const Expr::Tag phiTag   ( phiName,          Expr::STATE_NONE );
  const Expr::Tag phiRHSTag( phiName + "_rhs", Expr::STATE_NONE );
  const Expr::Tag psiTag   ( psiName,          Expr::STATE_NONE );
  const Expr::Tag psiRHSTag( psiName + "_rhs", Expr::STATE_NONE );

  /* ---------------------------------------------------------------
   * build integrator, set constant physical and dual time step
   * ---------------------------------------------------------------
   */
  const unsigned bdfOrder = 1;
  boost::shared_ptr<Expr::DualTime::BDFDualTimeIntegrator> integrator;

  const Expr::Tag dtTag( "timestep"     , Expr::STATE_NONE );
  const Expr::Tag dsTag( "dual_timestep", Expr::STATE_NONE );

  double ds; // dual time step size

  switch( integratorType ){
    case FIXED_POINT:{
      ds = 0.01;
      integrator = boost::make_shared<Expr::DualTime::FixedPointBDFDualTimeIntegrator<FieldT> >( 0,
                                                                                "Fixed Point BDF",
                                                                                dtTag,
                                                                                dsTag,
                                                                                bdfOrder );
      break;
    }
    case VARIABLE_IMPLICIT:{
      ds = 1e16;
      integrator = boost::make_shared<Expr::DualTime::VariableImplicitBDFDualTimeIntegrator<FieldT> >( 0,
                                                                                "Variable Implicit BDF",
                                                                                dtTag,
                                                                                dsTag,
                                                                                bdfOrder );
      break;
    }
    case BLOCK_IMPLICIT:{
      ds = 1e16;
      integrator = boost::make_shared<Expr::DualTime::BlockImplicitBDFDualTimeIntegrator<FieldT> >( 0,
                                                                                "Block Implicit BDF",
                                                                                dtTag,
                                                                                dsTag,
                                                                                bdfOrder );
      break;
    }
  }

  const double dt   = 0.01;

  typedef Expr::ConstantExpr<so::SingleValueField>::Builder ConstantSingleValueT;
  integrator->set_physical_time_step_expression( new ConstantSingleValueT( dtTag, dt ) );
  integrator->set_dual_time_step_expression    ( new ConstantSingleValueT( dsTag, ds ) );

  /* ---------------------------------------------------------------
   * register variables and RHS at STATE_NONE to the integrator
   * ---------------------------------------------------------------
   */
  Expr::ExpressionFactory& factory = integrator->factory();

  const double k   =  4.0; // oscillation frequency
  const double K11 =  1*k;
  const double K12 =  1*k;
  const double K21 = -2*k;
  const double K22 = -1*k;

  factory.register_expression( new TwoEquationLinearRhs<FieldT>::Builder( K11, K12, phiRHSTag, phiTag, psiTag ) );
  factory.register_expression( new TwoEquationLinearRhs<FieldT>::Builder( K22, K21, psiRHSTag, psiTag, phiTag ) );

  integrator->add_variable<FieldT>( phiTag.name(), phiRHSTag );
  integrator->add_variable<FieldT>( psiTag.name(), psiRHSTag );

  /* ---------------------------------------------------------------
   * final integrator prep, must happen before setting initial conditions, and tree output
   * ---------------------------------------------------------------
   */
  integrator->prepare_for_integration( fml, sodb, patch.field_info() );
  {
    std::ofstream f("dualtimeOscillationTest-integrator-tree.dot");
    integrator->get_tree().write_tree( f );
  }

  /* ---------------------------------------------------------------
   * set initial conditions with STATE_N
   * ---------------------------------------------------------------
   */
  Expr::ExpressionFactory icFactory;

  const double phi0 = 1.0;
  const double psi0 = 1.0;

  const Expr::Tag phiNTag( phiName, Expr::STATE_N );
  const Expr::Tag psiNTag( psiName, Expr::STATE_N );
  typedef Expr::ConstantExpr<FieldT>::Builder ConstantT;
  const Expr::ExpressionID phiId = icFactory.register_expression( new ConstantT( phiNTag, phi0 ) );
  const Expr::ExpressionID psiId = icFactory.register_expression( new ConstantT( psiNTag, psi0 ) );

  std::set<Expr::ExpressionID> rootIds;
  rootIds.insert( phiId );
  rootIds.insert( psiId );
  Expr::ExpressionTree icTree( rootIds, icFactory, patch.id() );

  icTree.register_fields( fml );
  icTree.bind_fields( fml );
  icTree.execute_tree();
  {
    std::ofstream f("dualtimeOscillationTest-initialcond-tree.dot");
    icTree.write_tree( f );
  }

  integrator->get_tree().lock_fields(fml);
  integrator->get_tree().execute_tree();

  TestHelper status( true );

  if( integratorType == VARIABLE_IMPLICIT || integratorType == BLOCK_IMPLICIT ){
    const FieldT& hh = fml.field_ref<FieldT>( sens_tag( phiTag, phiTag ) );
    const FieldT& ss = fml.field_ref<FieldT>( sens_tag( psiTag, psiTag ) );
    const FieldT& hs = fml.field_ref<FieldT>( sens_tag( phiTag, psiTag ) );
    const FieldT& sh = fml.field_ref<FieldT>( sens_tag( psiTag, phiTag ) );

    SpatialOps::SpatFldPtr<FieldT> tmp = SpatialOps::SpatialFieldStore::get<FieldT>( hh );

    *tmp <<= 1.0;
    status( SpatialOps::field_equal_abs(hh,*tmp), "dh/dh" );
    status( SpatialOps::field_equal_abs(ss,*tmp), "ds/ds" );

    *tmp <<= 0.0;
    status( SpatialOps::field_equal_abs(hs,*tmp), "dh/ds" );
    status( SpatialOps::field_equal_abs(sh,*tmp), "ds/dh" );
  }

  status( !integrator->get_tree().computes_field( phiNTag ), "doesn't compute phi (placeholder)");
  status( !integrator->get_tree().computes_field( psiNTag ), "doesn't compute psi (placeholder)");

  /* ---------------------------------------------------------------
   * set iteration counts, prep solution history file
   * ---------------------------------------------------------------
   */
  const unsigned maxIterPerStep = 100;

  unsigned totalIter = 0;
  unsigned stepCount = 0;
  unsigned dualIter  = 0;
  bool     converged = false;

  /* ---------------------------------------------------------------
   * perform the integration
   * ---------------------------------------------------------------
   */
  const double tend = 1.0;

  double t = 0;
  while( t<tend ){

    dualIter = 0;
    integrator->begin_time_step();

    do{
      integrator->advance_dualtime( converged );
      dualIter++;
      totalIter++;
    } while( !converged && dualIter <= maxIterPerStep );

    integrator->end_time_step();
    stepCount++;
    t += dt;
  }

  /* ---------------------------------------------------------------
   * process the test
   * ---------------------------------------------------------------
   */

  const double meanStepPerIter = ( (double) totalIter ) / ( (double) stepCount );

  switch( integratorType ){
    case FIXED_POINT:{
      const double meanStepPerIterFromMatlabCode = 30.07;
      status( std::abs( meanStepPerIter - meanStepPerIterFromMatlabCode ) < 1e-8, "fixed-point convergence rate check" );
      break;
    }
    case VARIABLE_IMPLICIT:{
      const double meanStepPerIterFromMatlabCode = 8.6;
      status( std::abs( meanStepPerIter - meanStepPerIterFromMatlabCode ) < 1e-8, "variable-implicit convergence rate check" );
      break;
    }
    case BLOCK_IMPLICIT:{
      const double meanStepPerIterFromMatlabCode = 2.0;
      status( std::abs( meanStepPerIter - meanStepPerIterFromMatlabCode ) < 1e-8, "block-implicit convergence rate check" );
      break;
    }
  }

  const FieldT& phiPredicted     = fml.field_ref<FieldT>( Expr::Tag( phiName, Expr::STATE_N ) );
  const FieldT& psiPredicted     = fml.field_ref<FieldT>( Expr::Tag( psiName, Expr::STATE_N ) );
  const double phiFromMatlabCode = -1.999662876681719;
  const double psiFromMatlabCode =  1.487205902604459;

  SpatialOps::SpatFldPtr<FieldT> tmpPhi = SpatialOps::SpatialFieldStore::get<FieldT>( phiPredicted );
  SpatialOps::SpatFldPtr<FieldT> tmpPsi = SpatialOps::SpatialFieldStore::get<FieldT>( phiPredicted );
  *tmpPhi <<= phiFromMatlabCode;
  *tmpPsi <<= psiFromMatlabCode;

  status( SpatialOps::field_equal_abs( phiPredicted, *tmpPhi, 1e-6 ), "phi value-check" );
  status( SpatialOps::field_equal_abs( psiPredicted, *tmpPsi, 1e-6 ), "psi value-check" );

  return status.ok();
}

int main( int iarg, char* carg[] )
{
  /* ---------------------------------------------------------------
   * initialization from command line options
   * ---------------------------------------------------------------
   */
  bool fixedPoint       = false;
  bool variableImplicit = false;
  bool blockImplicit    = false;

  // parse the command line options input describing the problem
  try{
    po::options_description desc("Supported Options");
    desc.add_options()
        ( "help", "print help message" )
        ( "fixed-point", "Use the fixed point dualtime integration scheme")
        ( "variable-implicit", "Use the variable implicit dualtime integration scheme")
        ( "block-implicit", "Use the block implicit dualtime integration scheme");
    po::variables_map args;

    try{
      po::store( po::parse_command_line(iarg,carg,desc), args );
      po::notify(args);
    }
    catch( std::exception& err ){
      cout << "\nError parsing command line options.\n\n"
          << err.what() << "\n\n" << desc << "\n";
      return -1;
    }
    if( args.count("help") ){
      cout << desc << "\n";
      return 1;
    }

    fixedPoint       = args.count("fixed-point");
    variableImplicit = args.count("variable-implicit");
    blockImplicit    = args.count("block-implicit");

    if( !fixedPoint && !variableImplicit && !blockImplicit ){
      std::cout << "Error: no dualtime integration method selected.\n"
                << desc << std::endl;
        return -1;
    }
  }
  catch( std::exception& err ){
    std::cout << err.what() << std::endl;
    return -1;
  }

  try{
    TestHelper status( true );
    if( fixedPoint       ) status( test( FIXED_POINT       ), "Fixed Point"       );
    if( variableImplicit ) status( test( VARIABLE_IMPLICIT ), "Variable Implicit" );
    if( blockImplicit    ) status( test( BLOCK_IMPLICIT    ), "Block Implicit" );

    if( status.ok() ){
      std::cout << "PASS\n";
      return 0;
    }
  }
  catch( std::exception& err ){
    std::cout << err.what() << std::endl;
    return -1;
  }

  std::cout << "FAIL\n";
  return -1;
}


#include <boost/chrono.hpp>
#include <iostream>

using namespace boost::chrono;

int main()
{
  std::cout << floor<minutes>(minutes{1} + seconds{45}) << '\n';
  std::cout << round<minutes>(minutes{1} + seconds{15}) << '\n';
  std::cout << ceil<minutes>(minutes{1} + seconds{15}) << '\n';
}


#include <iostream>
#include <experimental/filesystem>

namespace fs = std::experimental::filesystem;

#include "include.hh"

#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>

#include <iostream>
#include <map>
#include <string>

using namespace EMC;
using namespace std;

namespace EMC {

enum task {
    tNONE = 0,
    ASSEMBLE,
	REBIN,
    INVERT,
	REBIN_INVERT,
	DE_PILEUP,
    SUBTRACT,
	MULTIPLY_RESPONSE_CURVE,
    PRINT_STATS,
	APPLY,
	FIT_VALUES
};

map<string, task> taskNameMap = {
    { "assemble", ASSEMBLE },
    { "rebin", REBIN },
    { "invert", INVERT},
    { "rebin_invert", REBIN_INVERT},
    { "de_pileup", DE_PILEUP},
    { "subtract", SUBTRACT},
    { "multiply_resp_curve", MULTIPLY_RESPONSE_CURVE},
    { "print_stats", PRINT_STATS},
    { "apply", APPLY },
    { "fit_values", FIT_VALUES }
};

} /* namespace EMC */

using namespace boost::numeric;

int main(int argc, char* argv[]) {
    cout << endl << "error-matrix-calculation v1 (c) Simon Michalke 2020" << endl;

    task todo = tNONE;
    bool forceOverwrite = false;
    string output;
    string input;

    string matrixFileName;

    std::vector<double> inputBinCenters;
    matrixType matType = mtNumber;
    int countRate = -1;

    double minRow = -1;
    double minCol = -1;
    double minRowBin = -1;
    double minColBin = -1;
    double maxRow = -1;
    double maxCol = -1;

    float rowErrorEst = -1;
    float colErrorEst = -1;

    double lowerBound = 0;

    bool fail = false;

	for (int argPos = 1; argPos < argc; argPos++) {
		if (argv[argPos][0] == '-') {
			switch (argv[argPos][1]) {
			case 't': // what task
				argPos++;
				if (taskNameMap.find(argv[argPos]) == taskNameMap.end()) {
					cout << "tasks not recognized!" << endl;
					fail = true;
				} else {
					todo = taskNameMap.at(argv[argPos]);
				}
				break;
			case 'f':
				forceOverwrite = true;
				break;
			case 'i': {
				argPos++;
				input = argv[argPos];
			}
				break;
			case 'o':
				argPos++;
				output = argv[argPos];
				break;
			case 'b': {
				argPos++;
				auto binVec = split(argv[argPos], ',');
				unsigned int i = 0;
				unsigned int binCount = binVec.size();
				double binIncrement = -1;
				for (;i<binVec.size();i++) {
					if (binVec[i] == "...") {
						binIncrement = inputBinCenters[i-1] - inputBinCenters[i-2];
						inputBinCenters.push_back(inputBinCenters[i-1] + binIncrement);
						binCount = std::stod(binVec[i+1]) / (inputBinCenters[i-1] - inputBinCenters[i-2]) - 1;
						break;
					}
					inputBinCenters.push_back(std::stod(binVec[i]));
				}
				for (;i<binCount;i++) {
					inputBinCenters.push_back(inputBinCenters[i] + binIncrement);
				}
			}
				break;
			case 'm': {
				argPos++;
				auto maxStrVec = split(argv[argPos], ',');
				minRow = std::stod(maxStrVec[0]);
				minCol = std::stod(maxStrVec[1]);
				minRowBin = std::stod(maxStrVec[2]);
				minColBin = std::stod(maxStrVec[3]);
				maxRow = std::stod(maxStrVec[4]);
				maxCol = std::stod(maxStrVec[5]);
			}
				break;
			case 'T':
				argPos++;
				if (matrixTypeMap.find(argv[argPos]) == matrixTypeMap.end()) {
					cout << "matrix type not recognized!" << endl;
					fail = true;
				} else {
					matType = matrixTypeMap.at(argv[argPos]);
				}
				break;
			case 'c':
				argPos++;
				countRate = std::stoi(argv[argPos]);
				break;
			case 'l':
				argPos++;
				lowerBound = std::stof(argv[argPos]);
				break;
			case 'M':
				argPos++;
				matrixFileName = argv[argPos];
				break;
			case 'd': {
				argPos++;
				auto strVec = split(argv[argPos], ',');
				rowErrorEst = std::stod(strVec[0]);
				colErrorEst = std::stod(strVec[1]);
				break;
			}
			default:
				cout << "unexpected flag in argument number " << argPos << endl;
				fail = true;
			}
		} else {
			cout << "expected \"-\" in argument number " << argPos << endl;
			fail = true;
		}
	}

    if (fail) {
        cout << "aborted" << endl;
        todo = tNONE;
    }

	switch (todo) {
	case ASSEMBLE: {
		cout << "assembling matrix" << endl;
		int retVal = assemble(forceOverwrite, input, output, inputBinCenters,
				matType, countRate);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case REBIN: {
		cout << "rebinning matrix" << endl;
		int retVal = rebin(forceOverwrite, input, output, countRate, minRow,
				minCol, minRowBin, minColBin, maxRow, maxCol);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case INVERT: {
		cout << "inverting matrix" << endl;
		int retVal = invert(forceOverwrite, input, output);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case REBIN_INVERT: {
		cout << "rebinning and inverting matrix" << endl;
		int retVal = rebin_invert(forceOverwrite, input, output, countRate, minRow,
				minCol, minRowBin, minColBin, maxRow, maxCol, rowErrorEst, colErrorEst);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case DE_PILEUP: {
		cout << "reversing pileup" << endl;
		int retVal = de_pileup(forceOverwrite, input, output, countRate, lowerBound);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case SUBTRACT: {
		cout << "subtracting matrix" << endl;
		int retVal = subtract(forceOverwrite, input, output, matrixFileName);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case MULTIPLY_RESPONSE_CURVE: {
		cout << "multiply-response-curving matrix" << endl;
		int retVal = multiply_resp_curve(forceOverwrite, input, output, matrixFileName);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case PRINT_STATS: {
		cout << "printing matrix stats matrix" << endl;
		int retVal = print_stats(matrixFileName);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case APPLY: {
		cout << "applying matrix" << endl;
		int retVal = apply(forceOverwrite, input, output, matrixFileName);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case FIT_VALUES: {
		cout << "fitting matrix parameters" << endl;
		int retVal = fit_values(input, output, countRate, minRow,
				minCol, minRowBin, minColBin, maxRow, maxCol);
		cout << "finished with status " << retVal << endl;
		break;
	}
	case tNONE:
    default:
        cout << "" << endl << endl;
        cout << " supported general arguments:" << endl;
        cout << "   -t [ de_pileup | assemble | rebin | invert | rebin_invert | apply | print_stats | fit_values ]" << endl;
        cout << "      specifies what task is being executed" << endl;
        cout << "      should be specified first" << endl;
        cout << "   -f overwrite output files / directories, deletes target files before writing" << endl;
        cout << "   -i <filename>" << endl;
        cout << "      input file name" << endl;
        cout << "   -o <filename>" << endl;
        cout << "      output file name" << endl << endl;
        cout << "   In case of a vector the columns are expected to be bin center, value, value error" << endl;
        cout << "   with space character for spacing" << endl;
        cout << " arguments for specific tasks:" << endl;
        cout << "   de_pileup: reconstructs pileup numerically" << endl;
        cout << "     -c <integer>" << endl;
        cout << "        total pixel count" << endl;
        cout << "     -l <float>" << endl;
        cout << "        up to this energy counts are set to 0" << endl;
        cout << "   assemble: assembles a matrix based on putting spectrums into columns, output bins determined by input spectrum" << endl;
        cout << "     -i <filename format string>" << endl;
        cout << "        input file name. String that will be formatted using sprintf with data passed by -b" << endl;
        cout << "        expects 'bin_center counts' in each line" << endl;
        cout << "     -b <bin0>,<bin1>,..." << endl;
        cout << "        list of input bin centers (floating point possible). Passed to -i via sprintf" << endl;
        cout << "        for evently spaced bins e.g. 1,2,...,100 is also possible" << endl;
        cout << "     -T <type>" << endl;
        cout << "        output type; possible values are 'number', 'probability' and 'density'" << endl;
        cout << "        in case of density bin width is determined based on the bin width in input files" << endl;
        cout << "     -c <integer>" << endl;
        cout << "        total events, count rate for normalization" << endl;
        cout << "   rebin: rebins a matrix by merging bins in order to increase statistical significance" << endl;
        cout << "     -c <integer>" << endl;
        cout << "        dimension of output square matrix" << endl;
        cout << "     -m <minRow><minCol><minRowBin><minColBin><maxRow><maxCol>" << endl;
        cout << "        row and column bin parameters, float" << endl;
        cout << "   rebin_invert: rebins a matrix and inverts it. Estimates errors as well." << endl;
        cout << "     -c <integer>" << endl;
        cout << "        dimension of output square matrix" << endl;
        cout << "     -m <minRow><minCol><minRowBin><minColBin><maxRow><maxCol>" << endl;
        cout << "        row and column bin parameters, float" << endl;
        cout << "     -d <row Error, float>,<column Error, float>" << endl;
        cout << "        estimate error based on row and column error." << endl;
        cout << "   invert: inverts a matrix" << endl;
        cout << "   subtract: subtracts spectra" << endl;
        cout << "        uses <filelane>.cntr and <filelane>.step for input, output and filename of subtracted spectrum." << endl;
        cout << "     -M <filename>" << endl;
        cout << "        filename of the spectrum that will be subtracted" << endl;
        cout << "   multiply_resp_curve: scales spectrum with response curve" << endl;
        cout << "        uses <filelane>.cntr and <filelane>.step for input and output" << endl;
        cout << "     -M <filename>" << endl;
        cout << "        filename of the transmission curve" << endl;
        cout << "        this is assumed to be more dense than the input spectrum" << endl;
        cout << "   print_stats: inverts a matrix" << endl;
        cout << "     -M <filename>" << endl;
        cout << "        filename of the matriy" << endl;
        cout << "   apply: inverts a matrix; input and output are spectrum files" << endl;
        cout << "        first column is expected to be bin center and second the counts in that bin" << endl;
        cout << "        vector will be rebinned and rescaled to matrix dimensions" << endl;
        cout << "        output is bin center and count rate" << endl;
        cout << "     -M <filename>" << endl;
        cout << "        filename of the matrix" << endl;
        cout << "   fit_values: optimizes rebinning based on inverted matrix error metric" << endl;
        cout << "               output is the base filename for the rebinned (.rebin.mat) and inverted (.rebin.inverted.mat) matrix" << endl;
        cout << "               these matrices will be overwritten multiple times!" << endl;
        cout << "     -c <integer>" << endl;
        cout << "        dimension of output square matrix" << endl;
        cout << "     -m <minRow><minCol><minRowBin><minColBin><maxRow><maxCol>" << endl;
        cout << "        initial row and column bin parameters; minCol and maxCol will stay constant" << endl;
        cout << endl;
    }

    return 0;
}


//
// Signature.cpp
//

#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include <pssr.h>
#include <osrng.h>
#include <fstream>
#include "Signature.hpp"
#include "Utils.hpp"
#include "Encrypter.hpp"
#include "../Logger.hpp"

namespace network {

using namespace CryptoPP;

Signature::Signature(const std::string& filename)
{
    AutoSeededRandomPool rnd;
    InvertibleRSAFunction params;
    params.GenerateRandomWithKeySize(rnd, 3072);

    private_key_ = RSA::PrivateKey(params);
    public_key_ = RSA::PublicKey(params);
}

Signature::~Signature()
{

}

std::string Signature::Sign(const std::string& in)
{
    AutoSeededRandomPool rng;
    RSASS<PSSR, SHA1>::Signer signer(private_key_);

    size_t length = signer.MaxSignatureLength();
    SecByteBlock signature(length);

    signer.SignMessage(rng, (const byte*)in.data(), in.size(), signature);
    return std::string(signature.begin(), signature.end());
}

bool Signature::Verify(const std::string& in, const std::string& sign)
{
    RSASS<PSS, SHA1>::Verifier verifier(public_key_);
    return verifier.VerifyMessage((const byte*)in.data(), in.size(),
        (const byte*)sign.data(), sign.size());
}

std::string Signature::GetPublicKey()
{
    ByteQueue queue;
    public_key_.Save(queue);

    size_t length = static_cast<size_t>(queue.CurrentSize());
    std::unique_ptr<char[]> outbuf(new char [length]);
    queue.Get((byte*)outbuf.get(), length);

    return std::string((const char*)outbuf.get(), length);
}

void Signature::SetPublicKey(const std::string& in)
{
    if (in.empty()) return;
    ByteQueue queue;
    queue.Put((const byte*)in.data(), in.size());
    public_key_.Load(queue);
}

std::string Signature::GetPrivateKey()
{
    ByteQueue queue;
    private_key_.Save(queue);

    size_t length = static_cast<size_t>(queue.CurrentSize());
    std::unique_ptr<char[]> outbuf(new char [length]);
    queue.Get((byte*)outbuf.get(), length);

    return std::string((const char*)outbuf.get(), length);
}

void Signature::SetPrivateKey(const std::string& in)
{
    if (in.empty()) return;
    ByteQueue queue;
    queue.Put((const byte*)in.data(), in.size());
    private_key_.Load(queue);
}

}


#include <armadillo>
#include <iostream>

using namespace std;
using namespace arma;

int main() {
    mat A = randu<mat>(4, 5);
    A.print("A:");

    cout << A.n_rows << endl;
    cout << A.n_cols << endl;
    cout << A.n_elem << endl;
    cout << A(1) << endl;
    cout << A.at(10) << endl;
    cout << A[20] << endl;
    cout << A.at(1, 2) << endl;
    cout << A.at(1, 9) << endl;  // 超出边界，随机值，接近0

    A.col(0).print("A:");
    A.row(1).print("A:");
    A.cols(0, 2).print("A:");
    A.rows(2, 2).print("A:");

    A.set_size(4, 4);
    A << 0 << 1 << 2 << 3 << endr << 4 << 5 << 6 << 7 << endr << 8 << 9 << 10
      << 11 << endr << 12 << 13 << 14 << 15 << endr;
    A.print("A:");

    A.set_size(3, 3);
    A.load("./sample.csv", csv_ascii);
    A.print("A:");

    A.save("./output", raw_ascii);

    return 0;
}


#ifndef MANIP_TRACKING_COMMON_H
#define MANIP_TRACKING_COMMON_H

#include <Eigen/Dense>

static double getUnixTime(void)
{
    struct timespec tv;

    if(clock_gettime(CLOCK_REALTIME, &tv) != 0) return 0;

    return (tv.tv_sec + (tv.tv_nsec / 1000000000.0));
}

// from https://forum.kde.org/viewtopic.php?f=74&t=91514
template<typename Derived>
static inline bool is_finite(const Eigen::MatrixBase<Derived>& x)
{
   return ( (x - x).array() == (x - x).array()).all();
}
template<typename Derived>
static inline bool is_nan(const Eigen::MatrixBase<Derived>& x)
{
   return ((x.array() == x.array())).all();
}

static inline double randrange(double min, double max){
  return (((double)rand()) / RAND_MAX)*(max - min) + min;
}

static Eigen::Transform<double, 3, Eigen::Isometry>
getAverageTransform(std::vector<Eigen::Transform<double, 3, Eigen::Isometry>> transforms){
  Eigen::Transform<double, 3, Eigen::Isometry> avg_transform;
  avg_transform.setIdentity();
  Eigen::Vector3d avg_pt = Eigen::Vector3d::Zero();
  Eigen::Matrix3d sum_rots = Eigen::Matrix3d::Zero();
  int k=0;
  for (auto iter=transforms.begin(); iter!=transforms.end(); iter++){
    avg_pt += iter->matrix().block<3, 1>(0, 3);
    sum_rots += iter->matrix().block<3, 3>(0, 0);
    k++;
  }

  if (k > 0){
    avg_pt /= (double)k;
    // following tip from "A Note on Averaging Rotations", Curtis, Janin, Zikan, 1993...
    Eigen::JacobiSVD<Eigen::Matrix3d> svd(sum_rots,  Eigen::ComputeFullU | Eigen::ComputeFullV);
    Eigen::Matrix3d avg_rot = svd.matrixU() * svd.matrixV().transpose();
    avg_transform.matrix().block<3, 3>(0, 0) = avg_rot;
    avg_transform.matrix().block<3, 1>(0, 3) = avg_pt;
  }
  return avg_transform;
}

#endif


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_POLYNOMIALS_FUNCTIONS_SCALAR_PLEVL_HPP_INCLUDED
#define NT2_POLYNOMIALS_FUNCTIONS_SCALAR_PLEVL_HPP_INCLUDED
#include <nt2/polynomials/functions/plevl.hpp>
#include <nt2/include/functions/scalar/tchebeval.hpp>
#include <nt2/include/functions/scalar/fma.hpp>
#include <nt2/sdk/meta/fusion.hpp>
#include <boost/fusion/adapted/array.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( plevl_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_< arithmetic_<A0> >)(fusion_sequence_<A1>)
                            )
  {

    typedef typename boost::dispatch::meta::as_floating<A0>::type result_type;

    NT2_FUNCTOR_CALL(2)
    {
      return plevl(result_type(a0), a1);
    }
  };
} }

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is floating_
/////////////////////////////////////////////////////////////////////////////
namespace nt2 { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( plevl_, tag::cpu_
                            , (A0)(A1)
                            , (scalar_< floating_<A0> >)(fusion_sequence_<A1>)
                            )
  {

    typedef typename boost::dispatch::meta::as_floating<A0>::type result_type;

    NT2_FUNCTOR_CALL(2)
    {
      typename A1::const_iterator p = a1.begin();
      A0 ans = a0+*p++;
      do
      ans = fma(ans, a0, *p);
      while( ++p !=  a1.end());
      return ans;
    }
  };
} }

#endif


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
#include <Expected.hpp>
using armadilloJava::Expected;

#include <iostream>
using std::cout;
using std::endl;

#include <utility>
using std::pair;

#include <armadillo>
using arma::ones;
using arma::zeros;

#include <InputClass.hpp>
using armadilloJava::InputClass;

#include <Input.hpp>
using armadilloJava::Input;

namespace armadilloJava {
	class ExpectedInPlaceGenColVec : public Expected {
    public:
		ExpectedInPlaceGenColVec() {
			cout << "Compute ExpectedInPlaceGenColVec(): " << endl;

			vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
				InputClass::GenColVec
			});

			for (vector<pair<string, void*>> input : inputs) {
				_fileSuffix = "";

				int n = 0;
				for (pair<string, void*> value : input) {
					switch (n) {
						case 0:
							_fileSuffix += value.first;
							_genColVec = *static_cast<Col<double>*>(value.second);
							break;
					}
					++n;
				}

				cout << "Using input: " << _fileSuffix << endl;

				expectedColOnes();
				expectedColZeros();
				expectedColReset();
				expectedColClear();
				expectedColInPlaceIncrement();
				expectedColInPlaceDecrement();
			}

			cout << "done." << endl;
		}
	protected:
		Col<double> _genColVec;

		void expectedColOnes() {
			cout << "- Compute expectedColOnes() ... ";
			Col<double> expected = _genColVec;
			save<double>("Col.ones", (expected.ones()));
			cout << "done." << endl;
		}

		void expectedColZeros() {
			cout << "- Compute expectedColZeros() ... ";
			Col<double> expected = _genColVec;
			save<double>("Col.zeros", (expected.zeros()));
			cout << "done." << endl;
		}

		void expectedColReset() {
			cout << "- Compute expectedColReset() ... ";
			Col<double> expected = _genColVec;
			expected.reset();
			save<double>("Col.reset", expected);
			cout << "done." << endl;
		}

		void expectedColClear() {
			cout << "- Compute expectedColClear() ... ";
			Col<double> expected = _genColVec;
			expected.clear();
			save<double>("Col.clear", expected);
			cout << "done." << endl;
		}

		void expectedColInPlaceIncrement() {
			cout << "- Compute expectedColInPlaceIncrement() ... ";
			Col<double> expected = _genColVec;
			expected++;
			save<double>("Col.inPlaceIncrement", expected);
			cout << "done." << endl;
		}

		void expectedColInPlaceDecrement() {
			cout << "- Compute expectedColInPlaceDecrement() ... ";
			Col<double> expected = _genColVec;
			expected--;
			save<double>("Col.inPlaceDecrement", expected);
			cout << "done." << endl;
		}

	};
}


/* ---------------------------------------------------------------------------
 *
 * Autonomous Navigation and Perception Lab (ANPL),
 * Technion, Israel Institute of Technology,
 * Faculty of Aerospace Engineering,
 * Haifa, Israel, 32000
 * All Rights Reserved
 *
 * See LICENSE for the license information
 *
 * -------------------------------------------------------------------------- */
/**
 * @file: eigen_utils.hpp
 * @brief:
 * @author: Andrej Kitanov
 *
 */

#ifndef PLANNER_EIGEN_UTILS_HPP
#define PLANNER_EIGEN_UTILS_HPP

#include <Eigen/Dense>
#include <Eigen/Sparse>

// This function performs row or column sum of a sparse matrix X and returns a result in the row vector v
template <typename T, typename S> inline void sumAlongDim(
        const Eigen::SparseMatrix<T>& X,
        const int dim,
        Eigen::Matrix<S, Eigen::Dynamic, 1>& v)
{
    // Get size of input
    int m = X.rows();
    int n = X.cols();
    // resize output
    if(dim==1)
    {
        v = Eigen::Matrix<S, Eigen::Dynamic, 1>(n);
    }else
    {
        v = Eigen::Matrix<S, Eigen::Dynamic, 1>(m);
    }

    v.setZero();

    // Iterate over outside
    for(int k=0; k<X.outerSize(); ++k)
    {
        // Iterate over inside
        for(typename Eigen::SparseMatrix<T>::InnerIterator it(X,k); it; ++it)
        {
            if(dim == 1)
            {
                v[it.col()] += it.value();
            }else
            {
                v[it.row()] += it.value();
            }
        }
    }
}

/*void example () {
    SparseMatrix<double> mat(5,5);
    for (int k = 0; k < mat.outerSize(); ++k)
        for (SparseMatrix<double>::InnerIterator it(mat, k); it; ++it) {
            it.value();
            it.row();   // row index
            it.col();   // col index (here it is equal to k)
            it.index(); // inner index, here it is equal to it.row()
        }
}*/

template<typename MatrixType>
void saveMatrixToFile(const char *filename, const MatrixType& m)
{
    ofstream f(filename, ios::binary);
    typename MatrixType::Index rows, cols;
    rows = m.rows();
    cols = m.cols();
    f.write((char *)&rows, sizeof(m.rows()));
    f.write((char *)&cols, sizeof(m.cols()));
    f.write((const char *)(m.data()), sizeof(typename MatrixType::Scalar)*m.rows()*m.cols());
    f.close();
}

template<typename MatrixType>
void loadMatrixFromFile(const char *filename, MatrixType& m)
{
    typename MatrixType::Index rows, cols;
    ifstream f(filename, ios::binary);
    f.read((char *)&rows, sizeof(rows));
    f.read((char *)&cols, sizeof(cols));
    m.resize(rows, cols);
    typename MatrixType::Scalar* p = m.data();
    f.read((char *)p, sizeof(typename MatrixType::Scalar)*rows*cols);
    if (f.bad())
        throw "Error reading matrix";
    f.close();
}

#include <opencv2/core/core.hpp>
template<typename _Tp, int _rows, int _cols, int _options, int _maxRows, int _maxCols>
void eigen2cv(const Eigen::Matrix<_Tp, _rows, _cols, _options, _maxRows, _maxCols> &src, cv::Mat &dst) {
    if (!(src.Flags & Eigen::RowMajorBit))
    {
        cv::Mat _src(src.cols(), src.rows(), cv::DataType<_Tp>::type,
                     (void*)src.data(), src.stride() * sizeof(_Tp));
        cv::transpose(_src, dst);
    }
    else
    {
        cv::Mat _src(src.rows(), src.cols(), cv::DataType<_Tp>::type,
                     (void*)src.data(), src.stride() * sizeof(_Tp));
        _src.copyTo(dst);
    }
}

#endif //PLANNER_EIGEN_UTILS_HPP


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

#include <Eigen/CXX11/Tensor>

static void test_float_rounding()
{
  Tensor<float, 2> ftensor(20,30);
  ftensor = ftensor.random() * 100.f;

  Tensor<float, 2> result = ftensor.round();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(result(i,j), numext::round(ftensor(i,j)));
    }
  }
}

static void test_float_flooring()
{
  Tensor<float, 2> ftensor(20,30);
  ftensor = ftensor.random() * 100.f;

  Tensor<float, 2> result = ftensor.floor();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(result(i,j), numext::floor(ftensor(i,j)));
    }
  }
}

static void test_float_ceiling()
{
  Tensor<float, 2> ftensor(20,30);
  ftensor = ftensor.random() * 100.f;

  Tensor<float, 2> result = ftensor.ceil();

  for (int i = 0; i < 20; ++i) {
    for (int j = 0; j < 30; ++j) {
      VERIFY_IS_EQUAL(result(i,j), numext::ceil(ftensor(i,j)));
    }
  }
}

void test_cxx11_tensor_roundings()
{
   CALL_SUBTEST(test_float_rounding());
   CALL_SUBTEST(test_float_ceiling());
   CALL_SUBTEST(test_float_flooring());
}


/*!
 * @file
 * Forward declares the @ref IntegralDomain typeclass.
 *
 *
 * @copyright Louis Dionne 2014
 * Distributed under the Boost Software License, Version 1.0.
 *         (See accompanying file LICENSE.md or copy at
 *             http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_MPL11_FWD_INTEGRAL_DOMAIN_HPP
#define BOOST_MPL11_FWD_INTEGRAL_DOMAIN_HPP

#include <boost/mpl11/fwd/bool.hpp>

namespace boost { namespace mpl11 {
    /*!
     * @ingroup typeclasses
     * @defgroup IntegralDomain Integral Domain
     *
     * Commutative `Ring` without nonzero zero divisors.
     *
     *
     * ### Methods
     * `div` and `mod`
     *
     * ### Minimal complete definition
     * All the methods.
     *
     * @{
     */
    template <typename Left, typename Right = Left, typename = true_>
    struct IntegralDomain;

    //! Integer division of `x` by `y`.
    template <typename x, typename y>
    struct div;

    //! Remainder of the integer division of `x` by `y`.
    template <typename x, typename y>
    struct mod;
    //! @}
}} // end namespace boost::mpl11

#endif // !BOOST_MPL11_FWD_INTEGRAL_DOMAIN_HPP


/* boost random/uniform_smallint.hpp header file
 *
 * Copyright Jens Maurer 2000-2001
 * Permission to use, copy, modify, sell, and distribute this software
 * is hereby granted without fee provided that the above copyright notice
 * appears in all copies and that both that copyright notice and this
 * permission notice appear in supporting documentation,
 *
 * Jens Maurer makes no representations about the suitability of this
 * software for any purpose. It is provided "as is" without express or
 * implied warranty.
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id: uniform_smallint.hpp 13178 2002-03-11 20:31:05Z jmaurer $
 *
 * Revision history
 *  2001-04-08  added min<max assertion (N. Becker)
 *  2001-02-18  moved to individual header files
 */

#ifndef BOOST_RANDOM_UNIFORM_SMALLINT_HPP
#define BOOST_RANDOM_UNIFORM_SMALLINT_HPP

#include <cassert>
#include <boost/config.hpp>
#include <boost/limits.hpp>

namespace boost {

// uniform integer distribution on a small range [min, max]
template<class UniformRandomNumberGenerator, class IntType = int>
class uniform_smallint
{
public:
  typedef UniformRandomNumberGenerator base_type;
  typedef IntType result_type;
  BOOST_STATIC_CONSTANT(bool, has_fixed_range = false);

  uniform_smallint(base_type & rng, IntType min, IntType max);
  result_type operator()()
  {
    // we must not use the low bits here, because LCGs get very bad then
    return ((_rng() - _rng.min()) / _factor) % _range + _min;
  }
  result_type min() const { return _min; }
  result_type max() const { return _max; }
#ifndef BOOST_NO_OPERATORS_IN_NAMESPACE
  friend bool operator==(const uniform_smallint& x, const uniform_smallint& y)
  { return x._min == y._min && x._max == y._max && x._rng == y._rng; }
#else
  // Use a member function
  bool operator==(const uniform_smallint& rhs) const
  { return _min == rhs._min && _max == rhs._max && _rng == rhs._rng;  }
#endif
private:
  typedef typename base_type::result_type base_result;
  base_type & _rng;
  IntType _min, _max;
  base_result _range;
  int _factor;
};

#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
//  A definition is required even for integral static constants
template<class UniformRandomNumberGenerator, class IntType>
const bool uniform_smallint<UniformRandomNumberGenerator, IntType>::has_fixed_range;
#endif

template<class UniformRandomNumberGenerator, class IntType>
uniform_smallint<UniformRandomNumberGenerator, IntType>::
uniform_smallint(base_type & rng, IntType min, IntType max)
  : _rng(rng), _min(min), _max(max),
    _range(static_cast<base_result>(_max-_min)+1), _factor(1)
{
#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
  BOOST_STATIC_ASSERT(std::numeric_limits<IntType>::is_integer);
#endif
  assert(min < max);

  // LCGs get bad when only taking the low bits.
  // (probably put this logic into a partial template specialization)
  // Check how many low bits we can ignore before we get too much
  // quantization error.
  base_result r_base = _rng.max() - _rng.min();
  if(r_base == std::numeric_limits<base_result>::max()) {
    _factor = 2;
    r_base /= 2;
  }
  r_base += 1;
  if(r_base % _range == 0) {
    // No quantization effects, good
    _factor = r_base / _range;
  } else {
    // carefully avoid overflow; pessimizing heree
    for( ; r_base/_range/32 >= _range; _factor *= 2)
      r_base /= 2;
  }
}

} // namespace boost

#endif // BOOST_RANDOM_UNIFORM_SMALLINT_HPP


/* Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 *
 * Copyright 2021 INRIA.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>

#include "SiconosVector.hpp"
#include "SimpleMatrix.hpp"
#include "BlockMatrixIterators.hpp"
#include "BlockMatrix.hpp"
#include "BlockVector.hpp"

#include "SiconosAlgebra.hpp"

#include "SiconosAlgebraProd.hpp" // for subprod
#include "SiconosException.hpp"
using namespace Siconos;

void subprod(const SiconosMatrix& A, const SiconosVector& x, SiconosVector& y, const Index& coord, bool init)
{
  // To compute subY = subA * subX in an "optimized" way (in comparison with y = prod(A,x) )
  // or subY += subA*subX if init = false.

  // coord is [r0A r1A c0A c1A r0x r1x r0y r1y]
  //
  // subA is the sub-matrix of A, for row numbers between r0A and r1A-1 and columns between c0A and c1A-1.
  // The same for x and y with rix and riy.

  // Check dims
  unsigned int rowA = coord[1] - coord[0];
  unsigned int colA = coord[3] - coord[2];
  unsigned int dimX = coord[5] - coord[4];
  unsigned int dimY = coord[7] - coord[6];
  if(colA != dimX)
    THROW_EXCEPTION("inconsistent sizes between A and x.");

  if(rowA != dimY)
    THROW_EXCEPTION("inconsistent sizes between A and y.");

  if(dimX > x.size() || dimY > y.size() || rowA > A.size(0) || colA > A.size(1))
    THROW_EXCEPTION("input index too large.");

  Siconos::UBLAS_TYPE numA = A.num();
  Siconos::UBLAS_TYPE numX = x.num();
  Siconos::UBLAS_TYPE numY = y.num();

  if(numA == Siconos::BLOCK)   // If A,x or y is Block
    THROW_EXCEPTION("not yet implemented for A block matrices.");

  if(numA == Siconos::ZERO)  // A = 0
  {
    if(init)
    {
      if(numY == Siconos::DENSE)
        ublas::subrange(*y.dense(), coord[6], coord[7]) *= 0.0;
      else //if(numY==Siconos::SPARSE)
        ublas::subrange(*y.sparse(), coord[6], coord[7]) *= 0.0;
    }
    //else nothing
  }
  else if(numA == Siconos::IDENTITY)  // A = identity
  {
    if(!init)
      ublas::subrange(*y.dense(), coord[6], coord[7]) += ublas::subrange(*x.dense(), coord[4], coord[5]);
    else
    {
      // if x and y do not share memory (ie are different objects)
      if(&x != &y)
        noalias(ublas::subrange(*y.dense(), coord[6], coord[7])) = ublas::subrange(*x.dense(), coord[4], coord[5]);

      // else nothing
    }
  }

  else // A is not 0 or identity
  {
    {
      if(init)
      {
        if(&x != &y)  // if no common memory between x and y.
        {
          if(numX == Siconos::DENSE)
          {
            ublas::vector_range<DenseVect> subX(*x.dense(), ublas::range(coord[4], coord[5]));

            if(numY != Siconos::DENSE)
              THROW_EXCEPTION("y (output) must be a dense vector.");
            ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));

            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
          }
          else //if(numX == Siconos::SPARSE)
          {
            ublas::vector_range<SparseVect> subX(*x.sparse(), ublas::range(coord[4], coord[5]));
            if(numY != Siconos::DENSE && numA != Siconos::SPARSE)
              THROW_EXCEPTION("y (output) must be a dense vector.");

            if(numA == Siconos::DENSE)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));

              if(numY == Siconos::DENSE)
              {
                ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
                noalias(subY) = ublas::prod(subA, subX);
              }
              else
              {
                ublas::vector_range<SparseVect> subY(*y.sparse(), ublas::range(coord[6], coord[7]));
                noalias(subY) = ublas::prod(subA, subX);
              }
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) = ublas::prod(subA, subX);
            }
          }
        }
        else // if x and y are the same object => alias
        {
          if(numX == Siconos::DENSE)
          {
            ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[4], coord[5]));
            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> and vector_range<SparseVect> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
          }
          else //if(numX == Siconos::SPARSE)
          {
            ublas::vector_range<SparseVect> subY(*y.sparse(), ublas::range(coord[4], coord[5]));
            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY = ublas::prod(subA, subY);
            }
          }
        }
      }
      else // += case
      {
        if(&x != &y)  // if no common memory between x and y.
        {
          if(numX == Siconos::DENSE)
          {
            ublas::vector_range<DenseVect> subX(*x.dense(), ublas::range(coord[4], coord[5]));

            if(numY != Siconos::DENSE)
              THROW_EXCEPTION("y (output) must be a dense vector.");
            ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));

            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
          }
          else //if(numX == Siconos::SPARSE)
          {
            ublas::vector_range<SparseVect> subX(*x.sparse(), ublas::range(coord[4], coord[5]));
            if(numY != Siconos::DENSE && numA != Siconos::SPARSE)
              THROW_EXCEPTION("y (output) must be a dense vector.");

            if(numA == Siconos::DENSE)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              if(numY == Siconos::DENSE)
              {
                ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
                noalias(subY) += ublas::prod(subA, subX);
              }
              else
              {
                ublas::vector_range<SparseVect> subY(*y.sparse(), ublas::range(coord[6], coord[7]));
                noalias(subY) += ublas::prod(subA, subX);
              }
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[6], coord[7]));
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              noalias(subY) += ublas::prod(subA, subX);
            }
          }
        }
        else // if x and y are the same object => alias
        {
          if(numX == Siconos::DENSE)
          {
            ublas::vector_range<DenseVect> subY(*y.dense(), ublas::range(coord[4], coord[5]));
            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
          }
          else //if(numX == Siconos::SPARSE)
          {
            ublas::vector_range<SparseVect> subY(*y.sparse(), ublas::range(coord[4], coord[5]));
            if(numA == Siconos::DENSE)
            {
              ublas::matrix_range<DenseMat> subA(*A.dense(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::TRIANGULAR)
            {
              ublas::matrix_range<TriangMat> subA(*A.triang(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SYMMETRIC)
            {
              ublas::matrix_range<SymMat> subA(*A.sym(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
            else if(numA == Siconos::SPARSE)
            {
#ifdef BOOST_LIMITATION
              THROW_EXCEPTION("ublas::matrix_range<SparseMat> does not exist for your boost distribution and your architecture.");
#else
              ublas::matrix_range<SparseMat> subA(*A.sparse(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
#endif
            }
            else //if(numA==Siconos::BANDED)
            {
              ublas::matrix_range<BandedMat> subA(*A.banded(), ublas::range(coord[0], coord[1]), ublas::range(coord[2], coord[3]));
              subY += ublas::prod(subA, subY);
            }
          }
        }
      }
    }
  }
}

void subprod(const SiconosMatrix& A, const BlockVector& x, SiconosVector& y, const Index& coord, bool init)
{
  assert(!(A.isFactorized()) && "A is Factorized in prod !!");

  // Number of the subvector of x that handles element at position coord[4]
  std::size_t firstBlockNum = x.getNumVectorAtPos(coord[4]);
  // Number of the subvector of x that handles element at position coord[5]
  unsigned int lastBlockNum = x.getNumVectorAtPos(coord[5]);
  Index subCoord = coord;
  SPC::SiconosVector tmp = x.vector(firstBlockNum);
  std::size_t subSize =  tmp->size(); // Size of the sub-vector
  const SP::Index xTab = x.tabIndex();
  if(firstBlockNum != 0)
  {
    subCoord[4] -= (*xTab)[firstBlockNum - 1];
    subCoord[5] =  std::min(coord[5] - (*xTab)[firstBlockNum - 1], subSize);
  }
  else
    subCoord[5] =  std::min(coord[5], subSize);

  if(firstBlockNum == lastBlockNum)
  {
    subprod(A, *tmp, y, subCoord, init);
  }
  else
  {
    unsigned int xPos = 0 ; // Position in x of the current sub-vector of x
    bool firstLoop = true;
    subCoord[3] = coord[2] + subCoord[5] - subCoord[4];
    for(VectorOfVectors::const_iterator it = x.begin(); it != x.end(); ++it)
    {
      if((*it)->num() == Siconos::BLOCK)
        THROW_EXCEPTION("not yet implemented for x block of blocks ...");
      if(xPos >= firstBlockNum && xPos <= lastBlockNum)
      {
        tmp = x.vector(xPos);
        if(firstLoop)
        {
          subprod(A, *tmp, y, subCoord, init);
          firstLoop = false;
        }
        else
        {
          subCoord[2] += subCoord[5] - subCoord[4]; // !! old values for 4 and 5
          subSize = tmp->size();
          subCoord[4] = 0;
          subCoord[5] = std::min(coord[5] - (*xTab)[xPos - 1], subSize);
          subCoord[3] = subCoord[2] + subCoord[5] - subCoord[4];
          subprod(A, *tmp, y, subCoord, false);
        }
      }
      xPos++;
    }
  }
}


//
// Created by Alex Beccaro on 18/01/18.
//

#ifndef PROJECT_EULER_PROBLEM15_HPP
#define PROJECT_EULER_PROBLEM15_HPP

#include <boost/multiprecision/cpp_int.hpp>

using boost::multiprecision::uint128_t;

namespace problems {
    class problem15 {
    public:
        /**
         * Solves problem
         * @param width The width of the grid
         * @param height The height of the grid
         * @return The solution
         */
        static uint128_t solve(uint32_t width = 20, uint32_t height = 20);
    };
}

#endif //PROJECT_EULER_PROBLEM15_HPP


#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>

#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/border.h>

#include <boost/function_output_iterator.hpp>
#include <fstream>
#include <vector>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;

typedef boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Mesh>::edge_descriptor     edge_descriptor;
typedef boost::graph_traits<Mesh>::face_descriptor     face_descriptor;

namespace PMP = CGAL::Polygon_mesh_processing;

struct halfedge2edge
{
  halfedge2edge(const Mesh& m, std::vector<edge_descriptor>& edges)
    : m_mesh(m), m_edges(edges)
  {}
  void operator()(const halfedge_descriptor& h) const
  {
    m_edges.push_back(edge(h, m_mesh));
  }
  const Mesh& m_mesh;
  std::vector<edge_descriptor>& m_edges;
};

int main(int argc, char* argv[])
{
  const char* filename = (argc > 1) ? argv[1] : "data/pig.off";
  std::ifstream input(filename);

  Mesh mesh;
  if (!input || !(input >> mesh) || !CGAL::is_triangle_mesh(mesh)) {
    std::cerr << "Not a valid input file." << std::endl;
    return 1;
  }

  double target_edge_length = 0.1;
  unsigned int nb_iter = 3;

  std::cout << "Split border...";

    std::vector<edge_descriptor> border;
    PMP::border_halfedges(faces(mesh),
      mesh,
      boost::make_function_output_iterator(halfedge2edge(mesh, border)));
    PMP::split_long_edges(border, target_edge_length, mesh);

  std::cout << "done." << std::endl;

  std::cout << "Start remeshing of " << filename
            << " (" << num_faces(mesh) << " faces,";

  std::vector<face_descriptor> seed, patch;
  Mesh::Property_map<face_descriptor,int> selected
    = mesh.add_property_map<face_descriptor,int>("f:selected",0).first;
  seed.push_back(*(faces(mesh).first));
  selected[seed.front()] = true;
  CGAL::expand_face_selection(seed, mesh, 5, selected, std::back_inserter(patch));

  std::cout << " and patch of size " << patch.size() << std::endl;
  PMP::isotropic_remeshing(patch,
                           target_edge_length,
                           mesh,
                           PMP::parameters::number_of_iterations(nb_iter)
                           .face_patch_map(selected)
                           .protect_constraints(true)//i.e. protect border, here
                           );

  std::cout << "Remeshing done." << std::endl;

  return 0;
}


#include <cassert>
#include <iostream>
#include <boost/static_assert.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>

template <typename V1, typename V2> class vector_sum;

template <typename T>
class vector
{
    void check_size(int that_size) const { assert(my_size == that_size); }
    void check_index(int i) const { assert(i >= 0 && i < my_size); }
  public:
    typedef T    value_type;

    explicit vector(int size)
      : my_size(size), data( new T[my_size] )
    {}

    vector()
      : my_size(0), data(0)
    {}

    vector( const vector& that )
      : my_size(that.my_size), data( new T[my_size] )
    {
	for (int i= 0; i < my_size; ++i)
	    data[i]= that.data[i];
    }

    ~vector() { if (data) delete [] data ; }

    template <typename Src>
    vector& operator=( const Src& that )
    {
	check_size(that.size());
	for (int i= 0; i < my_size; ++i)
	    data[i]= that[i];
	return *this;
    }

    int size() const { return my_size ; }

    const T& operator[]( int i ) const
    {
	check_index(i);
	return data[i];
    }

    T& operator[]( int i )
    {
	check_index(i);
	return data[i] ;
    }

  private:
    int   my_size ;
    T*    data ;
};

template <typename T>
std::ostream& operator<<(std::ostream& os, const vector<T>& v)
{
  os << '[';
  for (int i= 0; i < v.size(); ++i) os << v[i] << ',';
  os << ']';
  return os ;
}

template <typename V1, typename V2>
class vector_sum
{
  public:
    typedef typename V1::value_type value_type;
    BOOST_STATIC_ASSERT((boost::is_same<typename V2::value_type, value_type>::value));

    vector_sum(const V1& v1, const V2& v2) : v1(v1), v2(v2)
    {
	assert(v1.size() == v2.size());
    }

    int size() const { return v1.size(); }

    value_type operator[](int i) const { return v1[i] + v2[i]; }

  private:
    const V1 &v1;
    const V2 &v2;
};

template <typename  V1, typename V2>
// typename boost::enable_if_c<is_vector<V1>::value && is_vector<V2>::value, vector_sum<V1, V2> >::type
vector_sum<V1, V2>
inline operator+( const V1& x, const V2& y )
{
    return vector_sum<V1, V2>(x, y) ;
}

int main()
{

    vector<float> v( 4 ), w(4), x(4), y(4)  ;
    v[0]= v[1]= 1.0; v[2]= 2.0 ; v[3] = -3.0 ;
    w[0]= w[1]= 1.3; w[2]= 2.5 ; w[3] = -13.0 ;
    x[0]= x[1]= 1.0; x[2]= 2.0 ; x[3] = 3.0 ;

    std::cout << "v = " << v << std::endl ;

    y= v + w + x;
    std::cout << "y = " << y << std::endl ;

    return 0 ;
}


//
// Copyright 2010 Scott McMurray.
// Distributed under 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_HASH_BLOCK_CYPHERS_DETAIL_MD4_POLICY_HPP
#define BOOST_HASH_BLOCK_CYPHERS_DETAIL_MD4_POLICY_HPP

#include <boost/array.hpp>
#include <boost/hash/block_cyphers/detail/basic_functions.hpp>

namespace boost {
namespace hashes {
namespace block_cyphers {
namespace detail {

struct md4_functions : basic_functions<32> {
    static word_type ff(word_type x, word_type y, word_type z) {
        return (x & y) | (~x & z);
    }
    static word_type gg(word_type x, word_type y, word_type z) {
        return (x & y) | (x & z) | (y & z);
    }
    static word_type hh(word_type x, word_type y, word_type z) {
        return x ^ y ^ z;
    }
};

struct md4_policy : md4_functions {

    static unsigned const block_bits = 128;
    static unsigned const block_words = block_bits/word_bits;
    typedef array<word_type, block_words> block_type;

    static unsigned const key_words = 16;
    static unsigned const key_bits = key_words*word_bits;
    typedef array<word_type, key_words> key_type;

    static unsigned const rounds = 48;
    typedef array<unsigned, rounds> key_indexes_type;

    static unsigned key_index(unsigned t) {
        static key_indexes_type const key_indexes = {{
            0, 1, 2, 3,
            4, 5, 6, 7,
            8, 9, 10, 11,
            12, 13, 14, 15,

            0, 4, 8, 12,
            1, 5, 9, 13,
            2, 6, 10, 14,
            3, 7, 11, 15,

            0, 8, 4, 12,
            2, 10, 6, 14,
            1, 9, 5, 13,
            3, 11, 7, 15,
        }};
        return key_indexes[t];
    }
};

} // namespace detail
} // namespace block_cyphers
} // namespace hashes
} // namespace boost

#endif // BOOST_HASH_BLOCK_CYPHERS_DETAIL_MD4_POLICY_HPP


#include "cru/platform/GraphicsBase.hpp"
#include "cru/platform/bootstrap/Bootstrap.hpp"
#include "cru/platform/graphics/Factory.hpp"
#include "cru/platform/graphics/Painter.hpp"
#include "cru/platform/gui/UiApplication.hpp"
#include "cru/platform/gui/Window.hpp"

#include <dlib/matrix.h>
#include <dlib/numeric_constants.h>
#include <cmath>

using cru::platform::Point;

using matrix14 = dlib::matrix<float, 1, 4>;
using matrix13 = dlib::matrix<float, 1, 3>;
using matrix44 = dlib::matrix<float, 4, 4>;

matrix44 Identity() {
  matrix44 m;
  m = dlib::identity_matrix<float, 4>();
  return m;
}

matrix44 T1(float a, float b, float c) {
  auto m = Identity();

  m(0, 3) = -a;
  m(1, 3) = -b;
  m(2, 3) = -c;

  return m;
}

matrix44 T2(float theta) {
  auto m = Identity();

  m(0, 0) = -std::cos(theta);
  m(0, 2) = -std::sin(theta);
  m(2, 0) = std::sin(theta);
  m(2, 2) = -std::cos(theta);

  return m;
}

matrix44 T3(float phi) {
  auto m = Identity();

  m(1, 1) = std::sin(phi);
  m(1, 2) = -std::cos(phi);
  m(2, 1) = std::cos(phi);
  m(2, 2) = std::sin(phi);

  return m;
}

matrix44 T4(float alpha) {
  auto m = Identity();

  m(0, 0) = std::cos(alpha);
  m(1, 0) = std::sin(alpha);
  m(0, 1) = -std::sin(alpha);
  m(1, 1) = std::cos(alpha);

  return m;
}

matrix44 T5() {
  auto m = Identity();

  m(0, 0) = -1;

  return m;
}

struct Args {
  float a;
  float b;
  float c;
  float theta;
  float phi;
  float alpha;
};

matrix44 Tv(Args args) {
  return T1(args.a, args.b, args.c) * T2(args.theta) * T3(args.phi) *
         T4(args.alpha) * T5();
}

matrix14 Transform(matrix14 point, Args args) { return point * Tv(args); }

matrix14 Transform(matrix13 point, Args args) {
  return matrix14{point(0), point(1), point(2), 1} * Tv(args);
}

Point TransformTo2D(matrix14 point, float d) {
  return Point{point(1), point(2)};
}

const float length = 100;

matrix13 points[] = {
    {0, 0, 0},
    {length, 0, 0},
    {length, 0, 0},
    {length, length, 0},
    {length, length, 0},
    {0, length, 0},
    {0, length, 0},
    {0, 0, 0},
    {0, 0, 0},
    {0, 0, length},
    {length, 0, 0},
    {length, 0, length},
    {length, length, 0},
    {length, length, length},
    {0, length, 0},
    {0, length, length},
    {0, 0, length},
    {length, 0, length},
    {length, 0, length},
    {length, length, length},
    {length, length, length},
    {0, length, length},
    {0, length, length},
    {0, 0, length},
};

const float pi = static_cast<float>(dlib::pi);

Args args{30, 40, 50, pi / 3.f, pi / 4.f, pi / 5.f};

int main() {
  std::vector<Point> points2d;

  for (auto p : points) {
    auto point2d = TransformTo2D(Transform(std::move(p), args), length);
    points2d.push_back(point2d);
  }

  auto application = cru::platform::bootstrap::CreateUiApplication();
  auto window = application->CreateWindow();

  auto brush = application->GetGraphicsFactory()->CreateSolidColorBrush(
      cru::platform::colors::black);

  window->SetClientSize(cru::platform::Size(400, 400));

  window->PaintEvent()->AddHandler([window, &brush, points2d](nullptr_t) {
    auto painter = window->BeginPaint();
    painter->PushState();
    painter->ConcatTransform(cru::platform::Matrix::Translation(200, 200));
    for (int i = 0; i < points2d.size(); i += 2) {
      painter->DrawLine(points2d[i], points2d[i + 1], brush.get(), 1.f);
    }
    painter->PopState();
  });

  window->SetVisibility(cru::platform::gui::WindowVisibilityType::Show);

  return application->Run();
}


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#ifndef IONICLIQUID_HPP
#define IONICLIQUID_HPP

#include <cmath>
#include <iosfwd>
#include <vector>

#include "Config.hpp"

#include <Eigen/Core>

class Element;

#include "DerivativeTypes.hpp"
#include "DerivativeUtils.hpp"
#include "bi_operators/IntegratorForward.hpp"
#include "GreensFunction.hpp"
#include "dielectric_profile/Yukawa.hpp"

/*! \file IonicLiquid.hpp
 *  \class IonicLiquid
 *  \brief Green's functions for ionic liquid, described by the linearized Poisson-Boltzmann equation.
 *  \author Luca Frediani, Roberto Di Remigio
 *  \date 2013-2015
 *  \tparam DerivativeTraits evaluation strategy for the function and its derivatives
 *  \tparam IntegratorPolicy policy for the calculation of the matrix represenation of S and D
 */

template <typename DerivativeTraits = AD_directional,
          typename IntegratorPolicy = CollocationIntegrator>
class IonicLiquid __final : public GreensFunction<DerivativeTraits, IntegratorPolicy, Yukawa,
                                     IonicLiquid<DerivativeTraits, IntegratorPolicy> >
{
public:
    IonicLiquid(double eps, double k) : GreensFunction<DerivativeTraits, IntegratorPolicy, Yukawa,
                                                  IonicLiquid<DerivativeTraits, IntegratorPolicy> >() { this->profile_ = Yukawa(eps, k); }
    IonicLiquid(double eps, double k, double f) : GreensFunction<DerivativeTraits, IntegratorPolicy, Yukawa,
                                                  IonicLiquid<DerivativeTraits, IntegratorPolicy> >(f) { this->profile_ = Yukawa(eps, k); }
    virtual ~IonicLiquid() {}

    /*! Calculates the matrix representation of the S operator
     *  \param[in] e list of finite elements
     */
    virtual Eigen::MatrixXd singleLayer(const std::vector<Element> & e) const __override
    {
        return this->integrator_.singleLayer(*this, e);
    }
    /*! Calculates the matrix representation of the D operator
     *  \param[in] e list of finite elements
     */
    virtual Eigen::MatrixXd doubleLayer(const std::vector<Element> & e) const __override
    {
        return this->integrator_.doubleLayer(*this, e);
    }

    friend std::ostream & operator<<(std::ostream & os, IonicLiquid & gf) {
        return gf.printObject(os);
    }
private:
    /*!
     *  Evaluates the Green's function given a pair of points
     *
     *  \param[in] sp the source point
     *  \param[in] pp the probe point
     */
    virtual DerivativeTraits operator()(DerivativeTraits * sp, DerivativeTraits * pp) const __override
    {
        double eps = this->profile_.epsilon;
	    double k = this->profile_.kappa;
        return (exp(-k * distance(sp, pp)) / (eps * distance(sp, pp)));
    }
    /*! Returns value of the directional derivative of the
     *  Greens's function for the pair of points p1, p2:
     *  \f$ \nabla_{\mathbf{p_2}}G(\mathbf{p}_1, \mathbf{p}_2)\cdot \mathbf{n}_{\mathbf{p}_2}\f$
     *  Notice that this method returns the directional derivative with respect
     *  to the probe point, thus assuming that the direction is relative to that point.
     *  \param[in] direction the direction
     *  \param[in]        p1 first point
     *  \param[in]        p2 second point
     */
    virtual double kernelD_impl(const Eigen::Vector3d & direction,
                              const Eigen::Vector3d & p1, const Eigen::Vector3d & p2) const __override
    {
        return this->profile_.epsilon * (this->derivativeProbe(direction, p1, p2));
    }
    virtual KernelS exportKernelS_impl() const __override {
      return pcm::bind(&IonicLiquid<DerivativeTraits, IntegratorPolicy>::kernelS, *this, pcm::_1, pcm::_2);
    }
    virtual KernelD exportKernelD_impl() const __override {
      return pcm::bind(&IonicLiquid<DerivativeTraits, IntegratorPolicy>::kernelD, *this, pcm::_1, pcm::_2, pcm::_3);
    }
    virtual std::ostream & printObject(std::ostream & os) __override
    {
        os << "Green's function type: ionic liquid" << std::endl;
        os << this->profile_;
        return os;
    }
};

#endif // IONICLIQUID_HPP


#include <iostream>

#include "FiniteElementMesh.h"

#include <Eigen/Dense>

template<class T>
struct LatticeMesh : public FiniteElementMesh<T>
{
    using Base = FiniteElementMesh<T>;

    // from AnimatedMesh
    using Base::m_meshElements;
    using Base::m_particleX;
    using Base::initializeUSD;
    using Base::initializeTopology;
    using Base::initializeParticles;
    using Vector2 = typename Base::Vector2;

    // from FiniteElementMesh
    using Base::m_particleV;
    using Base::initializeUndeformedConfiguration;

    std::array<int, 2> m_cellSize; // dimensions in grid cells
    T m_gridDX;

    const int m_pinchRadius;

    LatticeMesh()
        :Base(1.e3, 1., 1., .1), m_pinchRadius(1)
    {}

    void initialize()
    {
        initializeUSD("MembranePinch6.usda");

        // Create a Cartesian lattice topology
        for(int cell_i = 0; cell_i < m_cellSize[0]; cell_i++)
        for(int cell_j = 0; cell_j < m_cellSize[1]; cell_j++){
            m_meshElements.emplace_back(
                std::array<int, 3>{
                    gridToParticleID(cell_i  , cell_j  ),
                    gridToParticleID(cell_i+1, cell_j  ),
                    gridToParticleID(cell_i+1, cell_j+1)
                }
            );
            m_meshElements.emplace_back(
                std::array<int, 3>{
                    gridToParticleID(cell_i  , cell_j  ),
                    gridToParticleID(cell_i+1, cell_j+1),
                    gridToParticleID(cell_i  , cell_j+1)
                }
            );
        }
        initializeTopology();

        // Also initialize the associated particles
        for(int node_i = 0; node_i <= m_cellSize[0]; node_i++)
        for(int node_j = 0; node_j <= m_cellSize[1]; node_j++)
            m_particleX.emplace_back(m_gridDX * (T)node_i, m_gridDX * (T)node_j);
        initializeParticles();

        // Check particle indexing in mesh
        for(const auto& element: m_meshElements)
            for(const auto vertex: element)
                if(vertex < 0 || vertex >= m_particleX.size())
                    throw std::logic_error("mismatch between mesh vertex and particle array");

        // Also resize the velocities to match
        m_particleV.resize(m_particleX.size(), Vector2::Zero());

        // Initialize rest shape matrices and particle mass
        initializeUndeformedConfiguration();
    }

    void initializeDeformation()
    {
        // Pinch membrane as initial configuration
        for(int node_i = m_cellSize[0]/2-m_pinchRadius; node_i <= m_cellSize[0]/2+m_pinchRadius; node_i++)
        for(int node_j = m_cellSize[1]/2-m_pinchRadius; node_j <= m_cellSize[1]/2+m_pinchRadius; node_j++)
            m_particleX[gridToParticleID(node_i,node_j)] = Vector2(
                m_gridDX * (T)node_i + m_gridDX * 0.1 * (T) (m_cellSize[0]+m_cellSize[1]),
                m_gridDX * (T)node_j + m_gridDX * 0.1 * (T) (m_cellSize[0]+m_cellSize[1])
            );

        // Relax every interior node
        for(int iteration = 0; iteration < 1000; iteration++)
            for(int node_i = 1; node_i < m_cellSize[0]; node_i++)
            for(int node_j = 1; node_j < m_cellSize[1]; node_j++){

                if( std::abs(node_i - m_cellSize[0]/2) <= m_pinchRadius &&
                    std::abs(node_j - m_cellSize[1]/2) <= m_pinchRadius ) continue;

                int pCenter = gridToParticleID(node_i  ,node_j  );
                int pPlusX  = gridToParticleID(node_i+1,node_j  );
                int pMinusX = gridToParticleID(node_i-1,node_j  );
                int pPlusY  = gridToParticleID(node_i  ,node_j+1);
                int pMinusY = gridToParticleID(node_i  ,node_j-1);

                m_particleX[pCenter] = .25 * ( m_particleX[pPlusX] + m_particleX[pMinusX] + m_particleX[pPlusY] + m_particleX[pMinusY]);
            }
    }

private:
    inline int gridToParticleID(const int i, const int j) const { return i * (m_cellSize[1]+1) + j; }
};

int main(int argc, char *argv[])
{
    LatticeMesh<float> simulationMesh;
    simulationMesh.m_cellSize = { 40, 40 };
    simulationMesh.m_gridDX = 0.025;
    simulationMesh.m_nFrames = 100;
    simulationMesh.m_subSteps = 1;
    simulationMesh.m_frameDt = 0.5;

    // Initialize the simulation example
    simulationMesh.initialize();
    simulationMesh.initializeDeformation();

    // Output the initial shape of the mesh
    simulationMesh.writeFrame(0);

    // Perform the animation, output results at each frame
    for(int frame = 1; frame <= simulationMesh.m_nFrames; frame++){
        simulationMesh.simulateFrame(frame);
        simulationMesh.writeFrame(frame);
    }

    // Write the entire timeline to USD
    simulationMesh.writeUSD();

    return 0;
}