#include "Common/Common.h"
#include "Demos/Visualization/MiniGL.h"
#include "Demos/Visualization/Selection.h"
#include "GL/glut.h"
#include "Simulation/TimeManager.h"
#include <Eigen/Dense>
#include "Simulation/SimulationModel.h"
#include "Simulation/TimeStepController.h"
#include <iostream>
#include "Utils/OBJLoader.h"
#include "Demos/Visualization/Visualization.h"
#include "Utils/Logger.h"
#include "Utils/Timing.h"
#include "Utils/FileSystem.h"
#include "Demos/Common/DemoBase.h"
#include "Demos/Common/TweakBarParameters.h"
#include "Simulation/Simulation.h"

#define _USE_MATH_DEFINES
#include "math.h"

// Enable memory leak detection
#if defined(_DEBUG) && !defined(EIGEN_ALIGN)
	#define new DEBUG_NEW
#endif

using namespace PBD;
using namespace Eigen;
using namespace std;
using namespace Utilities;

void initParameters();
void timeStep ();
void buildModel ();
void createBodyModel();
void render ();
void reset();

DemoBase *base;

const Real width = static_cast<Real>(0.4);
const Real height = static_cast<Real>(0.4);
const Real depth = static_cast<Real>(2.0);

// main
int main( int argc, char **argv )
{
	REPORT_MEMORY_LEAKS

	base = new DemoBase();
	base->init(argc, argv, "Rigid body demo");

	SimulationModel *model = new SimulationModel();
	model->init();
	Simulation::getCurrent()->setModel(model);

	buildModel ();

	initParameters();

	Simulation::getCurrent()->setSimulationMethodChangedCallback([&]() { reset(); initParameters(); base->getSceneLoader()->readParameterObject(Simulation::getCurrent()->getTimeStep()); });

	// OpenGL
	MiniGL::setClientIdleFunc(50, timeStep);
	MiniGL::setKeyFunc(0, 'r', reset);
	MiniGL::setClientSceneFunc(render);
	MiniGL::setViewport(60.0, 0.1f, 500.0, Vector3r(6.0, 0.0, 18.0), Vector3r(6.0, -4.0, 0.0));

	glutMainLoop ();

	Utilities::Timing::printAverageTimes();
	Utilities::Timing::printTimeSums();

	delete Simulation::getCurrent();
	delete base;
	delete model;

	return 0;
}

void initParameters()
{
	TwRemoveAllVars(MiniGL::getTweakBar());
	TweakBarParameters::cleanup();

	MiniGL::initTweakBarParameters();

	TweakBarParameters::createParameterGUI();
	TweakBarParameters::createParameterObjectGUI(base);
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent());
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getModel());
	TweakBarParameters::createParameterObjectGUI(Simulation::getCurrent()->getTimeStep());
}

void reset()
{
	Utilities::Timing::printAverageTimes();
	Utilities::Timing::reset();

	Simulation::getCurrent()->reset();
	base->getSelectedParticles().clear();
}

void timeStep()
{
	const Real pauseAt = base->getValue<Real>(DemoBase::PAUSE_AT);
	if ((pauseAt > 0.0) && (pauseAt < TimeManager::getCurrent()->getTime()))
		base->setValue(DemoBase::PAUSE, true);

	if (base->getValue<bool>(DemoBase::PAUSE))
		return;

	// Simulation code
	SimulationModel *model = Simulation::getCurrent()->getModel();
	const unsigned int numSteps = base->getValue<unsigned int>(DemoBase::NUM_STEPS_PER_RENDER);
	for (unsigned int i = 0; i < numSteps; i++)
	{
		START_TIMING("SimStep");
		Simulation::getCurrent()->getTimeStep()->step(*model);
		STOP_TIMING_AVG;

		// set target angle of motors for an animation
		const Real currentTargetAngle = static_cast<Real>(M_PI * 0.5) - static_cast<Real>(M_PI * 0.5) * cos(static_cast<Real>(0.25)*TimeManager::getCurrent()->getTime());
		SimulationModel::ConstraintVector &constraints = model->getConstraints();
		TargetAngleMotorHingeJoint &joint1 = (*(TargetAngleMotorHingeJoint*)constraints[8]);
		TargetVelocityMotorHingeJoint &joint2 = (*(TargetVelocityMotorHingeJoint*)constraints[9]);
		joint1.setTarget(currentTargetAngle);
		joint2.setTarget(3.5);

		const Real currentTargetPos = static_cast<Real>(1.5)*sin(static_cast<Real>(2.0)*TimeManager::getCurrent()->getTime());
		TargetPositionMotorSliderJoint &joint3 = (*(TargetPositionMotorSliderJoint*)constraints[12]);
		joint3.setTarget(currentTargetPos);

		Real currentTargetVel = 0.25;
		if (((int)(0.25*TimeManager::getCurrent()->getTime())) % 2 == 1)
			currentTargetVel = -currentTargetVel;
		TargetVelocityMotorSliderJoint &joint4 = (*(TargetVelocityMotorSliderJoint*)constraints[14]);
		joint4.setTarget(currentTargetVel);
	}
}

void buildModel ()
{
	TimeManager::getCurrent ()->setTimeStepSize (static_cast<Real>(0.005));

	createBodyModel();
}

void render ()
{
	base->render();

	float textColor[4] = { 0.0, .2f, .4f, 1 };
	MiniGL::drawStrokeText(-0.5, 1.5, 1.0, 0.002f, "ball joint", 11, textColor);
	MiniGL::drawStrokeText(3.0, 1.5, 1.0, 0.002f, "ball-on-line joint", 19, textColor);
	MiniGL::drawStrokeText(7.3f, 1.5, 1.0, 0.002f, "hinge joint", 12, textColor);
	MiniGL::drawStrokeText(11.2f, 1.5, 1.0, 0.002f, "universal joint", 15, textColor);

	MiniGL::drawStrokeText(-1.0, -4.0, 1.0, 0.002f, "motor hinge joint", 17, textColor);
	MiniGL::drawStrokeText(3.4f, -4.0, 1.0, 0.002f, "slider joint", 12, textColor);
	MiniGL::drawStrokeText(6.6f, -4.0, 1.0, 0.002f, "target position motor", 21, textColor);
	MiniGL::drawStrokeText(10.6f, -4.0, 1.0, 0.002f, "target velocity motor", 21, textColor);

	MiniGL::drawStrokeText(-0.25, -9.5, 1.0, 0.002f, "spring", 6, textColor);
	MiniGL::drawStrokeText(3.3, -9.5, 1.0, 0.002f, "distance joint", 14, textColor);

	MiniGL::drawTime( TimeManager::getCurrent ()->getTime ());
}

// Compute diagonal inertia tensor
Vector3r computeInertiaTensorBox(const Real mass, const Real width, const Real height, const Real depth)
{
	const Real Ix = (mass / static_cast<Real>(12.0)) * (height*height + depth*depth);
	const Real Iy = (mass / static_cast<Real>(12.0)) * (width*width + depth*depth);
	const Real Iz = (mass / static_cast<Real>(12.0)) * (width*width + height*height);
	return Vector3r(Ix, Iy, Iz);
}

void loadObj(const std::string &filename, VertexData &vd, IndexedFaceMesh &mesh, const Vector3r &scale)
{
	std::vector<OBJLoader::Vec3f> x;
	std::vector<OBJLoader::Vec3f> normals;
	std::vector<OBJLoader::Vec2f> texCoords;
	std::vector<MeshFaceIndices> faces;
	OBJLoader::Vec3f s = { (float)scale[0], (float)scale[1], (float)scale[2] };
	OBJLoader::loadObj(filename, &x, &faces, &normals, &texCoords, s);

	mesh.release();
	const unsigned int nPoints = (unsigned int)x.size();
	const unsigned int nFaces = (unsigned int)faces.size();
	const unsigned int nTexCoords = (unsigned int)texCoords.size();
	mesh.initMesh(nPoints, nFaces * 2, nFaces);
	vd.reserve(nPoints);
	for (unsigned int i = 0; i < nPoints; i++)
	{
		vd.addVertex(Vector3r(x[i][0], x[i][1], x[i][2]));
	}
	for (unsigned int i = 0; i < nTexCoords; i++)
	{
		mesh.addUV(texCoords[i][0], texCoords[i][1]);
	}
	for (unsigned int i = 0; i < nFaces; i++)
	{
		// Reduce the indices by one
		int posIndices[3];
		int texIndices[3];
		for (int j = 0; j < 3; j++)
		{
			posIndices[j] = faces[i].posIndices[j] - 1;
			if (nTexCoords > 0)
			{
				texIndices[j] = faces[i].texIndices[j] - 1;
				mesh.addUVIndex(texIndices[j]);
			}
		}

		mesh.addFace(&posIndices[0]);
	}
	mesh.buildNeighbors();

	mesh.updateNormals(vd, 0);
	mesh.updateVertexNormals(vd);

	LOG_INFO << "Number of triangles: " << nFaces;
	LOG_INFO << "Number of vertices: " << nPoints;
}

/** Create the rigid body model
*/
void createBodyModel()
{
	SimulationModel *model = Simulation::getCurrent()->getModel();
	SimulationModel::RigidBodyVector &rb = model->getRigidBodies();

	string fileName = FileSystem::normalizePath(base->getDataPath() + "/models/cube.obj");
	IndexedFaceMesh mesh;
	VertexData vd;
	loadObj(fileName, vd, mesh, Vector3r(width, height, depth));
	IndexedFaceMesh meshStatic;
	VertexData vdStatic;
	loadObj(fileName, vdStatic, meshStatic, Vector3r(0.5, 0.5, 0.5));

	// static body
	const unsigned int numberOfBodies = 30;
	rb.resize(numberOfBodies);
	Real startX = 0.0;
	Real startY = 6.5;
	for (unsigned int i = 0; i < 10; i++)
	{
		if (i % 4 == 0)
		{
			startY -= 5.5;
			startX = 0.0;
		}

		rb[3*i] = new RigidBody();
		rb[3*i]->initBody(0.0,
			Vector3r(startX, startY, 1.0),
			computeInertiaTensorBox(1.0, 0.5, 0.5, 0.5),
			Quaternionr(1.0, 0.0, 0.0, 0.0),
			vdStatic, meshStatic);

		// dynamic body
		rb[3*i+1] = new RigidBody();
		rb[3*i+1]->initBody(1.0,
			Vector3r(startX, startY- static_cast<Real>(0.25), static_cast<Real>(2.0)),
			computeInertiaTensorBox(1.0, width, height, depth),
			Quaternionr(1.0, 0.0, 0.0, 0.0),
			vd, mesh);

		// dynamic body
		rb[3 * i + 2] = new RigidBody();
		rb[3 * i + 2]->initBody(1.0,
			Vector3r(startX, startY - static_cast<Real>(0.25), static_cast<Real>(4.0)),
			computeInertiaTensorBox(1.0, width, height, depth),
			Quaternionr(1.0, 0.0, 0.0, 0.0),
			vd, mesh);

		startX += 4.0;
	}

	Real jointY = 0.75;
	model->addBallJoint(0, 1, Vector3r(0.25, jointY, 1.0));
	model->addBallJoint(1, 2, Vector3r(0.25, jointY, 3.0));

	model->addBallOnLineJoint(3, 4, Vector3r(4.25, jointY, 1.0), Vector3r(1.0, 0.0, 0.0));
	model->addBallJoint(4, 5, Vector3r(4.25, jointY, 3.0));

	model->addHingeJoint(6, 7, Vector3r(8.0, jointY, 1.0), Vector3r(1.0, 0.0, 0.0));
	model->addBallJoint(7, 8, Vector3r(8.25, jointY, 3.0));

	model->addUniversalJoint(9, 10, Vector3r(12.0, jointY, 1.0), Vector3r(1.0, 0.0, 0.0), Vector3r(0.0, 1.0, 0.0));
	model->addBallJoint(10, 11, Vector3r(12.25, jointY, 3.0));

	jointY -= 5.5;
	model->addTargetAngleMotorHingeJoint(12, 13, Vector3r(0.0, jointY, 1.0), Vector3r(1.0, 0.0, 0.0));
	model->addTargetVelocityMotorHingeJoint(13, 14, Vector3r(0.0, jointY, 3.0), Vector3r(0.0, 1.0, 0.0));

	model->addSliderJoint(15, 16, Vector3r(1.0, 0.0, 0.0));
	model->addBallJoint(16, 17, Vector3r(4.25, jointY, 3.0));

	model->addTargetPositionMotorSliderJoint(18, 19, Vector3r(1.0, 0.0, 0.0));
	model->addBallJoint(19, 20, Vector3r(8.25, jointY, 3.0));

	model->addTargetVelocityMotorSliderJoint(21, 22, Vector3r(1.0, 0.0, 0.0));
	model->addBallJoint(22, 23, Vector3r(12.25, jointY, 3.0));

	jointY -= 5.5;
	model->addRigidBodySpring(24, 25, Vector3r(0.25, jointY, 1.0), Vector3r(0.25, jointY, 1.0), 50.0);
	model->addBallJoint(25, 26, Vector3r(0.25, jointY, 3.0));

	model->addDistanceJoint(27, 28, Vector3r(4.25, jointY, 1.0), Vector3r(4.25, jointY, 2.0));
	model->addBallJoint(28, 29, Vector3r(4.25, jointY, 3.0));
}


#include <stan/math/prim.hpp>
#include <test/unit/math/prim/prob/vector_rng_test_helper.hpp>
#include <test/unit/math/prim/prob/util.hpp>
#include <boost/math/distributions.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <gtest/gtest.h>
#include <limits>
#include <vector>

class StudentTTestRig : public VectorRealRNGTestRig {
 public:
  StudentTTestRig()
      : VectorRealRNGTestRig(10000, 10, {1.1, 2.0, 2.5, 3.1}, {1, 2, 3, 4},
                             {-1.7, -0.5, -2.5, 0.0}, {-2, -1, -3, 0},
                             {-2.5, -1.7, -0.1, 0.0, 2.0, 5.8},
                             {-3, -2, -1, 0, 2, 6}, {}, {},
                             {0.1, 1.0, 2.5, 4.0}, {1, 2, 3, 4},
                             {-2.7, -1.5, -0.5, 0.0}, {-3, -2, -1, 0}) {}

  template <typename T1, typename T2, typename T3, typename T_rng>
  auto generate_samples(const T1& nu, const T2& mu, const T3& sigma,
                        T_rng& rng) const {
    return stan::math::student_t_rng(nu, mu, sigma, rng);
  }

  std::vector<double> generate_quantiles(double nu, double mu,
                                         double sigma) const {
    std::vector<double> quantiles;
    double K = stan::math::round(2 * std::pow(N_, 0.4));

    boost::math::students_t_distribution<> dist(nu);
    for (int i = 1; i < K; ++i) {
      double frac = i / K;
      quantiles.push_back(quantile(dist, frac) * sigma + mu);
    }
    quantiles.push_back(std::numeric_limits<double>::max());

    return quantiles;
  }
};

