//
// Copyright © 2017 Arm Ltd. All rights reserved.
// See LICENSE file in the project root for full license information.
//

#include <boost/test/unit_test.hpp>

#include "armnnTfParser/ITfParser.hpp"

#include "ParserPrototxtFixture.hpp"

BOOST_AUTO_TEST_SUITE(TensorflowParser)

// Tests that a Const node in Tensorflow can be converted to a ConstLayer in armnn (as opposed to most
// Const nodes which are used as weight inputs for convolutions etc. and are therefore not converted to
// armnn ConstLayers).
struct ConstantFixture : public ParserPrototxtFixture<armnnTfParser::ITfParser>
{
    ConstantFixture()
    {
        // input = tf.placeholder(tf.float32, name = "input")
        // const = tf.constant([17], tf.float32, [1])
        // output = tf.add(input, const, name = "output")
        m_Prototext =
            R"(
node {
  name: "input"
  op: "Placeholder"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "shape"
    value {
      shape {
        unknown_rank: true
      }
    }
  }
}
node {
  name: "Const"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: 1
          }
        }
        float_val: 17.0
      }
    }
  }
}
node {
  name: "output"
  op: "Add"
  input: "input"
  input: "Const"
  attr {
    key: "T"
    value {
      type: DT_FLOAT
    }
  }
}
            )";
        SetupSingleInputSingleOutput({ 1 }, "input", "output");
    }
};

BOOST_FIXTURE_TEST_CASE(Constant, ConstantFixture)
{
    RunTest<1>({1}, {18});
}

// Tests that a single Const node in Tensorflow can be used twice by a dependant node. This should result in only
// a single armnn ConstLayer being created.
struct ConstantReusedFixture : public ParserPrototxtFixture<armnnTfParser::ITfParser>
{
    ConstantReusedFixture()
    {
        // const = tf.constant([17], tf.float32, [1])
        // output = tf.add(const, const, name = "output")
        m_Prototext =
            R"(
node {
  name: "Const"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: 1
          }
        }
        float_val: 17.0
      }
    }
  }
}
node {
  name: "output"
  op: "Add"
  input: "Const"
  input: "Const"
  attr {
    key: "T"
    value {
      type: DT_FLOAT
    }
  }
}
            )";
        Setup({}, { "output" });
    }
};

BOOST_FIXTURE_TEST_CASE(ConstantReused, ConstantReusedFixture)
{
    RunTest<1>({}, { { "output", { 34 } } });
}

template <int ListSize>
struct ConstantValueListFixture : public ParserPrototxtFixture<armnnTfParser::ITfParser>
{
    ConstantValueListFixture()
    {
        m_Prototext =
            R"(
node {
  name: "output"
  op: "Const"
  attr {
    key: "dtype"
    value {
      type: DT_FLOAT
    }
  }
  attr {
    key: "value"
    value {
      tensor {
        dtype: DT_FLOAT
        tensor_shape {
          dim {
            size: 2
          }
          dim {
            size: 3
          }
        })";

        double value = 0.75;
        for (int i = 0; i < ListSize; i++, value += 0.25)
        {
            m_Prototext += std::string("float_val : ") + std::to_string(value) + "\n";
        }

        m_Prototext +=
            R"(
      }
    }
  }
}
            )";
        Setup({}, { "output" });
    }
};

using ConstantSingleValueListFixture = ConstantValueListFixture<1>;
using ConstantMultipleValueListFixture = ConstantValueListFixture<4>;
using ConstantMaxValueListFixture = ConstantValueListFixture<6>;

BOOST_FIXTURE_TEST_CASE(ConstantSingleValueList, ConstantSingleValueListFixture)
{
    RunTest<2>({}, { { "output", { 0.75f, 0.75f, 0.75f, 0.75f, 0.75f, 0.75f } } });
}
BOOST_FIXTURE_TEST_CASE(ConstantMultipleValueList, ConstantMultipleValueListFixture)
{
    RunTest<2>({}, { { "output", { 0.75f, 1.f, 1.25f, 1.5f,  1.5f,  1.5f } } });
}
BOOST_FIXTURE_TEST_CASE(ConstantMaxValueList, ConstantMaxValueListFixture)
{
    RunTest<2>({}, { { "output", { 0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.f } } });
}