TEST(ProbDistributionsStudentT, errorCheck) {
  check_dist_throws_all_types(StudentTTestRig());
}

TEST(ProbDistributionsStudentT, distributionTest) {
  check_quantiles_real_real_real(StudentTTestRig());
}

TEST(ProbDistributionsStudentT, error_check) {
  boost::random::mt19937 rng;
  EXPECT_NO_THROW(stan::math::student_t_rng(3.0, 2.0, 2.0, rng));

  EXPECT_THROW(stan::math::student_t_rng(3.0, 2.0, -2.0, rng),
               std::domain_error);
  EXPECT_THROW(stan::math::student_t_rng(-3.0, 2.0, 2.0, rng),
               std::domain_error);
  EXPECT_THROW(
      stan::math::student_t_rng(stan::math::positive_infinity(), 2.0, 2.0, rng),
      std::domain_error);
  EXPECT_THROW(
      stan::math::student_t_rng(3, stan::math::positive_infinity(), 2.0, rng),
      std::domain_error);
  EXPECT_THROW(
      stan::math::student_t_rng(3, 2, stan::math::positive_infinity(), rng),
      std::domain_error);
}

TEST(ProbDistributionsStudentT, chiSquareGoodnessFitTest) {
  boost::random::mt19937 rng;
  int N = 10000;
  int K = stan::math::round(2 * std::pow(N, 0.4));

  std::vector<double> samples;
  for (int i = 0; i < N; ++i) {
    samples.push_back((stan::math::student_t_rng(3.0, 2.0, 2.0, rng) - 2.0)
                      / 2.0);
  }

  // Generate quantiles from boost's student t distribution
  boost::math::students_t_distribution<> dist(3.0);
  std::vector<double> quantiles;
  for (int i = 1; i < K; ++i) {
    double frac = static_cast<double>(i) / K;
    quantiles.push_back(quantile(dist, frac));
  }
  quantiles.push_back(std::numeric_limits<double>::max());

  // Assert that they match
  assert_matches_quantiles(samples, quantiles, 1e-6);
}


/**
 * Copyright (c) 2012 Andreas Heider, Julius Adorf, Markus Grimm
 *
 * MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

#include "tpofinder/util.h"

#include <boost/program_options.hpp>

using namespace cv;
using namespace tpofinder;
using namespace std;
namespace bpo = boost::program_options;

int main(int argc, char** argv) {
    bpo::options_description options;
    string ifile, ofile;
    options.add_options()
            ("in,i", bpo::value<string > (&ifile), "file where the "
            "homography is read from.")
            ("out,o", bpo::value<string > (&ofile), "file where the inverted "
            "homography is written to.");
    bpo::positional_options_description posopts;
    posopts.add("in", 1);
    posopts.add("out", 1);

    bpo::variables_map vm;
    bpo::store(bpo::command_line_parser(argc, argv).
            options(options).positional(posopts).run(), vm);
    bpo::notify(vm);

    if (ifile.empty() || ofile.empty()) {
        cerr << "Usage: invert_homography <in-file> <out-file>" << endl;
        options.print(cerr);
        return -1;
    }

    invertHomography(ifile, ofile);

    return 0;
}


#include <CGAL/basic.h>
#include <CGAL/function_objects.h>
#include <boost/functional.hpp>
#include <functional>
#include <algorithm>
#include <numeric>
#include <cassert>

using CGAL::compose1_1;
using CGAL::compose1_2;
using CGAL::compose2_1;
using CGAL::compose2_2;
using CGAL::compare_to_less;
using boost::binder1st;
using boost::bind1st;
using std::accumulate;
using std::plus;
using std::multiplies;
using std::divides;
using std::transform;
using std::equal;

struct Myf {
  typedef double result_type;
  result_type operator()(int a, int b, int c, int d, int e) const
  { return a + (b * c - d) * e; }
};

int main()
{
  plus< int >        pl;
  multiplies< int >  mu;
  binder1st< plus< int > >       op1 = bind1st(pl, 1);
  binder1st< multiplies< int > > op2 = bind1st(mu, 2);

  // compose1_2:
  int a[] = {3,5,7,2,4};
  int r = accumulate(a, a + 5, 0, compose1_2(op2, pl));
  assert(r == 248);

  // compose2_2:
  r = accumulate(a, a + 5, 0, compose2_2(pl, op1, op2));
  assert(r == 47);

  // compose1_1:
  transform(a, a + 5, a, compose1_1(op1, op2));
  int b[] = {7,11,15,5,9};
  assert(equal(a, a + 5, b));

  // compose2_1:
  transform(b, b + 5, a, compose2_1(pl, op2, op2));
  transform(b, b + 5, b, bind1st(mu, 4));
  assert(equal(a, a + 5, b));

  // compare_to_less
  bool boo = compare_to_less(CGAL::Compare<int>())(3,5);
  (void) boo;

  return 0;
}


#include <bio/hmm_dna.h>
USING_BIO_NS

#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/test/unit_test.hpp>

#include <iostream>
#include <sstream>
using namespace std;

//#define VERBOSE_CHECKING

BIO_NS_START

/**
Functor to check conversion.
*/
struct ConversionChecker
{
	template <unsigned order>
	void check(const seq_t & seq) const
	{
		//convert to emissions
		typename DnaHmm<order>::emission_seq_t emissions;
		DnaHmm<order>::convert_to_emission(seq, emissions);

		//convert back again
		seq_t seq_copy;
		DnaHmm<order>::convert_to_dna(emissions, seq_copy);

		//length that should be equal
		unsigned len = (seq.size() / (order + 1)) * (order + 1);
		BOOST_CHECK_EQUAL(emissions.size(), seq.size() / (order + 1));
		BOOST_CHECK_EQUAL(seq_copy.size(), len);
		BOOST_CHECK_EQUAL(seq_copy, seq.substr(0, len));
	}

	void operator()(const seq_t & seq) const
	{
		check<0>(seq);
		check<1>(seq);
		check<2>(seq);
		check<3>(seq);
		check<4>(seq);
		check<5>(seq);
		check<6>(seq);
		check<7>(seq);
		check<8>(seq);
		check<9>(seq);
		check<10>(seq);
		check<11>(seq);
		check<12>(seq);
		check<13>(seq);
		check<14>(seq);
		check<15>(seq);
	}
};

BIO_NS_END

void
check_random_dna_creation()
{
	cout << "******* check_random_dna_creation()" << endl;

	DnaHmmOrderNumStateMap::singleton().insert_model(1, 0, create_dna_model(1, 0));
	DnaHmmOrderNumStateMap::singleton().get_model(1, 0);
	for (unsigned i = 0; 100 != i; ++i)
	{
		seq_t seq;
		DnaHmmOrderNumStateMap::singleton().gen_sequence_from_random_hmm(seq, i);
		BOOST_CHECK_EQUAL(seq.size(), i);

#ifdef VERBOSE_CHECKING
		std::cout << seq << std::endl;
#endif
	}
};

void
check_unsigned_power()
{
	cout << "******* check_unsigned_power()" << endl;

	BOOST_CHECK_EQUAL(1u, unsigned_power(1, 0));
	BOOST_CHECK_EQUAL(1u, unsigned_power(2, 0));
	BOOST_CHECK_EQUAL(1u, unsigned_power(3, 0));
	BOOST_CHECK_EQUAL(1u, unsigned_power(4, 0));

	BOOST_CHECK_EQUAL(0u, unsigned_power(0, 1));
	BOOST_CHECK_EQUAL(1u, unsigned_power(1, 1));
	BOOST_CHECK_EQUAL(2u, unsigned_power(2, 1));
	BOOST_CHECK_EQUAL(3u, unsigned_power(3, 1));
	BOOST_CHECK_EQUAL(4u, unsigned_power(4, 1));

	BOOST_CHECK_EQUAL(0u, unsigned_power(0, 2));
	BOOST_CHECK_EQUAL(1u, unsigned_power(1, 2));
	BOOST_CHECK_EQUAL(4u, unsigned_power(2, 2));
	BOOST_CHECK_EQUAL(9u, unsigned_power(3, 2));
	BOOST_CHECK_EQUAL(16u, unsigned_power(4, 2));

	BOOST_CHECK_EQUAL(0u, unsigned_power(0, 3));
	BOOST_CHECK_EQUAL(1u, unsigned_power(1, 3));
	BOOST_CHECK_EQUAL(8u, unsigned_power(2, 3));
	BOOST_CHECK_EQUAL(27u, unsigned_power(3, 3));
	BOOST_CHECK_EQUAL(64u, unsigned_power(4, 3));
};

void
check_emission_dna_conversion()
{
	cout << "******* check_emission_dna_conversion()" << endl;

	using namespace boost::assign;
	const SeqList test_seqs =
		list_of
			("")
			("A")
			("C")
			("G")
			("T")
			("AAAA")
			("CCCC")
			("GGGG")
			("TTTT")
			("ACGTACGTACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")
			("ACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGATACGTACGTACGTATCGATCGAT")
			;

	std::for_each(test_seqs.begin(), test_seqs.end(), ConversionChecker());
}

void
check_hmm_map()
{
	cout << "******* check_hmm_map()" << endl;

	using namespace boost::assign;
	const SeqList training_data =
		list_of
			("acgtacgtacgtatcgatcgat")
			("acgtacgtacgtatcgatcgat")
			("acgtacgtacgtatcgatcgat")
			("acgtacgtacgtatcgatcgat")
			("acgtacgtacgtatcgatcgat")
			;

	DnaHmmOrderNumStateMap hmm_map;
	hmm_map.insert_model(1, 0, create_dna_model(1, 0));
	DnaModel & model = hmm_map.get_model(1, 0);
	model.train(SequenceCollectionList(training_data));
	const prob_t avg_prob_1 = model.get_likelihood(SequenceCollectionList(training_data));
	model.train(SequenceCollectionList(training_data));
	const prob_t avg_prob_2 = model.get_likelihood(SequenceCollectionList(training_data));
	BOOST_CHECK(avg_prob_1 >= avg_prob_2);
}

/** Functor to check training increases sequences' likelihoods. */
struct CheckTrainingIncreasesLikelihoods
{
	typedef std::list<DnaHmmOrderNumStateMap::Index> IndexList;

	unsigned training_iterations;
	const IndexList & hmm_indices;

	CheckTrainingIncreasesLikelihoods(
		const IndexList & hmm_indices,
		unsigned training_iterations = 4)
		: training_iterations(training_iterations)
		, hmm_indices(hmm_indices)
	{
	}

	void operator()(const seq_t & test_seq) const
	{
#ifdef VERBOSE_CHECKING
		std::cout << "\"" << test_seq << "\"\n";
#endif

		DnaHmmOrderNumStateMap hmm_map;

		//train each model
		for (IndexList::const_iterator idx = hmm_indices.begin();
			hmm_indices.end() != idx;
			++idx)
		{

			hmm_map.insert_model(idx->num_states, idx->order, create_dna_model(idx->num_states, idx->order));
			DnaModel & model = hmm_map.get_model(idx->num_states, idx->order);

			const prob_t likelihood_before_training = model.get_likelihood(test_seq);

			for (unsigned i = 0; training_iterations != i; ++i)
			{
				model.train(test_seq);
			}

			const prob_t likelihood_after_training = model.get_likelihood(test_seq);

#ifdef VERBOSE_CHECKING
			std::cout
				<< '(' << idx->num_states << ',' << idx->order << "): "
				<< likelihood_before_training << ", "
				<< likelihood_after_training << '\n';
#endif

			//check we are not more than 5% worse than previously
			BOOST_CHECK(likelihood_after_training >= likelihood_before_training - 0.05);
		}
	}
};