template <bool WithShape, bool WithContent, bool WithValueList>
struct ConstantCreateFixture : public ParserPrototxtFixture<armnnTfParser::ITfParser>
{
    ConstantCreateFixture()
    {
        m_Prototext =
            R"(
node {
    name: "output"
    op: "Const"
    attr {
    key: "dtype"
    value {
        type: DT_FLOAT
    }
    }
    attr {
    key: "value"
    value {
        tensor {
        dtype: DT_FLOAT
            )";

        if (WithShape)
        {
            m_Prototext +=
                R"(
tensor_shape {
    dim {
    size: 2
    }
    dim {
    size: 2
    }
}
                )";
        }
        else
        {
            m_Prototext +=
                R"(
tensor_shape {
}
                )";
        }

        if (WithContent)
        {
            m_Prototext +=
                R"(
tensor_content: "\000\000\200?\000\000\200?\000\000\200?\000\000\200?\000\000\200?"
                )";
        }

        if (WithValueList)
        {
            m_Prototext +=
                R"(
float_val: 1.0
float_val: 1.0
float_val: 1.0
float_val: 1.0
float_val: 1.0
                )";
        }

        m_Prototext +=
            R"(
            }
        }
    }
}
            )";
    }
};

using ConstantCreateNoValueListFixture = ConstantCreateFixture<true, false, true>;
using ConstantCreateNoValueList2Fixture = ConstantCreateFixture<true, false, false>;
using ConstantCreateNoContentFixture = ConstantCreateFixture<true, true, false>;
using ConstantCreateNoContent2Fixture = ConstantCreateFixture<true, false, false>;
using ConstantCreateNoShapeFixture = ConstantCreateFixture<false, false, false>;
using ConstantCreateNoShape2Fixture = ConstantCreateFixture<false, true, false>;
using ConstantCreateNoShape3Fixture = ConstantCreateFixture<false, false, true>;

BOOST_FIXTURE_TEST_CASE(ConstantCreateInvalidValueList, ConstantCreateNoValueListFixture)
{
    BOOST_REQUIRE_THROW(Setup({}, { "output" }), armnn::ParseException);
}
BOOST_FIXTURE_TEST_CASE(ConstantCreateInvalidValueList2, ConstantCreateNoValueList2Fixture)
{
    BOOST_REQUIRE_THROW(Setup({}, { "output" }), armnn::ParseException);
}
BOOST_FIXTURE_TEST_CASE(ConstantCreateInvalidContent, ConstantCreateNoContentFixture)
{
    BOOST_REQUIRE_THROW(Setup({}, { "output" }), armnn::ParseException);
}
BOOST_FIXTURE_TEST_CASE(ConstantCreateInvalidShape, ConstantCreateNoShapeFixture)
{
    BOOST_REQUIRE_THROW(Setup({}, { "output" }), armnn::ParseException);
}
BOOST_FIXTURE_TEST_CASE(ConstantCreateNoShape2, ConstantCreateNoShape2Fixture)
{
    BOOST_REQUIRE_THROW(Setup({}, { "output" }), armnn::ParseException);
}
BOOST_FIXTURE_TEST_CASE(ConstantCreateNoShape3, ConstantCreateNoShape3Fixture)
{
    Setup({}, { "output" });
    RunTest<1>({}, { { "output", { 1.f, 1.f, 1.f, 1.f, 1.f } } });
}

BOOST_AUTO_TEST_SUITE_END()


#include <armadillo>
#include <h5cpp/all>