void
check_hmm_single_training()
{
	cout << "******* check_hmm_single_training()" << endl;

	const SeqList test_seqs = boost::assign::list_of
		("AGCTATCAGTCGATGATCGATGCTAGTCG")
		("")
		("c")
		("atatatatatatatatatatatatatatat")
		("AGCTATCAGTCGATGATCGATGCTAGTCG")
		("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
		("cgcgcgcgcgcgcgcgcgcgcgc")
		("agacgattagttagatggcatcgagctaatatcagcagctaatagcgc")
		;

	CheckTrainingIncreasesLikelihoods::IndexList hmm_indices = boost::assign::list_of
		(DnaHmmOrderNumStateMap::Index(1, 0))
		(DnaHmmOrderNumStateMap::Index(2, 0))
		(DnaHmmOrderNumStateMap::Index(3, 0))
		(DnaHmmOrderNumStateMap::Index(1, 1))
		(DnaHmmOrderNumStateMap::Index(2, 1))
		(DnaHmmOrderNumStateMap::Index(3, 1))
		(DnaHmmOrderNumStateMap::Index(1, 2))
		(DnaHmmOrderNumStateMap::Index(2, 2))
		(DnaHmmOrderNumStateMap::Index(3, 2))
		(DnaHmmOrderNumStateMap::Index(1, 3))
		(DnaHmmOrderNumStateMap::Index(2, 3))
		(DnaHmmOrderNumStateMap::Index(3, 3))
		;
	CheckTrainingIncreasesLikelihoods checker(hmm_indices, 3);

	std::for_each(test_seqs.begin(), test_seqs.end(), checker);
}

void
check_hmm_serialization()
{
	cout << "******* check_hmm_serialization()" << endl;

	using namespace boost::archive;

	DnaHmmOrderNumStateMap hmm_map;
	hmm_map.insert_model(1, 0, create_dna_model(1, 0));
	hmm_map.get_model(1, 0);

	//train it on a sequence
	const std::string test_seq = "AGCTATCAGTCGATGATCGATGCTAGTCG";
	hmm_map.get_model(1, 0).train(test_seq);
	BOOST_CHECK_CLOSE(
		hmm_map.get_model(1, 0).get_likelihood(test_seq),
		hmm_map.get_model(1, 0).get_likelihood(test_seq),
		0.001);

	{
		std::stringstream stream;
		text_oarchive(stream) << const_cast<const DnaHmmOrderNumStateMap &>(hmm_map);
		DnaHmmOrderNumStateMap hmm_map_copy;
		text_iarchive(stream) >> hmm_map_copy;

		BOOST_CHECK_CLOSE(
			hmm_map			.get_model(1, 0).get_likelihood(test_seq),
			hmm_map_copy	.get_model(1, 0).get_likelihood(test_seq),
			0.001);
	}

	{
		std::stringstream stream;
		binary_oarchive(stream) << const_cast<const DnaHmmOrderNumStateMap &>(hmm_map);
		DnaHmmOrderNumStateMap hmm_map_copy;
		binary_iarchive(stream) >> hmm_map_copy;

		BOOST_CHECK_CLOSE(
			hmm_map			.get_model(1, 0).get_likelihood(test_seq),
			hmm_map_copy	.get_model(1, 0).get_likelihood(test_seq),
			0.001);
	}
}

void register_hmm_dna_tests(boost::unit_test::test_suite * test)
{
	test->add(BOOST_TEST_CASE(&check_hmm_serialization), 0);
	test->add(BOOST_TEST_CASE(&check_hmm_single_training), 0);
	test->add(BOOST_TEST_CASE(&check_unsigned_power), 0);
	test->add(BOOST_TEST_CASE(&check_random_dna_creation), 0);
	test->add(BOOST_TEST_CASE(&check_emission_dna_conversion), 0);
	test->add(BOOST_TEST_CASE(&check_hmm_map), 0);

	//test->add(BOOST_TEST_CASE(&compare_hmm_sizes), 0);
}


#include <QtTest>

#include <iostream>

#include <thermostat.hpp>

class test_thermostat : public QObject
{
    Q_OBJECT
public:
    test_thermostat(){}
    ~test_thermostat(){}

private slots:
    void test_set_temperature();
    void test_output_below_set_temperature();
    void test_output_above_set_temperature();
    void test_output_hysteresis();

private:
    Thermostat cont;

};

void test_thermostat::test_set_temperature()
{
    const auto T=27.5;
    cont.setTargetTemperature(T);
    QVERIFY(cont.getTargetTemperature() == T);
}

void test_thermostat::test_output_below_set_temperature()
{
    const auto Tset = 25.0;
    cont.setTargetTemperature(Tset);

    const auto Tin = Tset-0.7;
    const auto controller_output = cont.getOutput(Tin);
    QVERIFY(controller_output == Thermostat::ON);

}

void test_thermostat::test_output_above_set_temperature()
{
    const auto Tset = 25.0;
    cont.setTargetTemperature(Tset);

    const auto Tin = Tset + 1.0;
    const auto controller_output = cont.getOutput(Tin);
    QVERIFY(controller_output == Thermostat::OFF);
}

#include <boost/range/combine.hpp>

void test_thermostat::test_output_hysteresis()
{
    const auto Tset = 25.0;
    const auto hysteresis = 0.5; // temperature has to drop by hysteresis before we switch on again, then has to rise until Tset

    cont.setTargetTemperature(Tset);
    cont.setHysteresis(hysteresis);

    const auto Tin = {25.5, 24.9, 24.0, 24.9, 25.01};
    constexpr auto ON = Thermostat::ON;
    constexpr auto OFF = Thermostat::OFF;
    const auto ExpectOut = {OFF, OFF, ON, ON, OFF};

    for (const auto tup : boost::combine(Tin, ExpectOut))
    {
        double T;
        Thermostat::output_t E;
        boost::tie(T, E) = tup;

        std::cout << "Tin: " << T << std::endl;
        QVERIFY(E == cont.getOutput(T));
    }
}

QTEST_APPLESS_MAIN(test_thermostat)

#include "test_thermostat.moc"


#include <boost/geometry/algorithms/is_convex.hpp>


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_GENERIC_FUNCTION_ALL_REDUCE_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_GENERIC_FUNCTION_ALL_REDUCE_HPP_INCLUDED

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/function/shuffle.hpp>
#include <boost/simd/function/combine.hpp>
#include <boost/simd/detail/dispatch/detail/declval.hpp>
#include <boost/simd/detail/nsm.hpp>

namespace boost { namespace simd { namespace detail
{
  namespace tt = nsm::type_traits;

  //------------------------------------------------------------------------------------------------
  // This meta-permutation implements the butterfly pattern required for log-tree based reductions.
  //
  // V is a vector of cardinal 4 :          [ v0 | v1 | v2 | v3 ]
  // The log-tree reduction will require :  [ v2 | v3 | v0 | v1 ]
  // and :                                  [ v3 | v2 | v1 | v0 ]
  //
  // Basically this requires permuting cardinal/2^n value at each iteration
  // stopping when only 1 element is left to be permuted.
  //------------------------------------------------------------------------------------------------
  template<int Step> struct butterfly_perm
  {
    template<typename I, typename>
    struct  apply : tt::integral_constant<int,(I::value >= Step) ? I::value-Step : I::value+Step>
    {};
  };

  template<int Step> struct butterfly
  {
    using next_t = butterfly<Step/2>;
    template<typename Op, typename V>
    BOOST_FORCEINLINE
    auto  operator()(Op const& op, V const& a0) const BOOST_NOEXCEPT
          -> decltype( next_t{}( op, op(a0, boost::simd::shuffle< butterfly_perm<Step> >(a0)) ) )
    {
      next_t next;
      return next( op, op(a0, boost::simd::shuffle< butterfly_perm<Step> >(a0)) );
    }
  };

  template<> struct butterfly<1>
  {
    template<typename Op, typename V>
    BOOST_FORCEINLINE
    auto  operator()(Op const& op, V const& a0) const BOOST_NOEXCEPT
          ->  decltype( op(boost::simd::shuffle< butterfly_perm<1> >(a0),a0) )
    {
      return op(boost::simd::shuffle< butterfly_perm<1> >(a0),a0);
    }
  };
} } }

namespace boost { namespace simd { namespace ext
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;

  BOOST_DISPATCH_OVERLOAD_FALLBACK  ( ( typename BinOp, typename Neutral
                                      , typename Arg, typename Ext
                                      )
                                    , tag::all_reduce_
                                    , bd::cpu_
                                    , bd::elementwise_<BinOp>
                                    , bs::pack_<bd::unspecified_<Neutral>, Ext>
                                    , bs::pack_<bd::unspecified_<Arg>, Ext>
                                    )
  {
    using function_t  = bd::functor<BinOp>;
    using result_t    = decltype( bd::functor<BinOp>()( bd::detail::declval<Arg>()
                                                      , bd::detail::declval<Neutral>()
                                                      )
                                );

    // ---------------------------------------------------------------------------------------------
    // singleton case
    template<typename K, typename N> static BOOST_FORCEINLINE
    result_t fold_( function_t const& op, Neutral const& z, Arg const& a0
                  , K const&, nsm::list<N> const&
                  )
    {
      return op( z, a0 );
    }

    // ---------------------------------------------------------------------------------------------
    // Native case
    template<typename N0, typename N1, typename... N> static BOOST_FORCEINLINE
    result_t fold_( function_t const& op, Neutral const& z, Arg const& a0
                  , native_storage const&, nsm::list<N0,N1,N...> const&
                  )
    {
      return op(detail::butterfly<Arg::static_size/2>{}(op,a0),z);
    }

    // ---------------------------------------------------------------------------------------------
    // Aggregate case
    template<typename N0, typename N1, typename... N> static BOOST_FORCEINLINE
    result_t fold_( function_t const& op, Neutral const& z, Arg const& a0
                  , aggregate_storage const&, nsm::list<N0,N1,N...> const&
                  )
    {
      auto  r = detail::all_reduce(op,z.storage()[0],a0.storage()[0]);
            r = detail::all_reduce(op,r,a0.storage()[1]);
      return combine(r,r);
    }

    // ---------------------------------------------------------------------------------------------
    // Scalar case
    template<typename N0, typename N1, typename... N> static BOOST_FORCEINLINE
    result_t fold_( function_t const& op, Neutral const& z, Arg const& a0
                  , scalar_storage const&, nsm::list<N0,N1,N...> const&
                  )
    {
      auto r = op( bs::extract<0>(a0), z );
           r = op( bs::extract<1>(a0), r );

      (void)std::initializer_list<bool> { ((r = op(bs::extract<N::value>(a0),r)),true)... };

      return result_t(r);
    }

    BOOST_FORCEINLINE result_t
    operator()(function_t const& op, Neutral const& z, Arg const& a0) const
    {
      return fold_(op, z, a0, typename Arg::storage_kind{}, typename Arg::traits::static_range{});
    }
  };
} } }

#endif


#pragma once

#include <list>
#include <deque>
#include <vector>

#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <opencv2/calib3d/calib3d.hpp>

#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>

#include "rcvio/ransac.hpp"
#include "rcvio/feature_detector.hpp"

namespace rcvio
{
    class Tracker
    {
    public:
        POINTER_TYPEDEFS(Tracker);
        EIGEN_MAKE_ALIGNED_OPERATOR_NEW

        Tracker(const cv::FileStorage &fs_settings);

        void track(const cv::Mat &image, std::list<ImuData *> &imu_data);

    private:
        template <typename T1, typename T2>
        void undistort(const int N, T1 &src, T2 &dst);

        void displayTrack(const cv::Mat &image_in,
                          std::vector<cv::Point2f> &points_1,
                          std::vector<cv::Point2f> &points_2,
                          std::vector<unsigned char> &inlier_flag,
                          cv_bridge::CvImage &image_out);

        void displayNewer(const cv::Mat &image_in,
                          std::vector<cv::Point2f> &features,
                          std::deque<cv::Point2f> &new_features,
                          cv_bridge::CvImage &image_out);

    public:
        std::vector<unsigned char> feature_types_for_update_;
        std::vector<std::list<cv::Point2f>> feature_measurements_for_update_;

    private:
        int max_features_per_image_;
        int max_features_for_update_;

        int max_tracking_length_;
        int min_tracking_length_;

        bool is_rgb_;
        bool is_fisheye_;
        bool is_the_first_image_;

        bool enable_equalizer_;

        cv::Mat K_;
        cv::Mat D_;

        cv::Mat last_image_;

        std::vector<std::list<cv::Point2f>> tracking_history_;

        std::vector<int> inlier_indices_;

        std::list<int> free_indices_;

        std::vector<cv::Point2f> features_to_track_;
        int num_features_to_track_;

        Eigen::MatrixXd points_1_for_ransac_;
        Eigen::MatrixXd points_2_for_ransac_;

        FeatureDetector::Ptr feature_detector_;
        Ransac::Ptr ransac_;

        ros::NodeHandle tracker_node_;
        ros::Publisher track_publish_;
        ros::Publisher newer_publish_;
    };

    template <typename T1, typename T2>
    void Tracker::undistort(const int N, T1 &src, T2 &dst)
    {
        cv::Mat mat(N, 2, CV_32F);

        for (int i = 0; i < N; i++)
        {
            mat.at<float>(i, 0) = src.at(i).x;
            mat.at<float>(i, 1) = src.at(i).y;
        }

        mat = mat.reshape(2);
        if (!is_fisheye_)
        {
            cv::undistortPoints(mat, mat, K_, D_);
        }
        else
        {
            cv::fisheye::undistortPoints(mat, mat, K_, D_);
        }
        mat = mat.reshape(1);

        dst.clear();
        for (int i = 0; i < N; ++i)
        {
            cv::Point2f pt_un;
            pt_un.x = mat.at<float>(i, 0);
            pt_un.y = mat.at<float>(i, 1);

            dst.push_back(pt_un);
        }
    }
}


/*
 * Copyright Andrey Semashev 2020
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * https://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file rotating.hpp
 *
 * This header includes all algorithms for rotating integers.
 */

#ifndef BOOST_BIT_OPS_ROTATING_HPP_INCLUDED_
#define BOOST_BIT_OPS_ROTATING_HPP_INCLUDED_

#include <boost/bit_ops/rotating/rotl.hpp>
#include <boost/bit_ops/rotating/rotr.hpp>

#endif // BOOST_BIT_OPS_ROTATING_HPP_INCLUDED_


// Copyright 2019, Intel Corporation

#include "pmlc/dialect/stripe/affine_poly.h"

#include <utility>

#include <boost/math/common_factor.hpp>

#include "pmlc/dialect/stripe/dialect.h"
#include "pmlc/dialect/stripe/ops.h"

namespace pmlc {
namespace dialect {
namespace stripe {

AffinePolynomial::AffinePolynomial() : constant(0) {}

AffinePolynomial::AffinePolynomial(int64_t x) : constant(x) {}

AffinePolynomial::AffinePolynomial(Value* value) : constant(0) {
  if (auto arg = mlir::dyn_cast<mlir::BlockArgument>(value)) {
    // This is a basic index: done
    terms.emplace(arg, 1);
    return;
  }
  auto defOp = value->getDefiningOp();
  if (auto op = mlir::dyn_cast<AffinePolyOp>(defOp)) {
    constant = op.offset().getSExtValue();
    for (size_t i = 0; i < op.coeffs().size(); i++) {
      *this += AffinePolynomial(op.getOperand(i)) * op.getCoeff(i);
    }
  } else {
    throw std::runtime_error("Invalid affine in ComputeAffineRange");
  }
}

AffinePolynomial& AffinePolynomial::operator*=(int64_t x) {
  constant *= x;
  if (x == 0) {
    terms.clear();
  } else {
    for (auto& kvp : terms) {
      kvp.second *= x;
    }
  }
  return *this;
}

AffinePolynomial& AffinePolynomial::operator+=(const AffinePolynomial& x) {
  constant += x.constant;
  for (const auto& kvp : x.terms) {
    terms[kvp.first] += kvp.second;
    if (terms[kvp.first] == 0) {
      terms.erase(kvp.first);
    }
  }
  return *this;
}

bool AffinePolynomial::operator<(const AffinePolynomial& rhs) const {
  if (constant < rhs.constant) {
    return true;
  }
  if (constant > rhs.constant) {
    return false;
  }
  return terms < rhs.terms;
}

bool AffinePolynomial::operator==(const AffinePolynomial& rhs) const {
  return constant == rhs.constant && terms == rhs.terms;
}

}  // namespace stripe
}  // namespace dialect
}  // namespace pmlc


/*
 * _build_in.cpp
 *
 *  Created on: 2013-2-14
 *      Author: fasiondog
 */

#include <boost/python.hpp>
#include <hikyuu/indicator/build_in.h>

using namespace hku;
using namespace boost::python;

Indicator (*PRICELIST2)(const PriceList&, int) = PRICELIST;
Indicator (*PRICELIST3)(const Indicator&, int) = PRICELIST;
Indicator (*PRICELIST4)(int) = PRICELIST;

Indicator (*KDATA1)(const KData&) = KDATA;
Indicator (*KDATA3)() = KDATA;

Indicator (*OPEN1)(const KData&) = OPEN;
Indicator (*OPEN3)() = OPEN;

Indicator (*HIGH1)(const KData&) = HIGH;
Indicator (*HIGH3)() = HIGH;

Indicator (*LOW1)(const KData&) = LOW;
Indicator (*LOW3)() = LOW;

Indicator (*CLOSE1)(const KData&) = CLOSE;
Indicator (*CLOSE3)() = CLOSE;

Indicator (*AMO1)(const KData&) = AMO;
Indicator (*AMO3)() = AMO;

Indicator (*VOL1)(const KData&) = VOL;
Indicator (*VOL3)() = VOL;

Indicator (*KDATA_PART1)(const KData& kdata, const string& part) = KDATA_PART;
Indicator (*KDATA_PART3)(const string& part) = KDATA_PART;

Indicator (*AMA_1)(int, int, int) = AMA;
Indicator (*AMA_2)(const Indicator&, int, int, int) = AMA;

Indicator (*ATR_1)(int) = ATR;
Indicator (*ATR_2)(const Indicator&, int) = ATR;

Indicator (*DIFF_1)() = DIFF;
Indicator (*DIFF_2)(const Indicator&) = DIFF;

Indicator (*MA_1)(int) = MA;
Indicator (*MA_2)(const IndParam&) = MA;
Indicator (*MA_3)(const Indicator&, const IndParam&) = MA;
Indicator (*MA_4)(const Indicator&, const Indicator&) = MA;
Indicator (*MA_5)(const Indicator&, int) = MA;

Indicator (*SMA_1)(int, double) = SMA;
Indicator (*SMA_2)(const Indicator&, int, double) = SMA;

Indicator (*EMA_1)(int) = EMA;
Indicator (*EMA_2)(const Indicator&, int) = EMA;

Indicator (*MACD_1)(int, int, int) = MACD;
Indicator (*MACD_2)(const Indicator&, int, int, int) = MACD;
// BOOST_PYTHON_FUNCTION_OVERLOADS(MACD_1_overload, MACD, 0, 3);
// BOOST_PYTHON_FUNCTION_OVERLOADS(MACD_2_overload, MACD, 1, 4);

Indicator (*REF_1)(int) = REF;
Indicator (*REF_2)(const Indicator&, int) = REF;

Indicator (*SAFTYLOSS_1)(int n1, int n2, double p) = SAFTYLOSS;
Indicator (*SAFTYLOSS_2)(const Indicator&, int n1, int n2, double p) = SAFTYLOSS;
// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_1_overload, SAFTYLOSS, 0, 3);
// BOOST_PYTHON_FUNCTION_OVERLOADS(SAFTYLOSS_2_overload, SAFTYLOSS, 1, 4);

Indicator (*STDEV_1)(int) = STDEV;
Indicator (*STDEV_2)(const IndParam&) = STDEV;
Indicator (*STDEV_3)(const Indicator&, const IndParam&) = STDEV;
Indicator (*STDEV_4)(const Indicator&, const Indicator&) = STDEV;
Indicator (*STDEV_5)(const Indicator&, int) = STDEV;

Indicator (*STDP_1)(int) = STDP;
Indicator (*STDP_2)(const IndParam&) = STDP;
Indicator (*STDP_3)(const Indicator&, const IndParam&) = STDP;
Indicator (*STDP_4)(const Indicator&, const Indicator&) = STDP;
Indicator (*STDP_5)(const Indicator&, int) = STDP;

Indicator (*HHV_1)(int) = HHV;
Indicator (*HHV_2)(const IndParam&) = HHV;
Indicator (*HHV_3)(const Indicator&, const Indicator&) = HHV;
Indicator (*HHV_4)(const Indicator&, const IndParam&) = HHV;
Indicator (*HHV_5)(const Indicator&, int) = HHV;

Indicator (*LLV_1)(int) = LLV;
Indicator (*LLV_2)(const IndParam&) = LLV;
Indicator (*LLV_3)(const Indicator&, const IndParam&) = LLV;
Indicator (*LLV_4)(const Indicator&, const Indicator&) = LLV;
Indicator (*LLV_5)(const Indicator&, int) = LLV;

Indicator (*VIGOR_1)(const KData&, int) = VIGOR;
Indicator (*VIGOR_2)(int) = VIGOR;

Indicator (*CVAL_1)(double, size_t) = CVAL;
Indicator (*CVAL_2)(const Indicator&, double, int) = CVAL;

Indicator (*LIUTONGPAN_1)() = LIUTONGPAN;
Indicator (*LIUTONGPAN_2)(const KData&) = LIUTONGPAN;

Indicator (*HSL_1)() = HSL;
Indicator (*HSL_2)(const KData&) = HSL;

Indicator (*IF_1)(const Indicator&, const Indicator&, const Indicator&) = IF;
Indicator (*IF_2)(const Indicator&, price_t, const Indicator&) = IF;
Indicator (*IF_3)(const Indicator&, const Indicator&, price_t) = IF;
Indicator (*IF_4)(const Indicator&, price_t, price_t) = IF;

Indicator (*COUNT_1)(int) = COUNT;
Indicator (*COUNT_2)(const IndParam&) = COUNT;
Indicator (*COUNT_3)(const Indicator&, const Indicator&) = COUNT;
Indicator (*COUNT_4)(const Indicator&, const IndParam&) = COUNT;
Indicator (*COUNT_5)(const Indicator&, int) = COUNT;

Indicator (*SUM_1)(int) = SUM;
Indicator (*SUM_2)(const IndParam&) = SUM;
Indicator (*SUM_3)(const Indicator&, const IndParam&) = SUM;
Indicator (*SUM_4)(const Indicator&, const Indicator&) = SUM;
Indicator (*SUM_5)(const Indicator&, int) = SUM;

Indicator (*ABS_1)() = ABS;
Indicator (*ABS_2)(price_t) = ABS;
Indicator (*ABS_3)(const Indicator&) = ABS;

Indicator (*NOT_1)() = NOT;
Indicator (*NOT_2)(const Indicator&) = NOT;

Indicator (*SGN_1)() = SGN;
Indicator (*SGN_2)(price_t) = SGN;
Indicator (*SGN_3)(const Indicator&) = SGN;

Indicator (*EXP_1)() = EXP;
Indicator (*EXP_2)(price_t) = EXP;
Indicator (*EXP_3)(const Indicator&) = EXP;

Indicator (*MAX_1)(const Indicator&, const Indicator&) = MAX;
Indicator (*MAX_2)(const Indicator&, price_t) = MAX;
Indicator (*MAX_3)(price_t, const Indicator&) = MAX;

Indicator (*MIN_1)(const Indicator&, const Indicator&) = MIN;
Indicator (*MIN_2)(const Indicator&, price_t) = MIN;
Indicator (*MIN_3)(price_t, const Indicator&) = MIN;

Indicator (*BETWEEN_1)(const Indicator&, const Indicator&, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_2)(const Indicator&, const Indicator&, price_t) = BETWEEN;
Indicator (*BETWEEN_3)(const Indicator&, price_t, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_4)(const Indicator&, price_t, price_t) = BETWEEN;
Indicator (*BETWEEN_5)(price_t, const Indicator&, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_6)(price_t, const Indicator&, price_t) = BETWEEN;
Indicator (*BETWEEN_7)(price_t, price_t, const Indicator&) = BETWEEN;
Indicator (*BETWEEN_8)(price_t, price_t, price_t) = BETWEEN;

Indicator (*LN_1)() = LN;
Indicator (*LN_2)(price_t) = LN;
Indicator (*LN_3)(const Indicator&) = LN;

Indicator (*LOG_1)() = LOG;
Indicator (*LOG_2)(price_t) = LOG;
Indicator (*LOG_3)(const Indicator&) = LOG;

Indicator (*HHVBARS_1)(int) = HHVBARS;
Indicator (*HHVBARS_2)(const IndParam&) = HHVBARS;
Indicator (*HHVBARS_3)(const Indicator&, const IndParam&) = HHVBARS;
Indicator (*HHVBARS_4)(const Indicator&, const Indicator&) = HHVBARS;
Indicator (*HHVBARS_5)(const Indicator&, int) = HHVBARS;

Indicator (*LLVBARS_1)(int) = LLVBARS;
Indicator (*LLVBARS_2)(const IndParam&) = LLVBARS;
Indicator (*LLVBARS_3)(const Indicator&, const IndParam&) = LLVBARS;
Indicator (*LLVBARS_4)(const Indicator&, const Indicator&) = LLVBARS;
Indicator (*LLVBARS_5)(const Indicator&, int) = LLVBARS;

Indicator (*POW_1)(int) = POW;
Indicator (*POW_2)(const IndParam&) = POW;
Indicator (*POW_3)(const Indicator&, int) = POW;
Indicator (*POW_4)(const Indicator&, const IndParam&) = POW;
Indicator (*POW_5)(const Indicator&, const Indicator&) = POW;
Indicator (*POW_6)(price_t, int) = POW;

Indicator (*SQRT_1)() = SQRT;
Indicator (*SQRT_2)(const Indicator&) = SQRT;
Indicator (*SQRT_3)(price_t) = SQRT;

Indicator (*ROUND_1)(int) = ROUND;
Indicator (*ROUND_2)(const Indicator&, int) = ROUND;
Indicator (*ROUND_3)(price_t, int) = ROUND;

Indicator (*ROUNDUP_1)(int) = ROUNDUP;
Indicator (*ROUNDUP_2)(const Indicator&, int) = ROUNDUP;
Indicator (*ROUNDUP_3)(price_t, int) = ROUNDUP;

Indicator (*ROUNDDOWN_1)(int) = ROUNDDOWN;
Indicator (*ROUNDDOWN_2)(const Indicator&, int) = ROUNDDOWN;
Indicator (*ROUNDDOWN_3)(price_t, int) = ROUNDDOWN;

Indicator (*FLOOR_1)() = FLOOR;
Indicator (*FLOOR_2)(const Indicator&) = FLOOR;
Indicator (*FLOOR_3)(price_t) = FLOOR;

Indicator (*CEILING_1)() = CEILING;
Indicator (*CEILING_2)(const Indicator&) = CEILING;
Indicator (*CEILING_3)(price_t) = CEILING;

Indicator (*INTPART_1)() = INTPART;
Indicator (*INTPART_2)(const Indicator&) = INTPART;
Indicator (*INTPART_3)(price_t) = INTPART;

Indicator (*EXIST_1)(int) = EXIST;
Indicator (*EXIST_2)(const IndParam&) = EXIST;
Indicator (*EXIST_3)(const Indicator&, const IndParam&) = EXIST;
Indicator (*EXIST_4)(const Indicator&, const Indicator&) = EXIST;
Indicator (*EXIST_5)(const Indicator&, int) = EXIST;

Indicator (*EVERY_1)(int) = EVERY;
Indicator (*EVERY_2)(const IndParam&) = EVERY;
Indicator (*EVERY_3)(const Indicator&, const IndParam&) = EVERY;
Indicator (*EVERY_4)(const Indicator&, const Indicator&) = EVERY;
Indicator (*EVERY_5)(const Indicator&, int) = EVERY;

Indicator (*LAST_1)(int, int) = LAST;
Indicator (*LAST_2)(const Indicator&, int, int) = LAST;

Indicator (*SIN_1)() = SIN;
Indicator (*SIN_2)(const Indicator&) = SIN;
Indicator (*SIN_3)(price_t) = SIN;

Indicator (*ASIN_1)() = ASIN;
Indicator (*ASIN_2)(const Indicator&) = ASIN;
Indicator (*ASIN_3)(price_t) = ASIN;

Indicator (*COS_1)() = COS;
Indicator (*COS_2)(const Indicator&) = COS;
Indicator (*COS_3)(price_t) = COS;

Indicator (*ACOS_1)() = ACOS;
Indicator (*ACOS_2)(const Indicator&) = ACOS;
Indicator (*ACOS_3)(price_t) = ACOS;

Indicator (*TAN_1)() = TAN;
Indicator (*TAN_2)(const Indicator&) = TAN;
Indicator (*TAN_3)(price_t) = TAN;

Indicator (*ATAN_1)() = ATAN;
Indicator (*ATAN_2)(const Indicator&) = ATAN;
Indicator (*ATAN_3)(price_t) = ATAN;

Indicator (*REVERSE_1)() = REVERSE;
Indicator (*REVERSE_2)(const Indicator&) = REVERSE;
Indicator (*REVERSE_3)(price_t) = REVERSE;

Indicator (*MOD_1)(const Indicator&, const Indicator&) = MOD;
Indicator (*MOD_2)(const Indicator&, price_t) = MOD;
Indicator (*MOD_3)(price_t, const Indicator&) = MOD;
Indicator (*MOD_4)(price_t, price_t) = MOD;

Indicator (*VAR_1)(int) = VAR;
Indicator (*VAR_2)(const IndParam&) = VAR;
Indicator (*VAR_3)(const Indicator&, const IndParam&) = VAR;
Indicator (*VAR_4)(const Indicator&, const Indicator&) = VAR;
Indicator (*VAR_5)(const Indicator&, int) = VAR;

Indicator (*VARP_1)(int) = VARP;
Indicator (*VARP_2)(const IndParam&) = VARP;
Indicator (*VARP_3)(const Indicator&, const IndParam&) = VARP;
Indicator (*VARP_4)(const Indicator&, const Indicator&) = VARP;
Indicator (*VARP_5)(const Indicator&, int) = VARP;

Indicator (*CROSS_1)(const Indicator&, const Indicator&) = CROSS;
Indicator (*CROSS_2)(const Indicator&, price_t) = CROSS;
Indicator (*CROSS_3)(price_t, const Indicator&) = CROSS;
Indicator (*CROSS_4)(price_t, price_t) = CROSS;

Indicator (*LONGCROSS_1)(const Indicator&, const Indicator&, int) = LONGCROSS;
Indicator (*LONGCROSS_2)(const Indicator&, price_t, int) = LONGCROSS;
Indicator (*LONGCROSS_3)(price_t, const Indicator&, int) = LONGCROSS;
Indicator (*LONGCROSS_4)(price_t, price_t, int) = LONGCROSS;

Indicator (*FILTER_1)(int) = FILTER;
Indicator (*FILTER_2)(const IndParam&) = FILTER;
Indicator (*FILTER_3)(const Indicator&, const IndParam&) = FILTER;
Indicator (*FILTER_4)(const Indicator&, const Indicator&) = FILTER;
Indicator (*FILTER_5)(const Indicator&, int) = FILTER;

Indicator (*BARSSINCE_1)() = BARSSINCE;
Indicator (*BARSSINCE_2)(const Indicator&) = BARSSINCE;
Indicator (*BARSSINCE_3)(price_t) = BARSSINCE;

Indicator (*BARSLAST_1)() = BARSLAST;
Indicator (*BARSLAST_2)(const Indicator&) = BARSLAST;
Indicator (*BARSLAST_3)(price_t) = BARSLAST;

Indicator (*SUMBARS_1)(double) = SUMBARS;
Indicator (*SUMBARS_2)(const IndParam&) = SUMBARS;
Indicator (*SUMBARS_3)(const Indicator&, const IndParam&) = SUMBARS;
Indicator (*SUMBARS_4)(const Indicator&, const Indicator&) = SUMBARS;
Indicator (*SUMBARS_5)(const Indicator&, double) = SUMBARS;

Indicator (*BARSCOUNT_1)() = BARSCOUNT;
Indicator (*BARSCOUNT_2)(const Indicator&) = BARSCOUNT;

Indicator (*BACKSET_1)(int) = BACKSET;
Indicator (*BACKSET_2)(const IndParam&) = BACKSET;
Indicator (*BACKSET_3)(const Indicator&, const IndParam&) = BACKSET;
Indicator (*BACKSET_4)(const Indicator&, const Indicator&) = BACKSET;
Indicator (*BACKSET_5)(const Indicator&, int) = BACKSET;

Indicator (*TIMELINE_1)() = TIMELINE;
Indicator (*TIMELINE_2)(const KData&) = TIMELINE;

Indicator (*TIMELINEVOL_1)() = TIMELINEVOL;
Indicator (*TIMELINEVOL_2)(const KData&) = TIMELINEVOL;