int main(){
	// create hdf5 container/file
	hid_t fd = h5::create("arma.h5");

	arma::vec data = {1,2,3,4,5};
	hid_t ds = h5::create<float>(fd,"/arma/data",{5,5,1,2});

	// offset - where to start within the array, in this case is {0,0,0,0}
	// count  - how many elements we would like to write: {a,b,c}
	h5::write(ds, data, {0,0,0,0},{1,5,1,1} ); 			//
	// we only want 3 elements back from offset 1, the rank must be 4
	auto rvo  = h5::read<arma::mat>(ds,{0,1,0,0},{1,3,1,1});
	rvo.print();

	H5Dclose(ds);
	h5::close(fd);
}


#ifndef __AST_BITVEC_HPP_DEFINED__
#define __AST_BITVEC_HPP_DEFINED__

#include <boost/lexical_cast.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <string>
#include <vector>

#include <assert.h>
#include <ilasynth/ast/node.hpp>
#include <ilasynth/type.hpp>
#include <z3++.h>

#include <ilasynth/ast.hpp>

namespace ilasynth {
class Abstraction;
class FuncReduction;

// ---------------------------------------------------------------------- //
// Bitvector expressions are derived from this class.
class BitvectorExpr : public Node {
public:
  // constructor.
  BitvectorExpr(int width);
  // constructor for ChoiceExpr.
  BitvectorExpr(NodeType t);
  // destructor.
  virtual ~BitvectorExpr();
};

// ---------------------------------------------------------------------- //
// Bitvector variables.
class BitvectorVar : public BitvectorExpr {
public:
  // constructor.
  BitvectorVar(const std::string& n, int width);
  // destructor.
  virtual ~BitvectorVar();
  // clone.
  virtual Node* clone() const;
  // equality method.
  virtual bool equal(const Node* that) const;
  // stream output.
  virtual std::ostream& write(std::ostream& out) const;
};

// ---------------------------------------------------------------------- //
// Bitvector constants.
class BitvectorConst : public BitvectorExpr {
protected:
  mp_int_t value;

public:
  // constructor with longs.
  BitvectorConst(const mp_int_t& v, int width);
  // constructor with ints.
  BitvectorConst(unsigned int v, int width);
  // copy constructor.
  BitvectorConst(const BitvectorConst& other);
  // destructor.
  virtual ~BitvectorConst();
  // clone
  virtual Node* clone() const;
  // equality method.
  virtual bool equal(const Node* that) const;
  // get value.
  const mp_int_t& val() const { return value; }
  // return value.
  virtual py::object getValue() const;
  // stream output.
  virtual std::ostream& write(std::ostream& out) const;
  // get the value as a string.
  std::string vstr() const { return boost::lexical_cast<std::string>(value); }
};

// ---------------------------------------------------------------------- //
// Bitvector operators.
class BitvectorOp : public BitvectorExpr {
public:
  // Number of operands.
  enum Arity { UNARY, BINARY, TERNARY, NARY } arity;

  // What is the operation?
  enum Op {
    // invalid
    INVALID,
    // unary
    NEGATE,
    COMPLEMENT,
    LROTATE,
    RROTATE,
    Z_EXT,
    S_EXT,
    EXTRACT,
    // binary.
    ADD,
    SUB,
    AND,
    OR,
    XOR,
    XNOR,
    NAND,
    NOR,
    SDIV,
    UDIV,
    SREM,
    UREM,
    SMOD,
    SHL,
    LSHR,
    ASHR,
    MUL,
    CONCAT,
    GET_BIT,
    READMEM,
    READMEMBLOCK,
    // ternary
    IF,
    APPLY_FUNC
  } op;

  static const std::string operatorNames[];

private:
  // the operands themselves.
  nptr_vec_t args;
  std::vector<int> params;

  // Don't forget to update these helper functions below.
  static bool isUnary(Op op) { return op >= NEGATE && op <= EXTRACT; }
  static bool isBinary(Op op) { return op >= ADD && op <= READMEMBLOCK; }
  static bool isTernary(Op op) { return op >= IF && op <= IF; }
  static bool isNary(Op op) { return op >= APPLY_FUNC && op <= APPLY_FUNC; }
  // helper functions to determine result and argument types.
  static int getUnaryResultWidth(Op op, const nptr_t& n);
  static int getBinaryResultWidth(Op op, const nptr_t& n1, const nptr_t& n2);
  static int getBinaryResultWidth(Op op, const nptr_t& n1, const nptr_t& n2,
                                  int param);
  static int getBinaryResultWidth(Op op, const nptr_t& n1, int param);
  static int getNaryResultWidth(Op op, nptr_vec_t& args);
  static int getNaryResultWidth(Op op, nptr_vec_t& args,
                                std::vector<int>& params);