Indicator (*DEVSQ_1)(int) = DEVSQ;
Indicator (*DEVSQ_2)(const IndParam&) = DEVSQ;
Indicator (*DEVSQ_3)(const Indicator&, const Indicator&) = DEVSQ;
Indicator (*DEVSQ_4)(const Indicator&, const IndParam&) = DEVSQ;
Indicator (*DEVSQ_5)(const Indicator&, int) = DEVSQ;

Indicator (*ROC_1)(int) = ROC;
Indicator (*ROC_2)(const IndParam&) = ROC;
Indicator (*ROC_3)(const Indicator&, const IndParam&) = ROC;
Indicator (*ROC_4)(const Indicator&, const Indicator&) = ROC;
Indicator (*ROC_5)(const Indicator&, int) = ROC;

Indicator (*ROCP_1)(int) = ROCP;
Indicator (*ROCP_2)(const IndParam&) = ROCP;
Indicator (*ROCP_3)(const Indicator&, const IndParam&) = ROCP;
Indicator (*ROCP_4)(const Indicator&, const Indicator&) = ROCP;
Indicator (*ROCP_5)(const Indicator&, int) = ROCP;

Indicator (*ROCR_1)(int) = ROCR;
Indicator (*ROCR_2)(const IndParam&) = ROCR;
Indicator (*ROCR_3)(const Indicator&, const IndParam&) = ROCR;
Indicator (*ROCR_4)(const Indicator&, const Indicator&) = ROCR;
Indicator (*ROCR_5)(const Indicator&, int) = ROCR;

Indicator (*ROCR100_1)(int) = ROCR100;
Indicator (*ROCR100_2)(const IndParam&) = ROCR100;
Indicator (*ROCR100_3)(const Indicator&, const IndParam&) = ROCR100;
Indicator (*ROCR100_4)(const Indicator&, const Indicator&) = ROCR100;
Indicator (*ROCR100_5)(const Indicator&, int) = ROCR100;

Indicator (*AD_1)() = AD;
Indicator (*AD_2)(const KData&) = AD;

Indicator (*COST_1)(double x) = COST;
Indicator (*COST_2)(const KData&, double x) = COST;

Indicator (*ALIGN_1)(const DatetimeList&) = ALIGN;
Indicator (*ALIGN_2)(const Indicator&, const DatetimeList&) = ALIGN;
Indicator (*ALIGN_3)(const Indicator&, const Indicator&) = ALIGN;
Indicator (*ALIGN_4)(const Indicator&, const KData&) = ALIGN;

Indicator (*DROPNA_1)() = DROPNA;
Indicator (*DROPNA_2)(const Indicator&) = DROPNA;

Indicator (*AVEDEV_1)(const Indicator&, int) = AVEDEV;
Indicator (*AVEDEV_2)(const Indicator&, const IndParam&) = AVEDEV;
Indicator (*AVEDEV_3)(const Indicator&, const Indicator&) = AVEDEV;

Indicator (*DOWNNDAY_1)(const Indicator&, int) = DOWNNDAY;
Indicator (*DOWNNDAY_2)(const Indicator&, const IndParam&) = DOWNNDAY;
Indicator (*DOWNNDAY_3)(const Indicator&, const Indicator&) = DOWNNDAY;

Indicator (*UPNDAY_1)(const Indicator&, int) = UPNDAY;
Indicator (*UPNDAY_2)(const Indicator&, const IndParam&) = UPNDAY;
Indicator (*UPNDAY_3)(const Indicator&, const Indicator&) = UPNDAY;

Indicator (*NDAY_1)(const Indicator&, const Indicator&, int) = NDAY;
Indicator (*NDAY_2)(const Indicator&, const Indicator&, const Indicator&) = NDAY;
Indicator (*NDAY_3)(const Indicator&, const Indicator&, const IndParam&) = NDAY;