  static bool checkUnaryOpWidth(Op op, const nptr_t& n, int width);
  static int checkBinaryOpWidth(Op op, const nptr_t& n1, const nptr_t& n2,
                                int width);
  static int checkBinaryOpWidth(Op op, const nptr_t& n1, const nptr_t& n2,
                                int param, int width);
  static int checkBinaryOpWidth(Op op, const nptr_t& n1, int param, int width);
  static int checkNaryOpWidth(Op op, nptr_vec_t& args, int width);
  static int checkNaryOpWidth(Op op, nptr_vec_t& args, std::vector<int>& params,
                              int width);

public:
  // constructors.
  // Unary op
  BitvectorOp(Op op, const nptr_t& n1);
  BitvectorOp(Op op, const nptr_t& n1, int param);
  BitvectorOp(Op op, const nptr_t& n1, int p1, int p2);
  // Binary op
  BitvectorOp(Op op, const nptr_t& n1, const nptr_t& n2);
  // Binary op with params (read-block)
  BitvectorOp(Op op, const nptr_t& n1, const nptr_t& n2, int blocks,
              endianness_t e);
  // Ternary op
  BitvectorOp(Op op, nptr_vec_t& args_);
  // copy-constructor with a fresh set of args.
  BitvectorOp(const BitvectorOp* other, nptr_vec_t& args_);
  // destructors.
  virtual ~BitvectorOp();

  // clone.
  virtual Node* clone() const;

  // equality method.
  virtual bool equal(const Node* that) const;

  // stream output.
  virtual std::ostream& write(std::ostream& out) const;

  // number of operands.
  virtual unsigned nArgs() const;

  // operand i.
  virtual nptr_t arg(unsigned i) const;

  Op getOp() const { return op; }

  // number of params
  unsigned nParams() const;

  // the ith param.
  int param(unsigned i) const;

  friend class FuncReduction;
};
} // namespace ilasynth
#endif


// The Expat License
//
// Copyright (c) 2017, Shlomi Fish
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <boost/countertree/set.hpp>
#include <cstdio>
#include <iostream>

static inline int read_int(FILE *const f)
{
    int ret;
    fscanf(f, "%d", &ret);
    return ret;
}

int main(int argc, char * argv[])
{
    const int n = std::stoi(argv[1]);
    const int k = std::stoi(argv[2]);

    countertree::multiset<int> myset;
    FILE * in = fopen("S2.txt", "r");
    for (int i = 0; i < k; i++) {
        myset.insert(read_int(in));
    }
    FILE * remove = fopen("S2.txt", "r");
    long long sum = 0;
    for (int i = 1; i <= n-k+1; i++) {
        sum += myset.pos((k-1) >> 1) + myset.pos((k) >> 1);
#if 0
        std::cout << "Sum[" << i << "] = " << sum << std::endl;
#endif
        myset.insert(read_int(in));
        const auto it = myset.find(read_int(remove));
        myset.erase(it);
    }
    fclose(remove);
    fclose(in);
    std::cout << "Sum = " << (sum >> 1) << (((sum&0x1) == 0) ? ".0" : ".5") <<
        std::endl;
    return 0;
}


#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/ximgproc/segmentation.hpp>
#include <boost/algorithm/string.hpp>
#include "getFiles.h"
#include "readCsv.h"
#include <ctime>
using namespace std;

/*!
 * 计算两个rect对象的iou
 * @param rects： selected search算法得到的rect
 * @param gt_rect：标注的ground truth
 * @return iou：两个rect的交并比
 */
double calcIOU(const cv::Rect &rects, const cv::Rect &gt_rect) {
  // 使用cv::Rect表示bbox的优势就是：可以方便的使用a|b表示并集，a&b表示交集
  cv::Rect I = rects & gt_rect;
  cv::Rect U = rects | gt_rect;
  double iou = I.area() * 1.0 / U.area();
  return iou;
}

int main() {
  clock_t start_time = clock();
  // 设置自动优化和多线程，可以加速程序
  cv::setUseOptimized(true);
  cv::setNumThreads(8);
  // select_search对象
  cv::Ptr<cv::ximgproc::segmentation::SelectiveSearchSegmentation>
	  ss = cv::ximgproc::segmentation::createSelectiveSearchSegmentation();

  // 读取image目录下的所有图片
  vector<string> gt_paths;
  vector<string> img_paths;
  getFiles("/home/ph/Dataset/Airplane/Airplanes_Annotations", gt_paths);
  getFiles("/home/ph/Dataset/Airplane/Images", img_paths);
//  getFiles("./image/gt", gt_paths);
//  getFiles("./image/img", img_paths);
  // sort竟然支持string数组的排序！！！
  // 对img_paths和gt_paths排序，保证两者同一下标表示的图片和标注能够匹配
  sort(gt_paths.begin(), gt_paths.end());
  sort(img_paths.begin(), img_paths.end());
  cout << "待处理图片总数量：" << img_paths.size() << endl;

  // 下面用selected search逐个处理图片
  for (int i = 0; i < img_paths.size(); i++) {
	string img_path = img_paths[i];
	string gt_path = gt_paths[i];
	// cv::Rect 内包含4个元素，矩形的左上角坐标(x，y)以及矩形的宽w和长h
	vector<cv::Rect> rects;// 保存region的vector,其中selected search算法返回的是opencv中的Rect类型的数据
	// 读取图片和ground_truth
	cv::Mat img = cv::imread(img_path, 1);
	vector<cv::Rect> gt_rects;
	readCsv(gt_path, gt_rects);
	// 使用switch...自动选择处理strategy时，需要先设置一个base_img。然后才能给出对应的strategy
	ss->setBaseImage(img); //设置为base_img
	// 设置处理精度
	ss->switchToSelectiveSearchQuality();
	// 处理图像,结果保存在rects中
	ss->process(rects);
	cout << "处理图片：" << i + 1 << " " << "候选区域数量：" << rects.size() << endl;

	// 处理这些regions
	int t_cnt = 0, f_cnt = 0; //统计正列和负例的数量，一般一副图片生成的regions中只要30个正列、负例就行
	vector<string> split_path;
	//获取图片的名称，方便下面给新图片命名使用
	boost::split(split_path, img_path, boost::is_any_of("/")); //c++的STL连个split都没有！！？
	string img_name = split_path.back();
	//当前处理图片，由selected search算法得到的每个region和标注的gt_rect都要计算iou
	for (int j = 0; j < rects.size() && j < 2000; j++) {
	  cv::Rect rect = rects[j];
	  int cnt = 0; //下面判断负例依据
	  for (const auto &gt_rect : gt_rects) {
		// 依次计算iou
		double iou = calcIOU(rect, gt_rect);
		// 根据iou为每个region打上标签
		// 如果这个region与标注中的任意一个gt_rect的iou>0.7就判断其为正例
		// 如果这个region与标注中的所有gt_rect的iou都小于0.3,就判断其为负例。因此要用cnt统计一下该信息
		if (iou > 0.7 && t_cnt <= 30) {
		  cv::Mat region(img, rect); // Mat可以使用原图像加rect的方式切割原图片的形式初始化
		  //resize到统一大小，方便之后输入到CNN中基
		  //这里不使用默认的双线性差值的方法，使用基于像素面积的采样方法
		  cv::resize(region, region, cv::Size(224, 224), cv::INTER_AREA);
		  //使用字符流来实现格式化字符串，为新图像命名，命名规则：j+正例(1)负例(2)+图像原名
		  ostringstream file_name;
		  file_name << "./region/" << j << "-1-" << img_name;
		  cv::imwrite(file_name.str(), region);
		  t_cnt++;
		} else if (iou < 0.3) {
		  cnt++;
		}
	  }
	  // 当cnt等于当前处理图片标注的数量时，代表该region是个背景（负例）
	  if (cnt == gt_rects.size() && f_cnt <= 30) {
		cv::Mat region(img, rect);
		cv::resize(region, region, cv::Size(224, 224), cv::INTER_AREA);
		ostringstream file_name;
		file_name << "./region/" << j << "-0-" << img_name;
		cv::imwrite(file_name.str(), region);
		f_cnt++;
	  }
	}
  }
  clock_t end_time = clock();
  cout<<"Done!"<<" "<<"total_time"<<(end_time - start_time)/CLOCKS_PER_SEC<<"s"<<endl;//2238s
  return 0;
}