void export_Indicator_build_in() {
    def("KDATA", KDATA1);
    def("KDATA", KDATA3, R"(KDATA([data])

    包装KData成Indicator，用于其他指标计算

    :param data: KData 或 具有6个返回结果的Indicator（如KDATA生成的Indicator）
    :rtype: Indicator)");

    def("CLOSE", CLOSE1);
    def("CLOSE", CLOSE3, R"(CLOSE([data])

    获取收盘价，包装KData的收盘价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("OPEN", OPEN1);
    def("OPEN", OPEN3, R"(OPEN([data])

    获取开盘价，包装KData的开盘价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("HIGH", HIGH1);
    def("HIGH", HIGH3, R"(HIGH([data])

    获取最高价，包装KData的最高价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("LOW", LOW1);
    def("LOW", LOW3, R"(LOW([data])

    获取最低价，包装KData的最低价成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("AMO", AMO1);
    def("AMO", AMO3, R"(AMO([data])

    获取成交金额，包装KData的成交金额成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("VOL", VOL1);
    def("VOL", VOL3, R"(VOL([data])

    获取成交量，包装KData的成交量成Indicator

    :param data: 输入数据（KData 或 Indicator）
    :rtype: Indicator)");

    def("KDATA_PART", KDATA_PART1, (arg("data"), arg("kpart")));
    def("KDATA_PART", KDATA_PART3, (arg("kpart")), R"(KDATA_PART([data, kpart])

    根据字符串选择返回指标KDATA/OPEN/HIGH/LOW/CLOSE/AMO/VOL，如:KDATA_PART("CLOSE")等同于CLOSE()

    :param data: 输入数据（KData 或 Indicator）
    :param string kpart: KDATA|OPEN|HIGH|LOW|CLOSE|AMO|VOL
    :rtype: Indicator)");

    def("PRICELIST", PRICELIST2, (arg("data"), arg("discard") = 0));
    def("PRICELIST", PRICELIST3, (arg("data"), arg("result_index") = 0));
    def("PRICELIST", PRICELIST4, (arg("result_index") = 0));

    def("SMA", SMA_1, (arg("n") = 22, arg("m") = 2.0));
    def("SMA", SMA_2, (arg("data"), arg("n") = 22, arg("m") = 2.0), R"(SMA([data, n=22, m=2])

    求移动平均

    用法：若Y=SMA(X,N,M) 则 Y=[M*X+(N-M)*Y')/N,其中Y'表示上一周期Y值

    :param Indicator data: 输入数据
    :param int n: 时间窗口
    :param float m: 系数
    :rtype: Indicator)");

    def("EMA", EMA_1, (arg("n") = 22));
    def("EMA", EMA_2, (arg("data"), arg("n") = 22), R"(EMA([data, n=22])

    指数移动平均线(Exponential Moving Average)

    :param data: 输入数据
    :param int n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("MA", MA_1, (arg("n") = 22));
    def("MA", MA_2, (arg("n")));
    def("MA", MA_3, (arg("data"), arg("n")));
    def("MA", MA_4, (arg("data"), arg("n")));
    def("MA", MA_5, (arg("data"), arg("n") = 22), R"(MA([data, n=22])

    简单移动平均

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("AMA", AMA_1, (arg("n") = 10, arg("fast_n") = 2, arg("slow_n") = 30));
    def("AMA", AMA_2, (arg("data"), arg("n") = 10, arg("fast_n") = 2, arg("slow_n") = 30),
        R"(AMA([data, n=10, fast_n=2, slow_n=30])

    佩里.J 考夫曼（Perry J.Kaufman）自适应移动平均 [BOOK1]_

    :param Indicator data: 输入数据
    :param int n: 计算均值的周期窗口，必须为大于2的整数
    :param int fast_n: 对应快速周期N
    :param int slow_n: 对应慢速EMA线的N值
    :rtype: Indicator

    * result(0): AMA
    * result(1): ER)");

    def("ATR", ATR_1, (arg("n") = 14));
    def("ATR", ATR_2, (arg("data"), arg("n") = 14), R"(ATR([data, n=14])

    平均真实波幅(Average True Range)

    :param Indicator data 待计算的源数据
    :param int n: 计算均值的周期窗口，必须为大于1的整数
    :rtype: Indicator)");

    def("MACD", MACD_1, (arg("n1") = 12, arg("n2") = 26, arg("n3") = 9));
    def("MACD", MACD_2, (arg("data"), arg("n1") = 12, arg("n2") = 26, arg("n3") = 9),
        R"(MACD([data, n1=12, n2=26, n3=9])

    平滑异同移动平均线

    :param Indicator data: 输入数据
    :param int n1: 短期EMA时间窗
    :param int n2: 长期EMA时间窗
    :param int n3: （短期EMA-长期EMA）EMA平滑时间窗
    :rtype: 具有三个结果集的 Indicator

    * result(0): MACD_BAR：MACD直柱，即MACD快线－MACD慢线
    * result(1): DIFF: 快线,即（短期EMA-长期EMA）
    * result(2): DEA: 慢线，即快线的n3周期EMA平滑)");

    def("VIGOR", VIGOR_1, (arg("kdata"), arg("n") = 2));
    def("VIGOR", VIGOR_2, (arg("n") = 2), R"(VIGOR([kdata, n=2])

    亚历山大.艾尔德力度指数 [BOOK2]_

    计算公式：（收盘价今－收盘价昨）＊成交量今

    :param KData data: 输入数据
    :param int n: EMA平滑窗口
    :rtype: Indicator)");

    def("SAFTYLOSS", SAFTYLOSS_1, (arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0));
    def("SAFTYLOSS", SAFTYLOSS_2, (arg("data"), arg("n1") = 10, arg("n2") = 3, arg("p") = 2.0),
        R"(SAFTYLOSS([data, n1=10, n2=3, p=2.0])

    亚历山大 艾尔德安全地带止损线，参见 [BOOK2]_

    计算说明：在回溯周期内（一般为10到20天），将所有向下穿越的长度相加除以向下穿越的次数，得到噪音均值（即回溯期内所有最低价低于前一日最低价的长度除以次数），并用今日最低价减去（前日噪音均值乘以一个倍数）得到该止损线。为了抵消波动并且保证止损线的上移，在上述结果的基础上再取起N日（一般为3天）内的最高值

    :param Indicator data: 输入数据
    :param int n1: 计算平均噪音的回溯时间窗口
    :param int n2: 对初步止损线去n2日内的最高值
    :param float p: 噪音系数
    :rtype: Indicator)");

    def("DIFF", DIFF_1);
    def("DIFF", DIFF_2, R"(DIFF([data])

    差分指标，即data[i] - data[i-1]

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("REF", REF_1, (arg("n")));
    def("REF", REF_2, (arg("data"), arg("n")), R"(REF([data, n])

    向前引用 （即右移），引用若干周期前的数据。

    用法：REF(X，A)　引用A周期前的X值。

    :param Indicator data: 输入数据
    :param int n: 引用n周期前的值，即右移n位
    :rtype: Indicator)");

    def("STDEV", STDEV_1, (arg("n") = 10));
    def("STDEV", STDEV_2, (arg("n")));
    def("STDEV", STDEV_3, (arg("data"), arg("n")));
    def("STDEV", STDEV_4, (arg("data"), arg("n")));
    def("STDEV", STDEV_5, (arg("data"), arg("n") = 10), R"(STDEV([data, n=10])

    计算N周期内样本标准差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("STDP", STDP_1, (arg("n") = 10));
    def("STDP", STDP_2, (arg("n")));
    def("STDP", STDP_3, (arg("data"), arg("n")));
    def("STDP", STDP_4, (arg("data"), arg("n")));
    def("STDP", STDP_5, (arg("data"), arg("n") = 10), R"(STDP([data, n=10])

    总体标准差，STDP(X,N)为X的N日总体标准差

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("POS", POS, (arg("block"), arg("query"), arg("sg")));

    def("HHV", HHV_1, (arg("n") = 20));
    def("HHV", HHV_2, (arg("n")));
    def("HHV", HHV_3, (arg("data"), arg("n")));
    def("HHV", HHV_4, (arg("data"), arg("n")));
    def("HHV", HHV_5, (arg("data"), arg("n") = 20), R"(HHV([data, n=20])

    N日内最高价，N=0则从第一个有效值开始。

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("LLV", LLV_1, (arg("n") = 20));
    def("LLV", LLV_2, (arg("n")));
    def("LLV", LLV_3, (arg("data"), arg("n")));
    def("LLV", LLV_4, (arg("data"), arg("n")));
    def("LLV", LLV_5, (arg("data"), arg("n") = 20), R"(LLV([data, n=20])

    N日内最低价，N=0则从第一个有效值开始。

    :param data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("CVAL", CVAL_1, (arg("value") = 0.0, arg("discard") = 0));
    def("CVAL", CVAL_2, (arg("data"), arg("value") = 0.0, arg("discard") = 0),
        R"(CVAL([data, value=0.0, discard=0])

    data 为 Indicator 实例，创建和 data 等长的常量指标，其值和为value，抛弃长度discard和data一样

    :param Indicator data: Indicator实例
    :param float value: 常数值
    :param int discard: 抛弃数量
    :rtype: Indicator)");

    def("LIUTONGPAN", LIUTONGPAN_1);
    def("LIUTONGPAN", LIUTONGPAN_2, R"(LIUTONGPAN(kdata)

   获取流通盘（单位：万股），同 CAPITAL

   :param KData kdata: k线数据
   :rtype: Indicator)");

    def("HSL", HSL_1);
    def("HSL", HSL_2, R"(HSL(kdata)

    获取换手率，等于 VOL(k) / CAPITAL(k)

    :param KData kdata: k线数据
    :rtype: Indicator)");

    def("WEAVE", WEAVE, R"(WEAVE(ind1, ind2)

    将ind1和ind2的结果组合在一起放在一个Indicator中。如ind = WEAVE(ind1, ind2), 则此时ind包含多个结果，按ind1、ind2的顺序存放。

    :param Indicator ind1: 指标1
    :param Indicator ind2: 指标2
    :rtype: Indicator)");

    def("IF", IF_1);
    def("IF", IF_2);
    def("IF", IF_3);
    def("IF", IF_4, R"(IF(x, a, b)

    条件函数, 根据条件求不同的值。

    用法：IF(X,A,B)若X不为0则返回A,否则返回B

    例如：IF(CLOSE>OPEN,HIGH,LOW)表示该周期收阳则返回最高值,否则返回最低值

    :param Indicator x: 条件指标
    :param Indicator a: 待选指标 a
    :param Indicator b: 待选指标 b
    :rtype: Indicator)");

    def("COUNT", COUNT_1, (arg("n") = 20));
    def("COUNT", COUNT_2, (arg("n")));
    def("COUNT", COUNT_3, (arg("data"), arg("n")));
    def("COUNT", COUNT_4, (arg("data"), arg("n")));
    def("COUNT", COUNT_5, (arg("data"), arg("n") = 20), R"(COUNT([data, n=20])

    统计满足条件的周期数。

    用法：COUNT(X,N),统计N周期中满足X条件的周期数,若N=0则从第一个有效值开始。

    例如：COUNT(CLOSE>OPEN,20)表示统计20周期内收阳的周期数

    :param Indicator data: 条件
    :param int|Indicator|IndParam n: 周期
    :rtype: Indicator)");

    def("SUM", SUM_1, (arg("n") = 20));
    def("SUM", SUM_2, (arg("n")));
    def("SUM", SUM_3, (arg("data"), arg("n")));
    def("SUM", SUM_4, (arg("data"), arg("n")));
    def("SUM", SUM_5, (arg("data"), arg("n") = 20), R"(SUM([data, n=20])

    求总和。SUM(X,N),统计N周期中X的总和,N=0则从第一个有效值开始。

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("ABS", ABS_1);
    def("ABS", ABS_2);
    def("ABS", ABS_3, R"(ABS([data])

    求绝对值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("NOT", NOT_1);
    def("NOT", NOT_2, R"(NOT([data])

    求逻辑非。NOT(X)返回非X,即当X=0时返回1，否则返回0。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("SGN", SGN_1);
    def("SGN", SGN_2);
    def("SGN", SGN_3, R"(SGN([data])

    求符号值, SGN(X)，当 X>0, X=0, X<0分别返回 1, 0, -1。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("EXP", EXP_1);
    def("EXP", EXP_2);
    def("EXP", EXP_3, R"(EXP([data])

    EXP(X)为e的X次幂

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("MAX", MAX_1);
    def("MAX", MAX_2);
    def("MAX", MAX_3, R"(MAX(ind1, ind2)

    求最大值, MAX(A,B)返回A和B中的较大值。

    :param Indicator ind1: A
    :param Indicator ind2: B
    :rtype: Indicator)");

    def("MIN", MIN_1);
    def("MIN", MIN_2);
    def("MIN", MIN_3, R"(MIN(ind1, ind2)

    求最小值, MIN(A,B)返回A和B中的较小值。

    :param Indicator ind1: A
    :param Indicator ind2: B
    :rtype: Indicator)");

    def("BETWEEN", BETWEEN_1);
    def("BETWEEN", BETWEEN_2);
    def("BETWEEN", BETWEEN_3);
    def("BETWEEN", BETWEEN_4);
    def("BETWEEN", BETWEEN_5);
    def("BETWEEN", BETWEEN_6);
    def("BETWEEN", BETWEEN_7);
    def("BETWEEN", BETWEEN_8, R"(BETWEEN(a, b, c)

    介于(介于两个数之间)

    用法：BETWEEN(A,B,C)表示A处于B和C之间时返回1，否则返回0

    例如：BETWEEN(CLOSE,MA(CLOSE,10),MA(CLOSE,5))表示收盘价介于5日均线和10日均线之间

    :param Indicator a: A
    :param Indicator b: B
    :param Indicator c: C
    :rtype: Indicator)");

    def("LN", LN_1);
    def("LN", LN_2);
    def("LN", LN_3, R"(LN([data])

    求自然对数, LN(X)以e为底的对数

    :param data: 输入数据
    :rtype: Indicator)");

    def("LOG", LOG_1);
    def("LOG", LOG_2);
    def("LOG", LOG_3, R"(LOG([data])

    以10为底的对数

    :param data: 输入数据
    :rtype: Indicator)");

    def("HHVBARS", HHVBARS_1, (arg("n") = 20));
    def("HHVBARS", HHVBARS_2, (arg("n")));
    def("HHVBARS", HHVBARS_3, (arg("data"), arg("n")));
    def("HHVBARS", HHVBARS_4, (arg("data"), arg("n")));
    def("HHVBARS", HHVBARS_5, (arg("data"), arg("n") = 20), R"(HHVBARS([data, n=20])

    上一高点位置 求上一高点到当前的周期数。

    用法：HHVBARS(X,N):求N周期内X最高值到当前周期数N=0表示从第一个有效值开始统计

    例如：HHVBARS(HIGH,0)求得历史新高到到当前的周期数

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("LLVBARS", LLVBARS_1, (arg("n") = 20));
    def("LLVBARS", LLVBARS_2, (arg("n")));
    def("LLVBARS", LLVBARS_3, (arg("data"), arg("n")));
    def("LLVBARS", LLVBARS_4, (arg("data"), arg("n")));
    def("LLVBARS", LLVBARS_5, (arg("data"), arg("n") = 20), R"(LLVBARS([data, n=20])

    上一低点位置 求上一低点到当前的周期数。

    用法：LLVBARS(X,N):求N周期内X最低值到当前周期数N=0表示从第一个有效值开始统计

    例如：LLVBARS(HIGH,20)求得20日最低点到当前的周期数

    :param data: 输入数据
    :param int|Indicator|IndParam n: N日时间窗口
    :rtype: Indicator)");

    def("POW", POW_1, (arg("n")));
    def("POW", POW_2, (arg("n")));
    def("POW", POW_3, (arg("data"), arg("n")));
    def("POW", POW_4, (arg("data"), arg("n")));
    def("POW", POW_5, (arg("data"), arg("n")));
    def("POW", POW_6), (arg("data"), arg("n"), R"(POW(data, n)

    乘幂

    用法：POW(A,B)返回A的B次幂

    例如：POW(CLOSE,3)求得收盘价的3次方

    :param data: 输入数据
    :param int|Indicator|IndParam n: 幂
    :rtype: Indicator)");

    def("SQRT", SQRT_1);
    def("SQRT", SQRT_2);
    def("SQRT", SQRT_3, R"(SQRT([data])

    开平方

    用法：SQRT(X)为X的平方根

    例如：SQRT(CLOSE)收盘价的平方根

    :param data: 输入数据
    :rtype: Indicator)");

    def("ROUND", ROUND_1, (arg("ndigits") = 2));
    def("ROUND", ROUND_2, (arg("data"), arg("ndigits") = 2));
    def("ROUND", ROUND_3, (arg("data"), arg("ndigits") = 2), R"(ROUND([data, ndigits=2])

    四舍五入

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("ROUNDUP", ROUNDUP_1, (arg("ndigits") = 2));
    def("ROUNDUP", ROUNDUP_2, (arg("data"), arg("ndigits") = 2));
    def("ROUNDUP", ROUNDUP_3, (arg("data"), arg("ndigits") = 2), R"(ROUNDUP([data, ndigits=2])

    向上截取，如10.1截取后为11

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("ROUNDDOWN", ROUNDDOWN_1, (arg("ndigits") = 2));
    def("ROUNDDOWN", ROUNDDOWN_2, (arg("data"), arg("ndigits") = 2));
    def("ROUNDDOWN", ROUNDDOWN_3, (arg("data"), arg("ndigits") = 2), R"(ROUND([data, ndigits=2])

    四舍五入

    :param data: 输入数据
    :param int ndigits: 保留的小数点后位数
    :rtype: Indicator)");

    def("FLOOR", FLOOR_1);
    def("FLOOR", FLOOR_2);
    def("FLOOR", FLOOR_3, R"(FLOOR([data])

    向下舍入(向数值减小方向舍入)取整

    用法：FLOOR(A)返回沿A数值减小方向最接近的整数

    例如：FLOOR(12.3)求得12

    :param data: 输入数据
    :rtype: Indicator)");

    def("CEILING", CEILING_1);
    def("CEILING", CEILING_2);
    def("CEILING", CEILING_3, R"(CEILING([data])

    向上舍入(向数值增大方向舍入)取整

    用法：CEILING(A)返回沿A数值增大方向最接近的整数

    例如：CEILING(12.3)求得13；CEILING(-3.5)求得-3

    :param data: 输入数据
    :rtype: Indicator)");

    def("INTPART", INTPART_1);
    def("INTPART", INTPART_2);
    def("INTPART", INTPART_3, R"(INTPART([data])

    取整(绝对值减小取整，即取得数据的整数部分)

    :param data: 输入数据
    :rtype: Indicator)");

    def("EXIST", EXIST_1, (arg("n") = 20));
    def("EXIST", EXIST_2, (arg("n")));
    def("EXIST", EXIST_3, (arg("data"), arg("n")));
    def("EXIST", EXIST_4, (arg("data"), arg("n")));
    def("EXIST", EXIST_5, (arg("data"), arg("n") = 20), R"(EXIST([data, n=20])

    存在, EXIST(X,N) 表示条件X在N周期有存在

    :param data: 输入数据
    :param int|Indicator|IndParam n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("EVERY", EVERY_1, (arg("n") = 20));
    def("EVERY", EVERY_2, (arg("n")));
    def("EVERY", EVERY_3, (arg("data"), arg("n")));
    def("EVERY", EVERY_4, (arg("data"), arg("n")));
    def("EVERY", EVERY_5, (arg("data"), arg("n") = 20), R"(EVERY([data, n=20])

    一直存在

    用法：EVERY (X,N) 表示条件X在N周期一直存在

    例如：EVERY(CLOSE>OPEN,10) 表示前10日内一直是阳线

    :param data: 输入数据
    :param int|Indicator|IndParam n: 计算均值的周期窗口，必须为大于0的整数
    :rtype: Indicator)");

    def("LAST", LAST_1, (arg("m") = 10, arg("n") = 5));
    def("LAST", LAST_2, (arg("data"), arg("m") = 10, arg("n") = 5), R"(LAST([data, m=10, n=5])

    区间存在。

    用法：LAST (X,M,N) 表示条件 X 在前 M 周期到前 N 周期存在。

    例如：LAST(CLOSE>OPEN,10,5) 表示从前10日到前5日内一直阳线。

    :param data: 输入数据
    :param int m: m周期
    :param int n: n周期
    :rtype: Indicator)");

    def("SIN", SIN_1);
    def("SIN", SIN_2);
    def("SIN", SIN_3, R"(SIN([data])

    正弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ASIN", ASIN_1);
    def("ASIN", ASIN_2);
    def("ASIN", ASIN_3, R"(ASIN([data])

    反正弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("COS", COS_1);
    def("COS", COS_2);
    def("COS", COS_3, R"(COS([data])

    余弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ACOS", ACOS_1);
    def("ACOS", ACOS_2);
    def("ACOS", ACOS_3, R"(ACOS([data])

    反余弦值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("TAN", TAN_1);
    def("TAN", TAN_2);
    def("TAN", TAN_3, R"(TAN([data])

    正切值

    :param Indicator data: 输入数据
    :rtype: Indicato)");

    def("ATAN", ATAN_1);
    def("ATAN", ATAN_2);
    def("ATAN", ATAN_3, R"(ATAN([data])

    反正切值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("REVERSE", REVERSE_1);
    def("REVERSE", REVERSE_2);
    def("REVERSE", REVERSE_3, R"(REVERSE([data])

    求相反数，REVERSE(X)返回-X

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("MOD", MOD_1);
    def("MOD", MOD_2);
    def("MOD", MOD_3);
    def("MOD", MOD_4, R"(MOD(ind1, ind2)

    取整后求模。该函数仅为兼容通达信。实际上，指标求模可直接使用 % 操作符

    用法：MOD(A,B)返回A对B求模

    例如：MOD(26,10) 返回 6

    :param Indicator ind1:
    :param Indicator ind2:
    :rtype: Indicator)");

    def("VAR", VAR_1, (arg("n") = 10));
    def("VAR", VAR_2, (arg("n")));
    def("VAR", VAR_3, (arg("data"), arg("n")));
    def("VAR", VAR_4, (arg("data"), arg("n")));
    def("VAR", VAR_5, (arg("data"), arg("n") = 10), R"(VAR([data, n=10])

    估算样本方差, VAR(X,N)为X的N日估算样本方差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("VARP", VARP_1, (arg("n") = 10));
    def("VARP", VARP_2, (arg("n")));
    def("VARP", VARP_3, (arg("data"), arg("n")));
    def("VARP", VARP_4, (arg("data"), arg("n")));
    def("VARP", VARP_5, (arg("data"), arg("n") = 10), R"(VARP([data, n=10])

    总体样本方差, VARP(X,N)为X的N日总体样本方差

    :param Indicator data: 输入数据
    :param int n|Indicator|IndParam: 时间窗口
    :rtype: Indicator)");

    def("UPNDAY", UPNDAY_1, (arg("data"), arg("n") = 3));
    def("UPNDAY", UPNDAY_2, (arg("data"), arg("n")));
    def("UPNDAY", UPNDAY_3, (arg("data"), arg("n")), R"(UPNDAY(data[, n=3])

    连涨周期数, UPNDAY(CLOSE,M)表示连涨M个周期

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("DOWNNDAY", DOWNNDAY_1, (arg("data"), arg("n") = 3));
    def("DOWNNDAY", DOWNNDAY_2, (arg("data"), arg("n")));
    def("DOWNNDAY", DOWNNDAY_3, (arg("data"), arg("n")), R"(DOWNNDAY(data[, n=3])

    连跌周期数, DOWNNDAY(CLOSE,M)表示连涨M个周期

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("NDAY", NDAY_1, (arg("x"), arg("y"), arg("n") = 3));
    def("NDAY", NDAY_1, (arg("x"), arg("y"), arg("n")));
    def("NDAY", NDAY_3, (arg("x"), arg("y"), arg("n")), R"(NDAY(x, y[, n=3])

    连大, NDAY(X,Y,N)表示条件X>Y持续存在N个周期

    :param Indicator x:
    :param Indicator y:
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("CROSS", CROSS_1);
    def("CROSS", CROSS_2);
    def("CROSS", CROSS_3);
    def("CROSS", CROSS_4, R"(CROSS(x, y)

    交叉函数

    :param x: 变量或常量，判断交叉的第一条线
    :param y: 变量或常量，判断交叉的第二条线
    :rtype: Indicator)");

    def("LONGCROSS", LONGCROSS_1, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_2, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_3, (arg("a"), arg("b"), arg("n") = 3));
    def("LONGCROSS", LONGCROSS_4, (arg("a"), arg("b"), arg("n") = 3), R"(LONGCROSS(a, b[, n=3])

    两条线维持一定周期后交叉

    用法：LONGCROSS(A,B,N)表示A在N周期内都小于B，本周期从下方向上穿过B时返 回1，否则返回0

    例如：LONGCROSS(MA(CLOSE,5),MA(CLOSE,10),5)表示5日均线维持5周期后与10日均线交金叉

    :param Indicator a:
    :param Indicator b:
    :param int n:
    :rtype: Indicator)");

    def("FILTER", FILTER_1, (arg("n") = 5));
    def("FILTER", FILTER_2, (arg("n")));
    def("FILTER", FILTER_3, (arg("data"), arg("n")));
    def("FILTER", FILTER_4, (arg("data"), arg("n")));
    def("FILTER", FILTER_5, (arg("data"), arg("n") = 5), R"(FILTER([data, n=5])

    信号过滤, 过滤连续出现的信号。

    用法：FILTER(X,N): X 满足条件后，删除其后 N 周期内的数据置为 0。

    例如：FILTER(CLOSE>OPEN,5) 查找阳线，5 天内再次出现的阳线不被记录在内。

    :param Indicator data: 输入数据
    :param int|Indicaot|IndParam n: 过滤周期
    :rtype: Indicator)");

    def("BARSSINCE", BARSSINCE_1);
    def("BARSSINCE", BARSSINCE_2);
    def("BARSSINCE", BARSSINCE_3, R"(BARSSINCE([data])

    第一个条件成立位置到当前的周期数。

    用法：BARSSINCE(X):第一次X不为0到现在的天数。

    例如：BARSSINCE(HIGH>10)表示股价超过10元时到当前的周期数

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("BARSLAST", BARSLAST_1);
    def("BARSLAST", BARSLAST_2);
    def("BARSLAST", BARSLAST_3, R"(BARSLAST([data])

    上一次条件成立位置 上一次条件成立到当前的周期数。

    用法：BARSLAST(X): 上一次 X 不为 0 到现在的天数。

    例如：BARSLAST(CLOSE/REF(CLOSE,1)>=1.1) 表示上一个涨停板到当前的周期数

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("SUMBARS", SUMBARS_1, (arg("a")));
    def("SUMBARS", SUMBARS_2, (arg("a")));
    def("SUMBARS", SUMBARS_3, (arg("data"), arg("a")));
    def("SUMBARS", SUMBARS_4, (arg("data"), arg("a")));
    def("SUMBARS", SUMBARS_5, (arg("data"), arg("a")), R"(SUMBARS([data,] a)

    累加到指定周期数, 向前累加到指定值到现在的周期数

    用法：SUMBARS(X,A):将X向前累加直到大于等于A,返回这个区间的周期数

    例如：SUMBARS(VOL,CAPITAL)求完全换手到现在的周期数

    :param Indicator data: 输入数据
    :param float a|Indicator|IndParam: 指定累加和
    :rtype: Indicator)");

    def("BARSCOUNT", BARSCOUNT_1);
    def("BARSCOUNT", BARSCOUNT_2, R"(BARSCOUNT([data])

    有效值周期数, 求总的周期数。

    用法：BARSCOUNT(X)第一个有效数据到当前的天数。

    例如：BARSCOUNT(CLOSE)对于日线数据取得上市以来总交易日数，对于1分钟线取得当日交易分钟数。

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("BACKSET", BACKSET_1, (arg("n") = 2));
    def("BACKSET", BACKSET_2, (arg("n")));
    def("BACKSET", BACKSET_3, (arg("data"), arg("n")));
    def("BACKSET", BACKSET_4, (arg("data"), arg("n")));
    def("BACKSET", BACKSET_5, (arg("data"), arg("n") = 2), R"(BACKSET([data, n=2])

    向前赋值将当前位置到若干周期前的数据设为1。

    用法：BACKSET(X,N),X非0,则将当前位置到N周期前的数值设为1。

    例如：BACKSET(CLOSE>OPEN,2)若收阳则将该周期及前一周期数值设为1,否则为0

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: N周期
    :rtype: Indicator)");

    def("TIMELINE", TIMELINE_1);
    def("TIMELINE", TIMELINE_2, R"(TIMELINE([k])

    分时价格数据

    :param KData k: 上下文
    :rtype: Indicator)");

    def("TIMELINEVOL", TIMELINEVOL_1);
    def("TIMELINEVOL", TIMELINEVOL_2, R"(TIMELINEVOL([k])

    分时成交量数据

    :param KData k: 上下文
    :rtype: Indicator)");

    def("DMA", DMA, R"(DMA(ind, a)

    动态移动平均

    用法：DMA(X,A),求X的动态移动平均。

    算法：若Y=DMA(X,A) 则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值。

    例如：DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价

    :param Indicator ind: 输入数据
    :param Indicator a: 动态系数
    :rtype: Indicator)");

    def("AVEDEV", AVEDEV_1, (arg("data"), arg("n") = 22));
    def("AVEDEV", AVEDEV_2, (arg("data"), arg("n")));
    def("AVEDEV", AVEDEV_3, (arg("data"), arg("n")), R"(AVEDEV(data[, n=22])

    平均绝对偏差，求X的N日平均绝对偏差

    :param Indicator data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("DEVSQ", DEVSQ_1, (arg("n") = 10));
    def("DEVSQ", DEVSQ_2, (arg("n")));
    def("DEVSQ", DEVSQ_3, (arg("data"), arg("n")));
    def("DEVSQ", DEVSQ_4, (arg("data"), arg("n")));
    def("DEVSQ", DEVSQ_5, (arg("data"), arg("n") = 10), R"(DEVSQ([data, n=10])

    数据偏差平方和，求X的N日数据偏差平方和

    :param Indicator data: 输入数据
    :param int|Indicator n: 时间窗口
    :rtype: Indicator)");

    def("ROC", ROC_1, (arg("n") = 10));
    def("ROC", ROC_2, (arg("n")));
    def("ROC", ROC_3, (arg("data"), arg("n")));
    def("ROC", ROC_4, (arg("data"), arg("n")));
    def("ROC", ROC_5, (arg("data"), arg("n") = 10), R"(ROC([data, n=10])

    变动率指标: ((price / prevPrice)-1)*100

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("ROCP", ROCP_1, (arg("n") = 10));
    def("ROCP", ROCP_2, (arg("n")));
    def("ROCP", ROCP_3, (arg("data"), arg("n")));
    def("ROCP", ROCP_4, (arg("data"), arg("n")));
    def("ROCP", ROCP_5, (arg("data"), arg("n") = 10), R"(ROCP([data, n=10])

    变动率指标: (price - prevPrice) / prevPrice

    :param data: 输入数据
    :param int n: 时间窗口
    :rtype: Indicator)");

    def("ROCR", ROCR_1, (arg("n") = 10));
    def("ROCR", ROCR_2, (arg("n")));
    def("ROCR", ROCR_3, (arg("data"), arg("n")));
    def("ROCR", ROCR_4, (arg("data"), arg("n")));
    def("ROCR", ROCR_5, (arg("data"), arg("n") = 10), R"(ROCR([data, n=10])

    变动率指标: (price / prevPrice)

    :param data: 输入数据
    :param int n|Indicator|IndParam: 时间窗口
    :rtype: Indicator)");

    def("ROCR100", ROCR100_1, (arg("n") = 10));
    def("ROCR100", ROCR100_2, (arg("n")));
    def("ROCR100", ROCR100_3, (arg("data"), arg("n")));
    def("ROCR100", ROCR100_4, (arg("data"), arg("n")));
    def("ROCR100", ROCR100_5, (arg("data"), arg("n") = 10), R"(ROCR100([data, n=10])

    变动率指标: (price / prevPrice) * 100

    :param data: 输入数据
    :param int|Indicator|IndParam n: 时间窗口
    :rtype: Indicator)");

    def("AD", AD_1);
    def("AD", AD_2, R"(AD(kdata)

   累积/派发线

   :param KData kdata: k线数据
   :rtype: Indicator)");

    def("COST", COST_1, (arg("x") = 10.0));
    def("COST", COST_2, (arg("k"), arg("x") = 10.0), R"(COST(k[, x=10.0])

    成本分布

    用法：COST(k, X) 表示X%获利盘的价格是多少

    例如：COST(k, 10),表示10%获利盘的价格是多少，即有10%的持仓量在该价格以下，其余90%在该价格以上，为套牢盘 该函数仅对日线分析周期有效

    :param KData k: 关联的K线数据
    :param float x: x%获利价格, 0~100
    :rtype: Indicator)");

    def("ALIGN", ALIGN_1);
    def("ALIGN", ALIGN_2);
    def("ALIGN", ALIGN_3);
    def("ALIGN", ALIGN_4, R"(ALIGN(data, ref):

    按指定的参考日期对齐

    :param Indicator data: 输入数据
    :param ref: 指定做为日期参考的 DatetimeList、Indicator 或 KData
    :retype: Indicator)");

    def("DROPNA", DROPNA_1);
    def("DROPNA", DROPNA_2, R"(DROPNA([data])

    删除 nan 值

    :param Indicator data: 输入数据
    :rtype: Indicator)");

    def("ADVANCE", ADVANCE,
        (arg("query") = KQueryByIndex(-100), arg("market") = "SH", arg("stk_type") = STOCKTYPE_A,
         arg("ignore_context") = false),
        R"(ADVANCE([query=Query(-100), market='SH', stk_type='constant.STOCKTYPE_A'])

    上涨家数。当存在指定上下文且 ignore_context 为 false 时，将忽略 query, market, stk_type 参数。

    :param Query query: 查询条件
    :param str market: 所属市场，等于 "" 时，获取所有市场
    :param int stk_type: 证券类型, 大于 constant.STOCKTYPE_TMP 时，获取所有类型证券
    :param bool ignore_context: 是否忽略上下文。忽略时，强制使用 query, market, stk_type 参数。
    :rtype: Indicator)");

    def("DECLINE", DECLINE,
        (arg("query") = KQueryByIndex(-100), arg("market") = "SH", arg("stk_type") = STOCKTYPE_A,
         arg("ignore_context") = false),
        R"(DECLINE([query=Query(-100), market='SH', stk_type='constant.STOCKTYPE_A'])

    下跌家数。当存在指定上下文且 ignore_context 为 false 时，将忽略 query, market, stk_type 参数。

    :param Query query: 查询条件
    :param str market: 所属市场，等于 "" 时，获取所有市场
    :param int stk_type: 证券类型, 大于 constant.STOCKTYPE_TMP 时，获取所有类型证券
    :param bool ignore_context: 是否忽略上下文。忽略时，强制使用 query, market, stk_type 参数。
    :rtype: Indicator)");
}


// Author: Senthil Kumar Thangavelu kingjuliyen@gmail.com

#ifndef ITEM_ITEM_LEARNER_HPP
#define ITEM_ITEM_LEARNER_HPP

#include <cmath>
#include <algorithm>
#include <map>
#include <thread>
#include <boost/dynamic_bitset.hpp>

#include "../utils/Mtx.hpp"
#include "../utils/UserItemTableHelper.hpp"

typedef boost::dynamic_bitset<> DynBitSet;
typedef map<INT_T, FLT_T> * UID_RATING_PTR;
typedef map<INT_T, FLT_T> UID_RATING;
typedef vector<FLT_T> fltvec;

// random generator function:
inline int newRandom (int i) { return std::rand()%i; }
const FLT_T BAD_SIMILARITY = -100000.0;

typedef vector<Rating_T> RatingVector;

struct NeighbourHoodRecoParams {
    INT_T top_K_neighbours;
    STRING_T recommendation_type;
    FLT_T training_sample_percentage;
    INT_T max_row_dim;
    INT_T max_col_dim;
    STRING_T csv_input_file_path;
    INT_T verbose_mode_level;
    INT_T loop_mode_count;
    INT_T max_thread_count;
    STRING_T sim_mtx_file_save_path;
    STRING_T item_index_table_path;
    STRING_T user_index_table_path;
};

struct ItemCombination {
  INT_T item1;
  INT_T item2;
  ItemCombination(INT_T i1 , INT_T i2): item1(i1), item2(i2) {}
};

template < typename SIMILARITY_TYPE >
  class NeighbourHoodRecommender;

#define DELETE_AR(x) \
{ \
  if(x) { \
    delete[] x; \
    x = 0; \
  } \
}

#define DELETE(x) \
{ \
  if(x) { \
    delete x; \
    x = 0; \
  } \
}

template < typename SIMILARITY_TYPE >
struct SimilarityThreadParams {
  NeighbourHoodRecommender<SIMILARITY_TYPE>& reco;
  INT_T nextThreadIndex;
  INT_T itemComboIndexstart;
  INT_T itemComboIndexend;
  SimilarityThreadParams(NeighbourHoodRecommender<SIMILARITY_TYPE>& _reco,
    INT_T nti, INT_T icis, INT_T icie): reco(_reco), nextThreadIndex(nti),
    itemComboIndexstart(icis), itemComboIndexend(icie)
    {
    }
};

template < typename SIMILARITY_TYPE >
void similarityThread(SimilarityThreadParams<SIMILARITY_TYPE> stp);

template < typename SIMILARITY_TYPE >
class NeighbourHoodRecommender {
    NeighbourHoodRecoParams algoParams;
    vector<RatingEntry> * ratingsList;
    vector<RatingEntry> testSetRatingsList;
    vector<INT_T> user_index_table; // user index
    vector<INT_T> item_index_table; // movie index
    const long long MAX_USERS, MAX_ITEMS;
    INT_T * uidRMap;
    INT_T * iidRMap;
    vector<RatingVector> itemRV; // itembased rating vector
    vector<FLT_T> itemAvgRating;
    vector<DynBitSet> userBst; // user bitset
    vector<fltvec> itemRtQuick; // quick rating lookup
    INT_T simCalcThreadCount;
    INT_T train_start, train_end, test_start, test_end;
    vector <long long> randomShuffledIndexes;

    Mtx * simTbl; // similarity table

    inline void partitionAsTrainingAndValidationSets(vector<RatingEntry> &T) {
        FLT_T tpct = T.size() * algoParams.training_sample_percentage;
        train_start = 0;
        train_end = test_start = (INT_T) tpct;
        test_end = T.size();
    }

    void printMaps(vector<INT_T> &vi, const char *s) {
        cout << s << "\n";
        for(INT_T i=0; i < vi.size(); i++) {
            cout << " " << i << " : " << vi[i] << ", ";
        }
    }

    void printRatingVector(RatingVector &rv, INT_T itm_id) {
      cout << " itemid " << itm_id << " orig itm id "
        << item_index_table[itm_id] << "\n";

      for(int i=0; i<rv.size(); i++) {
        cout << " " << rv[i].uId // transformed uids
          << " " << rv[i].rtng;
      }
      cout << "\n";
    }

    FLT_T getAvgRating(RatingVector &rv) {
      FLT_T avg = 0;
      FLT_T n = rv.size();
      for(int i=0; i<rv.size(); i++) {
        avg += rv[i].rtng;
      }
      return avg/n;
    }

    void printUsers(RatingVector &rv, const char * s) {
      cout << " " << s;
      for(INT_T x = 0; x < rv.size(); x++) {
        INT_T u = rv[x].uId;
        cout << " " << u;
      }
      cout << "\n";
    }

    void checkItemSimiliarty()
    {
      cout << " checkItemSimiliarty()\n";
      auto flt_min = std::numeric_limits<FLT_T>::min();
      INT_T num_items = item_index_table.size();
      simTbl = new Mtx(num_items, num_items, flt_min);
      INT_T item1 = 0, item2 = 0;
      INT_T cmb = 0;
      while(item1 < num_items) {
        for(item2 = item1+1; item2 < num_items; item2++) {
          FLT_T sim = getSimilarity(item1, item2);
          simTbl->set(item1, item2, sim);
          simTbl->set(item2, item1, sim);
          cmb++;
        }
        item1++;
      }
      simTbl->writeMtxToFileSystem(algoParams.sim_mtx_file_save_path.c_str());
      cout << " similarity comparisions made: " << cmb << "\n";
    }

    void checkItemSimiliartyThreaded(INT_T threadCount)
    {
      cout << " checkItemSimiliartyThreaded() threadCount " << threadCount << "\n";
      simCalcThreadCount = threadCount;

      auto flt_min = std::numeric_limits<FLT_T>::min();
      INT_T num_items = item_index_table.size();
      simTbl = new Mtx(num_items, num_items, flt_min);
      INT_T item1 = 0, item2 = 0;

      while(item1 < num_items) {
        for(item2 = item1+1; item2 < num_items; item2++) {
          itemCombo.push_back(ItemCombination(item1, item2));
        }
        item1++;
      }
      cout << " itemCombo.size() " << itemCombo.size() << "\n";
      INT_T comboSz = itemCombo.size()/threadCount;
      vector<thread> threadList;

      for(INT_T i=0; i<threadCount; i++) {
        INT_T start = i*comboSz;
        INT_T end = (start + comboSz);

        itemComboIndexstart = start ;
        itemComboIndexend = end;

        if(i == threadCount-1){
            itemComboIndexend = itemCombo.size() - 1;
        } else {
            itemComboIndexend -= 1;
        }

        SimilarityThreadParams<SIMILARITY_TYPE> stp(*this, nextThreadIndex,
          itemComboIndexstart, itemComboIndexend);

        threadList.push_back(thread(similarityThread<SIMILARITY_TYPE>, stp));
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
        nextThreadIndex++;
      }

      for(INT_T i=0; i<threadList.size(); i++) {
          threadList[i].join();
      }
      cout << " similarity comparisions todo: " << itemCombo.size() << "\n";

      simTbl->writeMtxToFileSystem(algoParams.sim_mtx_file_save_path.c_str());
    }

    void setItemsRatingMaps(RatingVector &rv,
          DynBitSet &ubst, FLT_T avgRating, vector<FLT_T> &quickRating)
    {
      for(int i=0; i<rv.size(); i++) {
        INT_T u = rv[i].uId;
        FLT_T rtg = rv[i].rtng;
        // rv[i].weightedRtng = rtg - avgRating;
        ubst[u] = 1;
        quickRating[u] = rtg  - avgRating; // mean centered rating
      }
    }

    void populateRatings() { // convert to internal form
      cout << " populateRatings item_index_table.size() "
        << item_index_table.size() << "\n";

      itemRV = vector<RatingVector>(item_index_table.size());
      itemAvgRating = vector<FLT_T>(item_index_table.size());
      userBst = vector<DynBitSet> (item_index_table.size());
      itemRtQuick = vector<fltvec> (item_index_table.size());

      for(INT_T i = 0; i< ratingsList->size(); i++) {
        RatingEntry &re = ratingsList->at(i);
        INT_T u = re.user_id;
        INT_T iid = re.item_id;
        FLT_T r = re.rating;
        INT_T newIid = iidRMap[iid];
        INT_T newUid = uidRMap[u];
        RatingVector &rv = itemRV[newIid];
        rv.push_back(Rating_T(newUid, r));
      }
      DELETE(ratingsList);

      for(INT_T i=0; i<itemRV.size(); i++) {
        INT_T itm = i;
        RatingVector &rv = itemRV[i];
        sort(itemRV[i].begin(), itemRV[i].end());

        INT_T last = rv.size() -1;
        INT_T maxUsrId = rv[last].uId;
        itemRtQuick[i] = vector<FLT_T>(maxUsrId+1);

        itemAvgRating[i] = getAvgRating(rv);
        userBst[i]  = DynBitSet(MAX_USERS);

        setItemsRatingMaps(itemRV[i],userBst[i], itemAvgRating[i], itemRtQuick[i]);
      }
    }

    void mapUserAndItemIndexes() {
        sort(user_index_table.begin(), user_index_table.end()); // sorted user ids
        sort(item_index_table.begin(), item_index_table.end()); // sorted item ids

        INT_T umax = user_index_table[user_index_table.size() -1] + 1;
        INT_T imax = item_index_table[item_index_table.size() -1] + 1;

        uidRMap = new INT_T[umax]();
        iidRMap = new INT_T[imax]();

        if(!uidRMap || !iidRMap) {
            cout << " if(!uidRMap || !iidRMap) \n";
            return;
        }

        INT_T i = 0;
        for(vector<INT_T>::iterator it = user_index_table.begin(),
                    ie = user_index_table.end(); it != ie; it++) {
            INT_T uid = *it;
            uidRMap[uid] = i;
            i++;
        }

        i = 0;
        for(vector<INT_T>::iterator it = item_index_table.begin(),
                    ie = item_index_table.end(); it != ie; it++) {
            INT_T iid = *it;

            iidRMap[iid] = i;
            i++;
        }
        cout << " user_index_table.size() " << user_index_table.size()
          << " item_index_table.size() " << item_index_table.size() << "\n";
    }

    void shuffleRatingsListIndexes() {
      std::srand(std::time(0));
      random_shuffle(randomShuffledIndexes.begin(), randomShuffledIndexes.end(), newRandom);
    }

    bool isCrossValidationEnabled()
    {
        return algoParams.training_sample_percentage > 0;
    }

    INT_T totalEntriesRead;
    bool readInputAndPartitionForCrossvalidationTests()
    {
        cout << " NeighbourHoodRecommender::readInputAndPartitionForCrossvalidationTests\n";
        INT_T userid, itemid, rating;

        char * uid_table = new char[MAX_USERS]();
        char * iid_table = new char[MAX_ITEMS]();
        FILE * csvFile = fopen (algoParams.csv_input_file_path.c_str(), "r");

        if(!csvFile) {
            cout << "ERROR: Unable to open input file check " << algoParams.csv_input_file_path
                 << " check --input-csv-file-path arg"<< "\n";
            return false;
        }

        INT_T rc = 0; // ratings count
        vector<RatingEntry> tmpRatingList;

        while(true) {
            int r = fscanf(csvFile,"%d %d %d",&userid, &itemid, &rating);
            if(r<0) {
                break;
            }
            tmpRatingList.push_back(RatingEntry(userid, itemid, rating));
            randomShuffledIndexes.push_back(rc++);
        }
        totalEntriesRead = rc;
        partitionAsTrainingAndValidationSets(tmpRatingList);

        for(INT_T i=train_start; i<train_end; i++) {
            INT_T idx = randomShuffledIndexes[i];

            userid = tmpRatingList[idx].user_id;
            itemid = tmpRatingList[idx].item_id;
            rating = tmpRatingList[idx].rating;

            if(uid_table[userid] == 0) {
                uid_table[userid] = 1;
                user_index_table.push_back(userid);
            }
            if(iid_table[itemid] == 0) {
                iid_table[itemid] = 1;
                item_index_table.push_back(itemid);
            }

            ratingsList->push_back(RatingEntry(userid, itemid, rating));
        }

        for(INT_T i=test_start; i<test_end; i++) {
            INT_T idx = randomShuffledIndexes[i];

            userid = tmpRatingList[idx].user_id;
            itemid = tmpRatingList[idx].item_id;
            rating = tmpRatingList[idx].rating;

            testSetRatingsList.push_back(RatingEntry(userid, itemid, rating));
        }

        tmpRatingList.clear();

        if(!user_index_table.size() || !item_index_table.size()) {
          throw("NeighbourHoodRecommender::readInput "
            "if(!user_index_table.size() || !item_index_table.size())");
        }

        mapUserAndItemIndexes();

        DELETE_AR(uid_table);
        DELETE_AR(iid_table);
        fclose(csvFile);

        return true;
    }

    bool readInput() {
        cout << " NeighbourHoodRecommender::readInput\n";
        INT_T userid, itemid, rating;

        char * uid_table = new char[MAX_USERS]();
        char * iid_table = new char[MAX_ITEMS]();
        FILE * csvFile = fopen (algoParams.csv_input_file_path.c_str(), "r");

        if(!csvFile) {
            cout << "ERROR: Unable to open input file check " << algoParams.csv_input_file_path
                 << " check --input-csv-file-path arg"<< "\n";
            return false;
        }

        INT_T rc = 0; // ratings count

        while(true) {
            int r = fscanf(csvFile,"%d %d %d",&userid, &itemid, &rating);
            if(r<0) {
                break;
            }

            if(uid_table[userid] == 0) {
                uid_table[userid] = 1;
                user_index_table.push_back(userid);
            }
            if(iid_table[itemid] == 0) {
                iid_table[itemid] = 1;
                item_index_table.push_back(itemid);
            }

            ratingsList->push_back(RatingEntry(userid, itemid, rating));
        }

        if(!user_index_table.size() || !item_index_table.size()) {
          throw("NeighbourHoodRecommender::readInput "
            "if(!user_index_table.size() || !item_index_table.size())");
        }

        mapUserAndItemIndexes();

        DELETE_AR(uid_table);
        DELETE_AR(iid_table);
        fclose(csvFile);

        return true;
    }

    void writeIndexTableToFileSystem(vector<INT_T> vi, const char * path)
    {
      FILE *fp = fopen(path, "wb");
      if(!fp)
        throw("NeighbourHoodRecommender::writeIndexTableToFileSystem if(!fp)");
      long long viSz = vi.size();
      size_t sz = fwrite(&viSz, sizeof(viSz), 1, fp);
      if(sz!=1) {
        fclose(fp);
        throw("NeighbourHoodRecommender::writeIndexTableToFileSystem if(sz!=1)");
      }

      long long written = 0;
      for(long long i=0; i<vi.size(); i++) {
        INT_T val = vi[i];
        written += fwrite(&val, sizeof(val), 1, fp);
      }
      if(written != vi.size()) {
        fclose(fp);
        throw("NeighbourHoodRecommender::writeIndexTableToFileSystem written != vi.size()");
      }
      fclose(fp);
    }

    void writeItemAndUserIndexTables()
    {
      writeIndexTableToFileSystem(item_index_table,
              algoParams.item_index_table_path.c_str());
      writeIndexTableToFileSystem(user_index_table,
              algoParams.user_index_table_path.c_str());
    }

    void cleanupIntermediates()
    {
      cout << " NeighbourHoodRecommender::cleanupIntermediates\n";
      user_index_table.clear();
      item_index_table.clear();
      DELETE_AR(uidRMap);
      DELETE_AR(uidRMap);
      itemRV.clear();
      itemAvgRating.clear();
      userBst.clear();
      itemRtQuick.clear();
    }

    void doItemItemRecommendation() {
        cout << " NeighbourHoodRecommender::doItemItemRecommendation starting\n";
        populateRatings();
        if(algoParams.max_thread_count ==1)
          checkItemSimiliarty();
        else
          checkItemSimiliartyThreaded(algoParams.max_thread_count);
        writeItemAndUserIndexTables();
        cleanupIntermediates();
        Mtx mtx2(algoParams.sim_mtx_file_save_path.c_str());
        bool b = simTbl->compare(mtx2);
        if(!b) {
          cout << " NeighbourHoodRecommender::doItemItemRecommendation if(!b)\n";
        } else {
          cout << " NeighbourHoodRecommender::doItemItemRecommendation "
            "simTbl->compare(mtx2) == true\n";
        }
    }

    void writeTestSetEntriesToFile(const char *path = "testSetRatingsList.RatingEntry")
    {
      FLT_T rmse = FLT_T_MAX();
      INT_T totalTestSetcount = 0;
      INT_T actualRMSENumElements = 0;

      cout << " NeighbourHoodRecommender::writeTestSetEntriesToFile writing "
        << testSetRatingsList.size() << " entries to " << path << endl;

      FILE * fp = fopen(path, "wb");
      if(!fp) {
        throw ("NeighbourHoodRecommender::writeTestSetEntriesToFile !fp");
      }

      INT_T numEntries = testSetRatingsList.size();

      size_t sz = fwrite(&numEntries, sizeof(INT_T), 1, fp);
      if(sz!=1) {
        throw ("NeighbourHoodRecommender::writeTestSetEntriesToFile sz!=1");
      }

      sz = 0;
      for(int i=0; i<testSetRatingsList.size(); i++) {
        INT_T uid = testSetRatingsList[i].user_id;
        INT_T iid = testSetRatingsList[i].item_id;
        FLT_T rtg = testSetRatingsList[i].rating;
        RatingEntry re(uid, iid, rtg);
        sz += fwrite(&re, sizeof(RatingEntry), 1, fp);
        // cout << "" << uid << "," << iid << "," << rtg << endl;
      }
      if(sz != testSetRatingsList.size()) {
        cout << " NeighbourHoodRecommender::writeTestSetEntriesToFile "
          << "sz != testSetRatingsList.size()" << endl;
      }
      fclose(fp);
    }

public:
    INT_T nextThreadIndex;
    INT_T itemComboIndexstart, itemComboIndexend;
    vector<ItemCombination> itemCombo; // used by threaded version only

    FLT_T getSimilarity(INT_T i1, INT_T i2);

    NeighbourHoodRecommender(NeighbourHoodRecoParams params) :algoParams(params),
        MAX_USERS(params.max_row_dim), MAX_ITEMS(params.max_col_dim),
        simTbl(0), simCalcThreadCount(0), nextThreadIndex(0)
    {
        ratingsList = new vector<RatingEntry> ();
    }

    ~NeighbourHoodRecommender() {
      cout << " ~NeighbourHoodRecommender() freeing resources \n";
    }

    bool readInput(bool isCrossValidate) {
        bool b = false;
        if(isCrossValidate) {
          b = readInputAndPartitionForCrossvalidationTests();
        } else {
          b = readInput();
        }
        return b;
    }

    bool startTraining() {
      return analyzeInputs();
    }

    bool analyzeInputs() {

        if(!readInput(isCrossValidationEnabled())) {
            return false;
        }

        doItemItemRecommendation();

        if(isCrossValidationEnabled()) {
          writeTestSetEntriesToFile();
        }

        return true;
    }

    void setSimTableValue(INT_T row, INT_T col, FLT_T val) {
        simTbl->set(row, col, val);
        simTbl->set(col, row, val);
    }

};

template < typename SIMILARITY_TYPE >
void similarityThread(SimilarityThreadParams<SIMILARITY_TYPE> stp) {
    NeighbourHoodRecommender<SIMILARITY_TYPE> &reco = stp.reco;
    INT_T nextThreadIndex = stp.nextThreadIndex;
    INT_T itemComboIndexstart = stp.itemComboIndexstart;
    INT_T itemComboIndexend = stp.itemComboIndexend;

    cout << "nextThreadIndex " << nextThreadIndex
            <<" itemComboIndexstart  "<< itemComboIndexstart
            << " itemComboIndexend " << itemComboIndexend << "\n";

    for(int i=itemComboIndexstart; i<=itemComboIndexend; i++){
        ItemCombination ic = reco.itemCombo[i];
        FLT_T sim = reco.getSimilarity(ic.item1, ic.item2);
        reco.setSimTableValue(ic.item1, ic.item2, sim);
        reco.setSimTableValue(ic.item2, ic.item1, sim);
    }
    execShellCommand("date");
    cout << "\n Thread exit " << nextThreadIndex << " exited \n";
}

#include "SimilarityFunctions.hpp"

#endif // ITEM_ITEM_LEARNER_HPP