#ifndef TVMTL_ALGOTRAITS_HPP
#define TVMTL_ALGOTRAITS_HPP

// own includes
#include "enumerators.hpp"

// Eigen includes
#include <Eigen/Sparse>
#include <Eigen/CholmodSupport>
#include <Eigen/SuperLUSupport>
#include <Eigen/IterativeLinearSolvers>
//#include <Eigen/PaStiXSupport>

namespace tvmtl {

template<enum MANIFOLD_TYPE MF>
struct algo_traits {

};

template<>
struct algo_traits<EUCLIDIAN> {

    static const int max_runtime = 1000;
    static const int max_irls_steps = 5;
    static const int max_newton_steps = 1;
    static constexpr double tolerance = 1e-4;

    static const bool use_preconditioner = false;

    template <typename H>
    using solver = Eigen::CholmodSupernodalLLT<H, Eigen::Upper>;
    //using solver = Eigen::ConjugateGradient<H, Eigen::Upper>;
    //using solver = Eigen::PastixLLT<H, Eigen::Lower>;
    //using solver = Eigen::SimplicialLLT<H, Eigen::Upper>;
};

template<>
struct algo_traits<SPHERE> {

    static const int max_runtime = 1000;
    static const int max_irls_steps = 5;
    static const int max_newton_steps = 1;
    static constexpr double tolerance = 1e-4;

    static const bool use_preconditioner = false;

    template <typename H>
    using solver = Eigen::SuperLU<H>;
    //using solver = Eigen::SparseLU<H>;
};

template<>
struct algo_traits<SO> {

    static const int max_runtime = 1000;
    static const int max_irls_steps = 5;
    static const int max_newton_steps = 1;
    static constexpr double tolerance = 1e-4;

    static const bool use_preconditioner = false;

    template <typename H>
    using solver = Eigen::SuperLU<H>;
    //using solver = Eigen::SparseLU<H>;
};

template<>
struct algo_traits<SPD> {

    static const int max_runtime = 1000;
    static const int max_irls_steps = 5;
    static const int max_newton_steps = 1;
    static constexpr double tolerance = 1e-4;

    static const bool use_preconditioner = false;

    template <typename H>
    using solver = Eigen::CholmodSupernodalLLT<H, Eigen::Upper>;
    //using solver = Eigen::SuperLU<H>;
};

template<>
struct algo_traits<GRASSMANN> {

    static const int max_runtime = 1000;
    static const int max_irls_steps = 5;
    static const int max_newton_steps = 1;
    static constexpr double tolerance = 1e-4;

    static const bool use_preconditioner = false;

    template <typename H>
    using solver = Eigen::SuperLU<H>;
};

}// end namespace tvmtl

#endif


#ifndef ASLAM_BACKEND_MAPPED_DV_QUAT_HPP
#define ASLAM_BACKEND_MAPPED_DV_QUAT_HPP

#include <Eigen/Core>
#include <aslam/backend/DesignVariable.hpp>
#include "RotationExpression.hpp"
#include "RotationExpressionNode.hpp"

namespace aslam {
namespace backend {

class MappedRotationQuaternion : public RotationExpressionNode, public DesignVariable {
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    MappedRotationQuaternion(double* q);

    virtual ~MappedRotationQuaternion();

    /// \brief Revert the last state update.
    virtual void revertUpdateImplementation();

    /// \brief Update the design variable.
    virtual void updateImplementation(const double* dp, int size);

    /// \brief the size of an update step
    virtual int minimalDimensionsImplementation() const;

    RotationExpression toExpression();

    void set(const Eigen::Vector4d& q) {
        _q = q;
        _p_q = q;
    }

  private:
    virtual Eigen::Matrix3d toRotationMatrixImplementation() const;
    virtual void evaluateJacobiansImplementation(JacobianContainer& outJacobians) const;
    virtual void evaluateJacobiansImplementation(JacobianContainer& outJacobians,
                                                 const Eigen::MatrixXd& applyChainRule) const;
    virtual void getDesignVariablesImplementation(DesignVariable::set_t& designVariables) const;

    /// Returns the content of the design variable
    virtual void getParametersImplementation(Eigen::MatrixXd& value) const;

    /// Sets the content of the design variable
    virtual void setParametersImplementation(const Eigen::MatrixXd& value);

    /// Computes the minimal distance in tangent space between the current value of the DV and xHat
    virtual void minimalDifferenceImplementation(const Eigen::MatrixXd& xHat, Eigen::VectorXd& outDifference) const;

    /// Computes the minimal distance in tangent space between the current value of the DV and xHat and the jacobian
    virtual void minimalDifferenceAndJacobianImplementation(const Eigen::MatrixXd& xHat, Eigen::VectorXd& outDifference,
                                                            Eigen::MatrixXd& outJacobian) const;

    Eigen::Map<Eigen::Vector4d> _q;
    Eigen::Vector4d _p_q;
    Eigen::Matrix3d _C;
};

}  // namespace backend
}  // namespace aslam

#endif /* ASLAM_BACKEND_DV_QUAT_HPP */


#ifndef HSMM_FORWARDBACKWARD_H
#define HSMM_FORWARDBACKWARD_H

#include <armadillo>
#include <set>

double logsumexp(arma::vec c);

class ObservedSegment {
    public:
        ObservedSegment(int t, int d);

        ObservedSegment(int t, int d, int hidden_state);

        int getDuration() const;

        int getEndingTime() const;

        int getHiddenState() const;

        int getStartingTime() const;

        bool operator< (const ObservedSegment & segment) const;

    private:
        int t_;
        int d_;
        int hidden_state_;
};

class Labels {
    public:
        Labels();

        // Sets a segment observation ending at t with duration d.
        void setLabel(int t, int d);

        // As above but additionaly specifies the generating hidden state.
        void setLabel(int t, int d, int hidden_state);

        bool empty() const;

        // Checks if a particular segment is consistent with the set labels.
        bool isConsistent(int t, int d, int hidden_state) const;

        // Checks if a particular segment was given as a label.
        bool isLabel(int t, int d, int hidden_state) const;

        // As above but without specifying the hidden state.
        bool isLabel(int t, int d) const;

        // Checks if there is a segment ending at t generated by state hs_from
        // and other starting at t+1 generated from hs_to.
        bool transition(int hs_from, int hs_to, int t) const;

        // Checks if there is a transition at time t regardless of the hidden
        // states involved.
        bool transition(int t) const;

        // Returns the leftmost segment present as a label.
        const ObservedSegment& getFirstSegment() const;

        // Returns the rightmost segment present as a label.
        const ObservedSegment& getLastSegment() const;

        const std::set<ObservedSegment>& getLabels() const;

    private:
        bool overlaps_(int t, int d) const;

        std::set<ObservedSegment> labels_;
};

void FB(const arma::mat& transition,const arma::vec& pi,
        const arma::mat& duration, const arma::cube& pdf,
        const Labels& obs_segments,arma::mat& alpha,
        arma::mat& beta, arma::mat& alpha_s, arma::mat& beta_s,
        arma::vec& beta_s_0, arma::cube& eta, const int min_duration,
        const int nobs);

void logsFB(const arma::mat& transition,const arma::vec& pi,
        const arma::mat& duration, const arma::cube& log_pdf,
        const Labels& obs_segments, arma::mat& alpha, arma::mat& beta,
        arma::mat& alpha_s, arma::mat& beta_s, arma::vec& beta_s_0,
        arma::cube& eta, arma::cube& zeta, const int min_duration,
        const int nobs);

void Viterbi(const arma::mat& transition,const arma::vec& pi,
        const arma::mat& duration, const arma::cube& log_pdf, arma::mat& delta,
        arma::imat& psi_duration, arma::imat& psi_state,
        const int min_duration, const int nobs);

void viterbiPath(const arma::imat& psi_d, const arma::imat& psi_s,
        const arma::mat& delta, arma::ivec& hiddenStates,
        arma::ivec& hiddenDurations);

#endif


#include <Eigen/Dense>
#include <iostream>

#include <chrono>
#include <random>
#include <stdexcept>
#include <thread>
#include <vector>

#include <cstdlib>

using std::cout;
using std::endl;
using std::mt19937_64;
using std::normal_distribution;
using std::random_device;
using std::runtime_error;
using std::thread;
using std::uniform_int_distribution;
using std::vector;

using std::chrono::high_resolution_clock;
using std::chrono::duration;
using std::chrono::duration_cast;

using Eigen::VectorXf;
using Eigen::Map;

void worker(float *dataset, float *query, const vector<int> &candidates, int d,
            int u, int v, float *dummy) {
  *dummy = 0.0;
  for (int i = u; i < v; ++i) {
    *dummy += Map<VectorXf>(dataset + candidates[i] * d, d)
                  .dot(Map<VectorXf>(query, d));
  }
}

int main() {
  const int N = 1200000;
  const int D = 104;
  const int Q = 100000000;
  cout << N << " points" << endl;
  cout << D << " dimensions" << endl;
  cout << "retrieving " << Q << " candidates" << endl;
  int max_num_threads = thread::hardware_concurrency();
  cout << max_num_threads << " threads are supported" << endl;
  float *dataset;
  if (posix_memalign((void **)&dataset, 32, sizeof(float) * N * D)) {
    throw runtime_error("can't allocate memory for dataset");
  }
  random_device rd;
  mt19937_64 gen(rd());
  normal_distribution<float> g(0.0, 1.0);
  for (int i = 0; i < N * D; ++i) {
    dataset[i] = g(gen);
  }
  vector<int> candidates(Q);
  uniform_int_distribution<> u(0, N - 1);
  for (int i = 0; i < Q; ++i) {
    candidates[i] = u(gen);
  }
  for (int num_threads = 1; num_threads <= max_num_threads; ++num_threads) {
    float *queries;
    if (posix_memalign((void **)&queries, 32,
                       sizeof(float) * D * num_threads)) {
      throw runtime_error("can't allocate memory for queries");
    }
    for (int i = 0; i < num_threads * D; ++i) {
      queries[i] = g(gen);
    }
    vector<int> start(num_threads + 1);
    start[0] = 0;
    for (int i = 0; i < num_threads; ++i) {
      int cnt = Q / num_threads;
      int rem = Q % num_threads;
      if (i < rem) {
        ++cnt;
      }
      start[i + 1] = start[i] + cnt;
    }
    vector<float> dummy(num_threads, 0.0);
    vector<thread> threads;
    auto t1 = high_resolution_clock::now();
    for (int i = 0; i < num_threads; ++i) {
      threads.push_back(thread(worker, dataset, queries + i * D, candidates, D,
                               start[i], start[i + 1], &dummy[i]));
    }
    for (int i = 0; i < num_threads; ++i) {
      threads[i].join();
    }
    auto t2 = high_resolution_clock::now();
    cout << num_threads << " thread" << ((num_threads > 1) ? "s" : "") << ": "
         << duration_cast<duration<double>>(t2 - t1).count() << " seconds"
         << endl;
    free(queries);
  }
  free(dataset);
}