#include "Graph.h"
#include "PropagatedValue.h"
#include "PngCreator.h"

#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>

int main(int argc, char* argv[]) {
    if(argc < 2) {
        std::cout << "you should provide an input file" << std::endl;
    }

    //Structures
    Graph<size_t> graph;
    PropagatedValue<size_t, long long int, Min<long long int>> time;
    PropagatedValue<size_t, float, Average<float>> longitudes;
    std::unordered_set<size_t> all_ids;

    //Parsing
    std::cout << "parsing" << std::endl;
    typedef boost::tokenizer<boost::char_separator<char>> Tokenizer;
    static boost::char_separator<char> sep("\t");
    static boost::regex time_regex("([+-]\\d+)-(\\d{2})-(\\d{2})T\\d{2}:\\d{2}:\\d{2}Z/\\d{1,2}");
    static boost::regex coordinates_regex("@([+-]?\\d{1,2}(\\.\\d+)?)/([+-]?\\d{1,3}(\\.\\d+))?");

    std::string line;
    std::vector<std::string> tokens;
    std::hash<std::string> hash;
    std::ifstream input(argv[1]);
    int count = 0;
    while(std::getline(input, line)) {
        count++;
        if((count % 1000000) == 0) {
            std::cout << "." << std::flush;
        }

        Tokenizer tok(line, sep);
        tokens.assign(tok.begin(), tok.end());
        if(tokens.size() < 3) {
            continue;
        }
        std::string subject = tokens[0];
        boost::algorithm::trim(subject);
        size_t subject_hash = hash(subject);
        all_ids.insert(subject_hash);

        std::string object = tokens[2];
        boost::algorithm::trim(object);

        boost::smatch sm;
        switch(object[0]) {
            case 'Q': {
                size_t object_hash = hash(object);
                all_ids.insert(object_hash);
                graph.addEdge(subject_hash, object_hash);
                graph.addEdge(object_hash, subject_hash);
                break;
            }
            case '+':
            case '-':
                if(boost::regex_match(object, sm, time_regex)) {
                    try {
                        time.addValue(subject_hash, std::stoll(sm.str(1))*31*12 + std::stoll(sm.str(2))*31 + std::stoll(sm.str(3)));
                    } catch(std::out_of_range&) {}
                }
                break;
            case '@':
                if(boost::regex_match(object, sm, coordinates_regex)) {
                    try {
                        longitudes.addValue(subject_hash, std::stof(sm.str(3)));
                    } catch(std::out_of_range&) {}
                    }
                break;
        }
    }

    std::cout << std::endl << "propagate time" << std::endl;
    time.propagate(graph);

    std::cout << "propagate coordinates" << std::endl;
    longitudes.propagate(graph);

    std::cout << "output" << std::endl;
    PngCreator png_creator;
    png_creator.createPng(std::string("wikidata"), all_ids, time, longitudes);

	return 0;
}


//==============================================================================
//         Copyright 2009 - 2014 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_AVX_DETAILS_PERMUTATION_HPP_INCLUDED
#define BOOST_SIMD_SWAR_FUNCTIONS_SIMD_SSE_AVX_DETAILS_PERMUTATION_HPP_INCLUDED

#include <boost/simd/include/functions/simd/bitwise_cast.hpp>
#include <boost/simd/include/functions/simd/bitwise_and.hpp>
#include <boost/simd/include/functions/simd/make.hpp>
#include <boost/simd/swar/functions/details/default_permutation.hpp>
#include <boost/simd/swar/functions/simd/sse/avx/details/topology.hpp>
#include <boost/simd/swar/functions/details/topology.hpp>
#include <boost/simd/swar/functions/details/permuted.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/dispatch/meta/as_floating.hpp>
#include <boost/dispatch/attributes.hpp>

namespace boost { namespace simd { namespace ext
{
  //============================================================================
  // AVX shuffle use general permutation with special case for some topology
  //============================================================================
  template<std::size_t Cardinal>
  struct avx_permutation : details::default_permutation<Cardinal>
  {};

  //============================================================================
  // Single parameters card4 permutation are handled by _mm256_shuffle_*
  //============================================================================
  template<>
  struct  avx_permutation<4>
  {
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call(T const& a0, P const& p)
    {
      return call(a0,p,typename details::avx_topology<P,4>::type());
    }

    // Duplicate _mm256_shuffle_pd
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0,  P const& p
                                    , details::dupe_ const&
                                    )
    {
      return call(a0,a0,p,details::direct_());
    }

    // Direct use _mm256_shuffle_pd(a0,Zero)
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::r_zero_ const&
                                    )
    {
      return call(a0,Zero<T>(),p,details::direct_());
    }

    // Direct use _mm256_shuffle_pd(Zero,a0)
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::l_zero_ const&
                                    )
    {
      return call(Zero<T>(),a0,p,details::direct_());
    }

    // perm2_ use permute2f128 for block based permutation
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::perm_ const&
                                    )
    {
      typename boost::is_integral<typename T::value_type>::type selector_;
      return perm2(a0,p,selector_);
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, P const&
                                    , boost::mpl::false_ const&
                                    )
    {
      return _mm256_permute2f128_pd(a0,a0,(details::avx_perm2_mask<P,4>::value));
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, P const&
                                    , boost::mpl::true_ const&
                                    )
    {
      return _mm256_permute2f128_si256(a0,a0,(details::avx_perm2_mask<P,4>::value));
    }

    // Mixed just use regular hand made permutation
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::mixed_ const&
                                    )
    {
      return details::default_permutation<4>::call(a0,p);
    }

    // Two arguments check topology
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call(T const& a0, T const& a1, P const& p)
    {
      return call(a0,a1,p,typename details::avx_topology<P,4>::type());
    }

    // Mixed just use regular hand made permutation
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::mixed_ const&
                                    )
    {
      return details::default_permutation<4>::call(a0,a1,p);
    }

    // blend use _mm256_blend_pd
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const&
                                    , details::blend_ const&
                                    )
    {
      typedef typename dispatch::meta::as_floating<T>::type f_t;

      return bitwise_cast<T>( f_t(_mm256_blend_pd ( bitwise_cast<f_t>(a0)
                                                  , bitwise_cast<f_t>(a1)
                                                  , (details::avx_blend_mask<P,4>::value)
                                                  )
                                 )
                            );
    }

    // Direct use _mm256_shuffle_pd
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const&
                                    , details::direct_ const&
                                    )
    {
      typedef typename dispatch::meta::as_floating<T>::type f_t;

      return bitwise_cast<T>( f_t(_mm256_shuffle_pd ( bitwise_cast<f_t>(a0)
                                                    , bitwise_cast<f_t>(a1)
                                                    , (details::avx_mask<P,4>::value)
                                                    )
                                 )
                            );
    }

    // Swapped _mm256_shuffle_pd
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::swap_ const&
                                    )
    {
      return call(a1,a0,p,details::direct_());
    }

    // permute2f128_* calls
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::perm_ const&
                                    )
    {
      typename boost::is_integral<typename T::value_type>::type selector_;
      return perm2(a0,a1,p,selector_);
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, T const& a1, P const&
                                    , boost::mpl::false_ const&
                                    )
    {
      return _mm256_permute2f128_pd(a0,a1,(details::avx_perm2_mask<P,4>::value));
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, T const& a1, P const&
                                    , boost::mpl::true_ const&
                                    )
    {
      return _mm256_permute2f128_si256(a0,a1,(details::avx_perm2_mask<P,4>::value));
    }
  };

  template<>
  struct  avx_permutation<8>
  {
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call(T const& a0, P const& p)
    {
      return call(a0,p,typename details::avx_topology<P,8>::type());
    }

    // Two arguments duplicate
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::dupe_ const&
                                    )
    {
      return call(a0,a0,p,details::direct_());
    }

    // Two arguments with a 0
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::r_zero_ const&
                                    )
    {
      return call(a0,Zero<T>(),p,details::direct_());
    }

    // Two arguments with a 0
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::l_zero_ const&
                                    )
    {
      return call(Zero<T>(),a0,p,details::direct_());
    }

    // perm2_ use permute2f128 for block based permutation
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::perm_ const&
                                    )
    {
      typename boost::is_integral<typename T::value_type>::type selector_;
      return perm2(a0,p,selector_);
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, P const&
                                    , boost::mpl::false_ const&
                                    )
    {
      return _mm256_permute2f128_ps(a0,a0,(details::avx_perm2_mask<P,8>::value));
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, P const&
                                    , boost::mpl::true_ const&
                                    )
    {
      return _mm256_permute2f128_si256(a0,a0,(details::avx_perm2_mask<P,8>::value));
    }

    // Two arguments mixed
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, P const& p
                                    , details::mixed_ const&
                                    )
    {
      return details::default_permutation<8>::call(a0,p);
    }

    // Two arguments check topology
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call(T const& a0, T const& a1, P const& p)
    {
      return call(a0,a1,p,typename details::avx_topology<P,8>::type());
    }

    // Two arguments direct
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const&
                                    , details::direct_ const&
                                    )
    {
      typedef typename dispatch::meta::as_floating<T>::type f_t;

      return bitwise_cast<T>(f_t(_mm256_shuffle_ps ( bitwise_cast<f_t>(a0)
                                                   , bitwise_cast<f_t>(a1)
                                                   , (details::avx_mask<P,8>::value)
                                                   )
                                )
                            );
    }

    // Two arguments swapped
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::swap_ const&
                                    )
    {
      return call(a1,a0,p,details::direct_());
    }

    // Two arguments using permute2f128
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::perm_ const&
                                    )
    {
      typename boost::is_integral<typename T::value_type>::type selector_;
      return perm2(a0,a1,p,selector_);
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, T const& a1, P const&
                                    , boost::mpl::false_ const&
                                    )
    {
      return _mm256_permute2f128_ps(a0,a1,(details::avx_perm2_mask<P,8>::value));
    }

    template<typename T, typename P>
    BOOST_FORCEINLINE static T perm2( T const& a0, T const& a1, P const&
                                    , boost::mpl::true_ const&
                                    )
    {
      return _mm256_permute2f128_si256(a0,a1,(details::avx_perm2_mask<P,8>::value));
    }

    // Two arguments mixed
    template<typename T, typename P>
    BOOST_FORCEINLINE static T call ( T const& a0, T const& a1, P const& p
                                    , details::mixed_ const&
                                    )
    {
      return details::default_permutation<8>::call(a0,a1,p);
    }
  };
} } }

#endif


// Author(s): Wieger Wesselink
// Copyright: see the accompanying file COPYING or copy at
// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
/// \file absinthe_test.cpp
/// \brief Test program for absinthe algorithm.

#define BOOST_TEST_MODULE absinthe_test
#include <boost/test/included/unit_test_framework.hpp>
#include "mcrl2/pbes/absinthe.h"
#include "mcrl2/pbes/txt2pbes.h"

using namespace mcrl2;
using namespace mcrl2::pbes_system;

BOOST_AUTO_TEST_CASE(test_separate_keyword_section)
{
  std::string text =
    "\n"
    "\n"
    "absmap\n"
    "h: Nat -> AbsNat\n"
  ;

  std::vector<std::string> all_keywords = { "sort", "absmap" };
  std::pair<std::string, std::string> q = utilities::detail::separate_keyword_section(text, "absmap", all_keywords);
  BOOST_CHECK(q.first.find("absmap") == 0);
  BOOST_CHECK(utilities::trim_copy(q.second).empty());
}

void test_absinthe(const std::string& pbes_text, const std::string& abstraction_text, bool is_over_approximation)
{
  pbes p = txt2pbes(pbes_text);
  absinthe_algorithm algorithm;
  algorithm.run(p, abstraction_text, is_over_approximation);
}

// test with structured sorts
BOOST_AUTO_TEST_CASE(test_structured_sorts)
{
  std::string PBES_TEXT =
    "sort Bit = struct e0 | e1;                     \n"
    "                                               \n"
    "map inv : Bit -> Bit;                          \n"
    "                                               \n"
    "eqn                                            \n"
    "  inv(e0) = e1;                                \n"
    "  inv(e1) = e0;                                \n"
    "                                               \n"
    "pbes                                           \n"
    "                                               \n"
    "nu X(b:Bit) = val (b == e0) && X(inv(inv(b))); \n"
    "                                               \n"
    "init X(e0);                                    \n"
    ;

  std::string ABSTRACTION_TEXT =
    "sort                                                                   \n"
    "  AbsBit  = struct arbitrary;                                          \n"
    "                                                                       \n"
    "var                                                                    \n"
    "  b:Bit;                                                               \n"
    "eqn                                                                    \n"
    "  h(b) = arbitrary;                                                    \n"
    "  abseq(arbitrary,arbitrary) = {true,false};                           \n"
    "  absinv(arbitrary) = {arbitrary};                                     \n"
    "                                                                       \n"
    "absmap                                                                 \n"
    "h: Bit -> AbsBit;                                                      \n"
    "                                                                       \n"
    "absfunc                                                                \n"
    "  ==: Bit # Bit -> Bool        := abseq : AbsBit # AbsBit -> Set(Bool) \n"
    "  inv: Bit -> Bit              := absinv : AbsBit -> Set(AbsBit)       \n"
  ;

  test_absinthe(PBES_TEXT, ABSTRACTION_TEXT, true);
}


#include <boost/math/special_functions/next.hpp>
#include <boost/random.hpp>

#include <limits>

#include "math_functions.hpp"

template<>
void cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const float alpha, const float* A, const float* B, const float beta,
    float* C) {
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
}

template<>
void cpu_gemm<double>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const double alpha, const double* A, const double* B, const double beta,
    double* C) {
  int lda = (TransA == CblasNoTrans) ? K : M;
  int ldb = (TransB == CblasNoTrans) ? N : K;
  cblas_dgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, N);
}

template<>
void cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const float alpha, const float* A, const int lda,
    const float* B, const int ldb,
    const float beta, float* C, const int ldc) {
  cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, ldc);
}

template<>
void cpu_gemm<double>(const CBLAS_TRANSPOSE TransA,
    const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
    const double alpha, const double* A, const int lda,
    const double* B, const int ldb,
    const double beta, double* C, const int ldc) {
  cblas_dgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
      ldb, beta, C, ldc);
}

template <>
void cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const float alpha, const float* A, const float* x,
    const float beta, float* y) {
  cblas_sgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
}

template <>
void cpu_gemv<double>(const CBLAS_TRANSPOSE TransA, const int M,
    const int N, const double alpha, const double* A, const double* x,
    const double beta, double* y) {
  cblas_dgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
}

template <>
void cpu_axpy<float>(const int N, const float alpha, const float* X,
    float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }

template <>
void cpu_axpy<double>(const int N, const double alpha, const double* X,
    double* Y) { cblas_daxpy(N, alpha, X, 1, Y, 1); }

template <>
void cpu_axpby<float>(const int N, const float alpha, const float* X,
                            const float beta, float* Y) {
  cblas_saxpby(N, alpha, X, 1, beta, Y, 1);
}

template <>
void cpu_axpby<double>(const int N, const double alpha, const double* X,
                             const double beta, double* Y) {
  cblas_daxpby(N, alpha, X, 1, beta, Y, 1);
}

template <>
float cpu_strided_dot<float>(const int n, const float* x, const int incx,
    const float* y, const int incy) {
  return cblas_sdot(n, x, incx, y, incy);
}

template <>
double cpu_strided_dot<double>(const int n, const double* x,
    const int incx, const double* y, const int incy) {
  return cblas_ddot(n, x, incx, y, incy);
}

template <typename Dtype>
Dtype cpu_dot(const int n, const Dtype* x, const Dtype* y) {
  return cpu_strided_dot(n, x, 1, y, 1);
}

template
float cpu_dot<float>(const int n, const float* x, const float* y);

template
double cpu_dot<double>(const int n, const double* x, const double* y);

template <>
float cpu_asum<float>(const int n, const float* x) {
  return cblas_sasum(n, x, 1);
}

template <>
double cpu_asum<double>(const int n, const double* x) {
  return cblas_dasum(n, x, 1);
}

template <typename Dtype>
void cpu_set(const int N, const Dtype alpha, Dtype* Y) {
  if (alpha == 0) {
    memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
    return;
  }
  for (int i = 0; i < N; ++i) {
    Y[i] = alpha;
  }
}

template void cpu_set<int>(const int N, const int alpha, int* Y);
template void cpu_set<float>(const int N, const float alpha, float* Y);
template void cpu_set<double>(const int N, const double alpha, double* Y);

template <>
void cpu_add<float>(const int n, const float* a, const float* b,
    float* y) {
  vsAdd(n, a, b, y);
}

template <>
void cpu_add<double>(const int n, const double* a, const double* b,
    double* y) {
  vdAdd(n, a, b, y);
}

template <>
void cpu_sub<float>(const int n, const float* a, const float* b,
    float* y) {
  vsSub(n, a, b, y);
}

template <>
void cpu_sub<double>(const int n, const double* a, const double* b,
    double* y) {
  vdSub(n, a, b, y);
}

template <>
void cpu_mul<float>(const int n, const float* a, const float* b,
    float* y) {
  vsMul(n, a, b, y);
}

template <>
void cpu_mul<double>(const int n, const double* a, const double* b,
    double* y) {
  vdMul(n, a, b, y);
}

template <>
void cpu_div<float>(const int n, const float* a, const float* b,
    float* y) {
  vsDiv(n, a, b, y);
}

template <>
void cpu_div<double>(const int n, const double* a, const double* b,
    double* y) {
  vdDiv(n, a, b, y);
}

template <>
void cpu_powx<float>(const int n, const float* a, const float b,
    float* y) {
  vsPowx(n, a, b, y);
}

template <>
void cpu_powx<double>(const int n, const double* a, const double b,
    double* y) {
  vdPowx(n, a, b, y);
}

template <>
void cpu_sqr<float>(const int n, const float* a, float* y) {
  vsSqr(n, a, y);
}

template <>
void cpu_sqr<double>(const int n, const double* a, double* y) {
  vdSqr(n, a, y);
}

template <>
void cpu_exp<float>(const int n, const float* a, float* y) {
  vsExp(n, a, y);
}

template <>
void cpu_exp<double>(const int n, const double* a, double* y) {
  vdExp(n, a, y);
}

template <>
void cpu_abs<float>(const int n, const float* a, float* y) {
    vsAbs(n, a, y);
}

template <>
void cpu_abs<double>(const int n, const double* a, double* y) {
    vdAbs(n, a, y);
}

template <>
void cpu_add_scalar(const int N, const float alpha, float* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <>
void cpu_add_scalar(const int N, const double alpha, double* Y) {
  for (int i = 0; i < N; ++i) {
    Y[i] += alpha;
  }
}

template <typename Dtype>
void cpu_copy(const int N, const Dtype* X, Dtype* Y) {
  if (X != Y) {
    memcpy(Y, X, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
  }
}

template void cpu_copy<int>(const int N, const int* X, int* Y);
template void cpu_copy<unsigned int>(const int N, const unsigned int* X,
    unsigned int* Y);
template void cpu_copy<float>(const int N, const float* X, float* Y);
template void cpu_copy<double>(const int N, const double* X, double* Y);


//
// Created by wiluite on 18.01.2020.
//

#define BOOST_TEST_MODULE boost_test_module_
#include <boost/test/unit_test.hpp> // UTF ??
#define MAIN_IS_ABSENT
#include "main.cpp"

BOOST_AUTO_TEST_CASE(test1) {

    using namespace daq;

    ricker_filter_data_type filter_data {};
    size_t filter_size {};
    ricker_filter filter {filter_data, filter_size};

    create_filter (make_odd(align_by_ten(ricker_filter_size(f, rate))), filter);

    signal_sequence_type ss (static_cast<size_t>(rate) + filter.sz);
    std::iota(std::begin(ss), std::end(ss), -rate/2);

    transform_uni (ss, std::addressof(transform_uni_result[0]), filter);
    transform_omp (ss, std::addressof(transform_omp_result[0]), filter);
    transform_tw (ss, std::addressof(transform_tw_result[0]), filter);

    // transform_uni always equals transform_tw
    BOOST_REQUIRE (transform_uni_result == transform_tw_result);

    // transform_omp_result differ from the others for a very small epsilon (dont ask why)
    auto cnt = 0;
    constexpr double epsilon_ = 0.0000000001;
    for (auto elem : transform_uni_result)
    {
        if (elem != transform_omp_result[cnt])
        {
            BOOST_REQUIRE ((abs (elem-transform_omp_result[cnt]) < epsilon_));
        }
        ++cnt;
    }
}


#include <iostream>
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include "Kinectv3.h"
#include "odometry.h"
hitcrt::Kinectv3 cam(0, false, "");
int main()
{
    std::shared_ptr<cv::Mat> depth, aligned_color, color;
    // Eigen::Matrix3d K, R;
    // Eigen::Vector3d t;
    // K << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1;
    // R << 1, 0, 0, 0, 1, 0, 0, 0, 1;
    // t << 0, 0, 0;
    // cv::Mat K = (cv::Mat_<double>(3,3)<<520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1);
    // odometry test(K);
    // cv::Mat p1 = cv::imread("/home/ethan/Documents/slam/slambook2/ch7/1.png");
    // cv::Mat d1 = cv::imread("/home/ethan/Documents/slam/slambook2/ch7/1_depth.png", cv::IMREAD_UNCHANGED);
    // cv::Mat p2 = cv::imread("/home/ethan/Documents/slam/slambook2/ch7/2.png");
    // cv::Mat d2 = cv::imread("/home/ethan/Documents/slam/slambook2/ch7/2_depth.png", cv::IMREAD_UNCHANGED);
    // test.solve(p1, d1);
    // test.solve(p2, d2);
    // test.print_info();
    odometry test(cam.color_intrin);
    cv::namedWindow("depth");
    cv::namedWindow("color");

    std::string result;
    char key = '\0';
    while(key != 'q' || key != 27)
    {
        result = cam.grab();
        if(result == "eof")
            break;
        color = cam.getColorImage();
        aligned_color = cam.getAlignedColorImage();
        depth = cam.getDepthImage();

        if(color!=NULL&&!color->empty()
            &&aligned_color!=NULL&&!aligned_color->empty()
            &&depth!=NULL&&!depth->empty())
        {
            // cv::normalize(*aligned_color, *aligned_color, 0, 256*256, cv::NORM_MINMAX);
            cv::imshow("color", *color);
            cv::imshow("depth", *depth);
            test.solve(*color, *depth);
            test.print_info();
        }
        key = cv::waitKey(1);
    }
    return 0;
}


#ifndef DESCARTES_LIGHT_SOLVERS_BGL_IMPL_BGL_DIJKSTRA_SOLVER_HPP
#define DESCARTES_LIGHT_SOLVERS_BGL_IMPL_BGL_DIJKSTRA_SOLVER_HPP

#include <descartes_light/solvers/bgl/bgl_dijkstra_solver.h>
#include <descartes_light/solvers/bgl/impl/event_visitors.hpp>

#include <descartes_light/descartes_macros.h>
DESCARTES_IGNORE_WARNINGS_PUSH
#include <boost/graph/dijkstra_shortest_paths.hpp>
DESCARTES_IGNORE_WARNINGS_POP

namespace descartes_light
{
template <typename FloatType>
SearchResult<FloatType> BGLDijkstraSVSESolver<FloatType>::search()
{
  // Convenience aliases
  auto& graph_ = BGLSolverBase<FloatType>::graph_;
  const auto& source_ = BGLSolverBase<FloatType>::source_;
  auto& predecessors_ = BGLSolverBase<FloatType>::predecessors_;
  const auto& ladder_rungs_ = BGLSolverBase<FloatType>::ladder_rungs_;

  // Internal properties
  auto index_prop_map = boost::get(boost::vertex_index, graph_);
  auto weight_prop_map = boost::get(boost::edge_weight, graph_);
  auto color_prop_map = boost::get(&Vertex<FloatType>::color, graph_);
  auto distance_prop_map = boost::get(&Vertex<FloatType>::distance, graph_);

  typedef typename boost::property_map<BGLGraph<FloatType>, boost::vertex_index_t>::type IndexMap;
  typedef boost::iterator_property_map<typename std::vector<VertexDesc<FloatType>>::iterator, IndexMap> PredecessorMap;
  predecessors_.resize(boost::num_vertices(graph_), std::numeric_limits<std::size_t>::max());
  PredecessorMap predecessor_it_map = boost::make_iterator_property_map(predecessors_.begin(), index_prop_map);

  // Perform the search
  boost::dijkstra_shortest_paths(graph_,
                                 source_,
                                 predecessor_it_map,
                                 distance_prop_map,
                                 weight_prop_map,
                                 index_prop_map,
                                 std::less<>(),
                                 std::plus<>(),
                                 std::numeric_limits<FloatType>::max(),
                                 static_cast<FloatType>(0.0),
                                 boost::default_dijkstra_visitor(),
                                 color_prop_map);

  // Find lowest cost node in last rung
  auto target = std::min_element(ladder_rungs_.back().begin(),
                                 ladder_rungs_.back().end(),
                                 [&](const VertexDesc<FloatType>& a, const VertexDesc<FloatType>& b) {
                                   return graph_[a].distance < graph_[b].distance;
                                 });

  SearchResult<FloatType> result;

  // Reconstruct the path from the predecesor map; remove the artificial start state
  const auto vd_path = BGLSolverBase<FloatType>::reconstructPath(source_, *target);
  result.trajectory = BGLSolverBase<FloatType>::toStates(vd_path);
  result.trajectory.erase(result.trajectory.begin());

  result.cost = graph_[*target].distance;

  return result;
}

template <typename FloatType>
SearchResult<FloatType> BGLEfficientDijkstraSVSESolver<FloatType>::search()
{
  // Convenience aliases
  auto& graph_ = BGLSolverBase<FloatType>::graph_;
  const auto& source_ = BGLSolverBase<FloatType>::source_;
  auto& predecessors_ = BGLSolverBase<FloatType>::predecessors_;
  const auto& ladder_rungs_ = BGLSolverBase<FloatType>::ladder_rungs_;

  // Internal properties
  auto index_prop_map = boost::get(boost::vertex_index, graph_);
  auto weight_prop_map = boost::get(boost::edge_weight, graph_);
  auto color_prop_map = boost::get(&Vertex<FloatType>::color, graph_);
  auto distance_prop_map = boost::get(&Vertex<FloatType>::distance, graph_);

  typedef typename boost::property_map<BGLGraph<FloatType>, boost::vertex_index_t>::type IndexMap;
  typedef boost::iterator_property_map<typename std::vector<VertexDesc<FloatType>>::iterator, IndexMap> PredecessorMap;
  predecessors_.resize(boost::num_vertices(graph_), std::numeric_limits<std::size_t>::max());
  PredecessorMap predecessor_it_map = boost::make_iterator_property_map(predecessors_.begin(), index_prop_map);

  const long last_rung_idx = static_cast<long>(ladder_rungs_.size() - 1);
  auto visitor = boost::make_dijkstra_visitor(early_terminator<FloatType>(last_rung_idx));

  // Perform the search
  try
  {
    boost::dijkstra_shortest_paths(graph_,
                                   source_,
                                   predecessor_it_map,
                                   distance_prop_map,
                                   weight_prop_map,
                                   index_prop_map,
                                   std::less<>(),
                                   std::plus<>(),
                                   std::numeric_limits<FloatType>::max(),
                                   static_cast<FloatType>(0.0),
                                   visitor,
                                   color_prop_map);
  }
  catch (const VertexDesc<FloatType>& target)
  {
    SearchResult<FloatType> result;

    // Reconstruct the path from the predecesor map; remove the artificial start state
    const auto vd_path = BGLSolverBase<FloatType>::reconstructPath(source_, target);
    result.trajectory = BGLSolverBase<FloatType>::toStates(vd_path);
    result.trajectory.erase(result.trajectory.begin());

    result.cost = graph_[target].distance;

    return result;
  }

  // If the visitor never threw the vertex descriptor, there was an issue with the search
  throw std::runtime_error("Search failed to encounter vertex associated with the last waypoint in the trajectory");
}

}  // namespace descartes_light

#endif  // DESCARTES_LIGHT_SOLVERS_BGL_IMPL_BGL_DIJKSTRA_SOLVER_HPP


// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

    This is an example illustrating the use of the perspective_window tool
    in the dlib C++ Library.  It is a simple tool for displaying 3D point
    clouds on the screen.

*/

#include <dlib/gui_widgets.h>
#include <dlib/image_transforms.h>
#include <cmath>

using namespace dlib;
using namespace std;

// ----------------------------------------------------------------------------------------

int main()
{
    // Let's make a point cloud that looks like a 3D spiral.
    std::vector<perspective_window::overlay_dot> points;
    dlib::rand rnd;
    for (double i = 0; i < 20; i+=0.001)
    {
        // Get a point on a spiral
        dlib::vector<double> val(sin(i),cos(i),i/4);

        // Now add some random noise to it
        dlib::vector<double> temp(rnd.get_random_gaussian(),
                                  rnd.get_random_gaussian(),
                                  rnd.get_random_gaussian());
        val += temp/20;

        // Pick a color based on how far we are along the spiral
        rgb_pixel color = colormap_jet(i,0,20);

        // And add the point to the list of points we will display
        points.push_back(perspective_window::overlay_dot(val, color));
    }

    // Now finally display the point cloud.
    perspective_window win;
    win.set_title("perspective_window 3D point cloud");
    win.add_overlay(points);
    win.wait_until_closed();
}

//  ----------------------------------------------------------------------------


#include "graph.h"
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <list>
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

Graph::Graph(int V) : g(V){
}

// Method to print connected components in an
// undirected graph
void Graph::connectedComponents(cv::Mat &Connected, int NVertex) {
    Mapping mappings;
    int n = boost::connected_components(g, boost::make_assoc_property_map(mappings));

    Connected = cv::Mat::zeros(n, NVertex, CV_8U);

    for (Component c = 0; c<n; ++c) {

        for (auto& mapping : mappings)
            if (mapping.second == c){
                Connected.at<uchar>(c,mapping.first) = 1;
            }
    }
}

void Graph::addEdge(int v, int w) {
    boost::add_edge(v, w, g);
}


#ifndef MATHEVAL_IMPLEMENTATION
#error "Do not include ast_adapted.hpp directly!"
#endif

#pragma once

#include "ast.hpp"

#include <boost/fusion/include/adapt_struct.hpp>

typedef double (*unary_fn)(double);
typedef double (*binary_fn)(double, double);

BOOST_FUSION_ADAPT_STRUCT(matheval::ast::unary_op,
                          (unary_fn, op)(matheval::ast::operand, rhs))

BOOST_FUSION_ADAPT_STRUCT(matheval::ast::binary_op,
                          (binary_fn, op)(matheval::ast::operand,
                                          lhs)(matheval::ast::operand, rhs))

BOOST_FUSION_ADAPT_STRUCT(matheval::ast::operation,
                          (binary_fn, op)(matheval::ast::operand, rhs))

BOOST_FUSION_ADAPT_STRUCT(matheval::ast::expression,
                          (matheval::ast::operand,
                           lhs)(std::list<matheval::ast::operation>, rhs))


#ifndef CX_DQMC_HPP
#define CX_DQMC_HPP
#include <boost/mpi.hpp>

#include <libdqmc/la.hpp>
#include <libdqmc/cx_dqmc_greens_general.hpp>
#include <libdqmc/cx_dqmc_greens_general_renyi.hpp>
#include <libdqmc/global.hpp>
#include <libdqmc/prefixed_out.hpp>
#include <libdqmc/cx_updates.hpp>
#include <libdqmc/tools.hpp>
#include <libdqmc/temper_package.hpp>
// #include <libdqmc/cx_dqmc_hopping.hpp>
#include <libdqmc/cx_density.hpp>
#include <libdqmc/cx_checkerboard.hpp>
// #include <libdqmc/cx_checkerboard_manual.hpp>
// #include <libdqmc/cx_measurements.hpp>
#include <complex>
// ALPS INCLUDES
#include <alps/lattice.h>
#include <alps/parapack/worker.h>
#include <alps/alea.h>
#include <alps/osiris.h>
#include <alps/osiris/dump.h>
#include <alps/expression.h>

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

// Standard includes
#include <mpi.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <cstdlib>
#include <list>
#include <vector>
#include <ctime>
#include <climits>
#include <limits>
#include <cmath>
#include <numeric>
#include <map>
#include <exception>

#include <boost/multi_array.hpp>
#include <boost/filesystem.hpp>

using namespace std;

class cx_dqmc_worker : public alps::parapack::mc_worker {

public:

    ///@{
    ~cx_dqmc_worker() { }

    cx_dqmc_worker(const alps::Parameters &);
    cx_dqmc_worker(boost::mpi::communicator const& comm, const alps::Parameters &);

    void start(const alps::Parameters& params);
    double work_done(alps::ObservableSet& obs);
    bool is_thermalized() const { return steps.is_thermalized(); } ;
    void init_observables(alps::Parameters const &, alps::ObservableSet& obs);
    void run(alps::ObservableSet& obs);
    double progress() const { return steps.progress(); } ;

    alps::mc_steps steps;
    dqmc::parameters cx_dqmc_params;

    cx_dqmc::workspace * ws;
    cx_dqmc::workspace * sqr_ws;
    cx_dqmc::workspace * r_ws;

    temper_package outgoing, incoming;

    /// The auxiliary spin arrays
    boost::multi_array<double, 3 > aux_spins;
    boost::multi_array<double, 3 > aux_spins_0;
    boost::multi_array<double, 3 > aux_spins_1;
    boost::multi_array<double, 3 > aux_spins_temp;

    int particles;
    int rank;
    double gamma;
    double T;
    double beta;
    double delta_tau;
    int slices;
    double hopping, hopping_perp;
    double interaction;
    int sweeps_measurement;

    double lambda;
    double gamma_table[2];

    // int bonds;
    int slice_steps;
    int start_slice;
    int end_slice;
    int volume;

    uint connected_0, connected_1, disconnected_0, disconnected_1;
    uint n_A_0, n_A_1, n_B_0, n_B_1;
    int volume_0, volume_1;
    int max_flips;
    bool first_run;
    cx_dqmc::greens_general * gs_0_0;
    cx_dqmc::greens_general * gs_0_1;
    cx_dqmc::greens_general_renyi * gs_1;

    int state;
    double log_det_ratio;
    pdouble_t det_up_0, det_down_0, det_up_1, det_down_1;
    pdouble_t det_up_0_0, det_down_0_0, det_up_0_1, det_down_0_1;
    pdouble_t last_det_0, last_det_1;
    cx_double sign_0_0, avg_sign_0_0;
    cx_double sign_0_1, avg_sign_0_1;
    cx_double sign_0, avg_sign_0;
    cx_double sign_1, avg_sign_1;

    pdouble_t log_ratio, ratio;
    pdouble_t local_moment, double_occupancy;
    int meas_counter;
    bool flipped;
    int test_runs;
    double alpha;
    double alpha_0;
    double alpha_1;

    bool state_0_done;
    bool state_1_done;

    bool grover;

    void switch_state(alps::ObservableSet& obs);
    bool gs_sweep(alps::ObservableSet& obs);
    bool gs_sweep_continuously(alps::ObservableSet& obs);
    double gs_delayed_spinful_flip(alps::ObservableSet& obs, int slice);
    double gs_delayed_continuous_spinful_flip(alps::ObservableSet& obs, int slice, vector<double>& switch_probs);
    double gs_simple_spinless_flip(alps::ObservableSet& obs, int slice);
    double gs_delayed_spinless_flip(alps::ObservableSet& obs, int slice);
    int gs_spinful_naive_flip(alps::ObservableSet& obs, int slice);
    bool package_ok;
    void TiePackage();
    void ComputeRatio();
    void Decide();
    void HandlePackage();

    void measure(alps::ObservableSet& obs);

    void randomize_spins() {
	for (uint s = 0; s < cx_dqmc_params.slices; s++) {
	    for (int i = 0; i < cx_dqmc_params.num_aux_spins; i++) {
		uniform_01() > 0.5 ?
		    aux_spins[0][s][i] = -1:
		    aux_spins[0][s][i] = 1;
		aux_spins_0[0][s][i] = aux_spins[0][s][i];
		aux_spins_1[0][s][i] = aux_spins[0][s][i];

		aux_spins[1][s][i] = aux_spins[0][s][i];;
		aux_spins_0[1][s][i] = aux_spins[1][s][i];
		aux_spins_1[1][s][i] = aux_spins[1][s][i];

	    }
	}
    };

    void ordered_spins() {
	for (uint s = 0; s < cx_dqmc_params.slices; s++) {
	    for (int i = 0; i < cx_dqmc_params.num_aux_spins; i++) {
		i % 2 == 0 ?
		    aux_spins[0][s][i] = -1:
		    aux_spins[0][s][i] = 1;

		aux_spins_0[0][s][i] = aux_spins[0][s][i];
		aux_spins_1[0][s][i] = aux_spins[0][s][i];

		aux_spins[1][s][i] = aux_spins[0][s][i];;
		aux_spins_0[1][s][i] = aux_spins[1][s][i];
		aux_spins_1[1][s][i] = aux_spins[1][s][i];

	    }
	}
    };

    void copy_aux_spins(boost::multi_array<double,3>& in,
			boost::multi_array<double, 3>& out) {
	for (int r = 0; r < 2; r++) {
	    for (int s = 0; s < cx_dqmc_params.slices; s++) {
		for (int l = 0; l < cx_dqmc_params.num_aux_spins; l++) {
		    out[r][s][l] = in[r][s][l];
		}
	    }
	}
    }

    std::string TimeString() {
	using namespace std;

	time_t timestamp;
	tm *now;
	timestamp = time(0);
	now = localtime(&timestamp);

	stringstream time_string;

	time_string << "[" << now->tm_year + 1900 << "-";
	if (now->tm_mon + 1 < 10) time_string << "0";
	time_string << now->tm_mon+1 << "-";
	if (now->tm_mday < 10) time_string << "0";
	time_string << now->tm_mday << " ";
	if (now->tm_hour < 10) time_string << "0";
	time_string << now->tm_hour << ":";
	if (now->tm_min < 10) time_string << "0";
	time_string << now->tm_min << ":";
	if (now->tm_sec < 10) time_string << "0";
	time_string << now->tm_sec << "]   ";

	return time_string.str();
    }

    void load(alps::IDump& dump) {
	dump >> steps >> state;

	for (int r = 0; r < 2; r++) {
	    for (int s = 0; s < cx_dqmc_params.slices; s++) {
		for (int l = 0; l < cx_dqmc_params.num_aux_spins; l++) {
		    dump >> aux_spins_0[r][s][l];
		    dump >> aux_spins_1[r][s][l];
		}
	    }
	}

	if (state == 0) {
	    for (int a = 0; a < 2; a++) {
		for (uint s = 0; s < cx_dqmc_params.slices; s++) {
		    for (int i = 0; i < cx_dqmc_params.num_aux_spins; i++) {
			aux_spins[a][s][i] = aux_spins_0[a][s][i];
		    }
		}
	    }
	} else {
	    for (int a = 0; a < 2; a++) {
		for (uint s = 0; s < cx_dqmc_params.slices; s++) {
		    for (int i = 0; i < cx_dqmc_params.num_aux_spins; i++) {
			aux_spins[a][s][i] = aux_spins_1[a][s][i];
		    }
		}
	    }
	}

	out << "Monte Carlo Steps " << steps() << endl;

	gs_0_0->build_stack();
	gs_0_1->build_stack();
	gs_1->build_stack();
    }

    void save(alps::ODump& dump) const {
	dump << steps << state;

	for (int r = 0; r < 2; r++) {
	    for (int s = 0; s < cx_dqmc_params.slices; s++) {
		for (int l = 0; l < cx_dqmc_params.num_aux_spins; l++) {
		    dump << aux_spins_0[r][s][l];
		    dump << aux_spins_1[r][s][l];
		}
	    }
	}
    }

    void calculate_weights() {
	if (state == 0) {
	    gs_0_0->build_stack();
	    gs_0_1->build_stack();

	    det_up_0_0 = gs_0_0->get_log_det();
	    det_up_0_1 = gs_0_1->get_log_det();

	    if (cx_dqmc_params.update_type == cx_dqmc_params.CX_SPINFUL_UPDATE) {
		det_up_0 = 2 * (det_up_0_0 + det_up_0_1);
	    } else {
		det_up_0 = (det_up_0_0 + det_up_0_1);
	    }
	} else {
	    gs_1->build_stack();

	    if (cx_dqmc_params.update_type == cx_dqmc_params.CX_SPINFUL_UPDATE) {
		det_up_1 = 2 * gs_1->get_log_det();
	    } else {
		det_up_1 =  gs_1->get_log_det();
	    }
	}
    }

    boost::mpi::communicator world;
    PrefixedOut out;
    PrefixedOut err;
};

#endif


#include <stdio.h>
#include <cfloat>
#include <list>
#include <vector>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <boost/algorithm/clamp.hpp>
#include <random>
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "unsupported/Eigen/CXX11/src/Tensor/TensorDeviceCuda.h"
//#include "unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/util/work_sharder.h"
#include "bboxes.h"
#include "matcher.h"
#include "wtoolkit.h"
#include "wtoolkit_cuda.h"
#include "bboxes_encode.h"
#include <future>

using namespace tensorflow;
using namespace std;

typedef Eigen::ThreadPoolDevice CPUDevice;
typedef Eigen::GpuDevice GPUDevice;

/*
 * max_overlap_as_pos:是否将与ground truth bbox交叉面积最大的box设置为正样本
 * force_in_gtbox: 是否强制正样本的中心点必须在相应的gtbox内
 * bottom_boxes:[1,X,4]/[batch_size,X,4](ymin,xmin,ymax,xmax) 候选box,相对坐标
 * bottom_gboxes:[batch_size,Y,4](ymin,xmin,ymax,xmax)ground truth box相对坐标
 * bottom_glabels:[batch_size,Y] 0为背景
 * bottom_glength:[batch_size] 为每一个batch中gboxes的有效数量
 * output_labels:[batch_size,X], 当前anchorbox的标签，背景为0,不为背景时为相应最大jaccard得分,-1表示忽略
 * output_scores:[batch_size,X], 当前anchorbox与groundtruthbox的jaccard得分，当jaccard得分高于threshold时就不为背影
 * output_indict:[batch_size,X], 当anchorbox有效时，与它对应的gboxes(从0开始)序号,无效时为-1
 */
REGISTER_OP("Matcher")
    .Attr("T: {float,double,int32,int64}")
	.Attr("pos_threshold:float")
	.Attr("neg_threshold:float")
    .Attr("max_overlap_as_pos:bool")
    .Attr("force_in_gtbox:bool=False")
    .Input("bottom_boxes: T")
    .Input("bottom_gboxes: T")
    .Input("bottom_glabels: int32")
    .Input("bottom_glength: int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("indict:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
            const auto input_shape0 = c->input(0);
            const auto input_shape1 = c->input(1);
            const auto batch_size = c->Dim(input_shape1,0);
            const auto boxes_nr  = c->Dim(input_shape0,1);
            auto shape = c->MakeShape({batch_size,boxes_nr});

            for(auto i=0; i<3; ++i)
			    c->set_output(i, shape);
			return Status::OK();
			});

template <typename Device, typename T>
class MatcherOp: public OpKernel {
};
template <typename T>
class MatcherOp<CPUDevice,T>: public OpKernel {
	public:
		explicit MatcherOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES_OK(context, context->GetAttr("force_in_gtbox", &force_in_gtbox_));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("Matcher");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			const Tensor &_bottom_gsize   = context->input(3);
			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,2>();
			auto          bottom_gsize    = _bottom_gsize.template tensor<int,1>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gsize.dims() == 1, errors::InvalidArgument("gsize data must be 1-dimensional"));

			int           dims_2d[2]            = {int(batch_size),int(data_nr)};
			TensorShape   outshape1;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_indict         = NULL;

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_indict));

			auto output_labels_tensor         =  output_labels->template tensor<int,2>();
			auto output_scores_tensor         =  output_scores->template tensor<T,2>();
			auto output_indict_tensor         =  output_indict->template tensor<int,2>();

            MatcherUnit<CPUDevice,T> encode_unit(pos_threshold,neg_threshold,max_overlap_as_pos_,force_in_gtbox_);
            auto shard = [&](int64 start,int64 limit){
                for(auto i=start; i<limit; ++i) {

                    auto size     = bottom_gsize(i);
                    auto boxes    = bottom_boxes.chip(bottom_boxes.dimension(0)==batch_size?i:0,0);
                    auto _gboxes  = bottom_gboxes.chip(i,0);
                    auto _glabels = bottom_glabels.chip(i,0);
                    Eigen::array<long,2> offset={0,0};
                    Eigen::array<long,2> extents={size,4};
                    Eigen::array<long,1> offset1={0};
                    Eigen::array<long,1> extents1={size};
                    auto gboxes             = _gboxes.slice(offset,extents);
                    auto glabels            = _glabels.slice(offset1,extents1);
                    auto out_labels         = output_labels_tensor.chip(i,0);
                    auto out_scores         = output_scores_tensor.chip(i,0);
                    auto out_indices        = output_indict_tensor.chip(i,0);
                    auto res                = encode_unit(boxes,gboxes,glabels);

                    out_labels          =  std::get<0>(res);
                    out_scores          =  std::get<1>(res);
                    out_indices         =  std::get<2>(res);
                }
            };
            list<future<void>> results;
            for(auto i=0; i<batch_size; ++i) {
                results.emplace_back(async(launch::async,[i,&shard](){ shard(i,i+1);}));
            }
        }
	private:
		float pos_threshold;
		float neg_threshold;
		bool  max_overlap_as_pos_ = true;
		bool  force_in_gtbox_     = false;
};
#ifdef GOOGLE_CUDA
template <typename T>
class MatcherOp<GPUDevice,T>: public OpKernel {
	public:
		explicit MatcherOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES_OK(context, context->GetAttr("force_in_gtbox", &force_in_gtbox_));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("MatcherGPU");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			const Tensor &_bottom_gsize   = context->input(3);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gsize.dims() == 1, errors::InvalidArgument("gsize data must be 1-dimensional"));

			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,2>();
			auto          d_bottom_gsize  = _bottom_gsize.template tensor<int,1>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);
		    Eigen::Tensor<int,1,Eigen::RowMajor> bottom_gsize;
            assign_tensor<CPUDevice,GPUDevice>(bottom_gsize,d_bottom_gsize);

			int           dims_2d[2]            = {int(batch_size),int(data_nr)};
			TensorShape   outshape1;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_indict         = NULL;

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_indict));

			auto output_labels_tensor         =  output_labels->template tensor<int,2>();
			auto output_scores_tensor         =  output_scores->template tensor<T,2>();
			auto output_indict_tensor         =  output_indict->template tensor<int,2>();

            MatcherUnit<GPUDevice,T> encode_unit(pos_threshold,neg_threshold,max_overlap_as_pos_,force_in_gtbox_);

            for(auto i=0; i<batch_size; ++i) {
                    auto size    = bottom_gsize(i);
                    auto boxes   = bottom_boxes.dimension(0)==batch_size?chip_data(bottom_boxes,i):chip_data(bottom_boxes,0);
                    auto gboxes  = chip_data(bottom_gboxes,i);
                    auto glabels = chip_data(bottom_glabels,i);
                    encode_unit(boxes,gboxes,glabels,
                            chip_data(output_labels_tensor,i),
                            chip_data(output_scores_tensor,i),
                            chip_data(output_indict_tensor,i),
                            size,bottom_boxes.dimension(1)
                            );
                }
        }
	private:
		float pos_threshold;
		float neg_threshold;
		bool  max_overlap_as_pos_ = true;
		bool  force_in_gtbox_     = false;
};
#endif
REGISTER_KERNEL_BUILDER(Name("Matcher").Device(DEVICE_CPU).TypeConstraint<float>("T"), MatcherOp<CPUDevice, float>);
#ifdef GOOGLE_CUDA
//REGISTER_KERNEL_BUILDER(Name("Matcher").Device(DEVICE_GPU).TypeConstraint<float>("T"), MatcherOp<GPUDevice, float>);
#endif
/*
 * 将boxes中与gboxes IOU最大且不小于threadshold的标记为相应的label, 即1个gboxes最多与一个boxes相对应
 * boxes:[batch_size,N,4](y,x,h,w)
 * gboxes:[batch_size,M,4]
 * glabels:[batch_size,M] 0表示背景
 * glens:[batch_size] 用于表明gboxes中的有效boxes数量
 * output_labels:[batch_size,N]
 * output_scores:[batch_size,N]
 * output_indexs:[batch_size,N] #gbboxes的index
 * threashold: IOU threshold
 */
REGISTER_OP("BoxesMatch")
    .Attr("T: {float, double,int32}")
	.Attr("threshold:float")
    .Input("boxes: T")
    .Input("gboxes: T")
	.Input("glabels:int32")
	.Input("glens:int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("output_indexs:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
		    auto boxes_shape = c->input(0);
			shape_inference::ShapeHandle outshape;
			c->Subshape(boxes_shape,0,2,&outshape);
			c->set_output(0, outshape);
			c->set_output(1, outshape);
			c->set_output(2, outshape);
			return Status::OK();
			});

template <typename Device, typename T>
class BoxesMatchOp: public OpKernel {
	public:
		explicit BoxesMatchOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold_));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &_boxes   = context->input(0);
			const Tensor &_gboxes  = context->input(1);
			const Tensor &_glabels = context->input(2);
			const Tensor &_glens   = context->input(3);
			auto          boxes    = _boxes.tensor<T,3>();
			auto          gboxes   = _gboxes.tensor<T,3>();
			auto          glabels  = _glabels.tensor<int,2>();
			auto          glens    = _glens.tensor<int,1>();

			OP_REQUIRES(context, _boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _gboxes.dims() == 3, errors::InvalidArgument("gboxes data must be 3-dimensional"));
			OP_REQUIRES(context, _glabels.dims() == 2, errors::InvalidArgument("glabels data must be 2-dimensional"));
			OP_REQUIRES(context, _glens.dims() == 1, errors::InvalidArgument("glens data must be 1-dimensional"));

			const int batch_nr  = _boxes.dim_size(0);
			const int boxes_nr  = _boxes.dim_size(1);
			const int gboxes_nr = _gboxes.dim_size(1);

			int dims_2d[2] = {batch_nr,boxes_nr};
			TensorShape  outshape;
			Tensor      *output_classes     = NULL;
			Tensor      *output_scores      = NULL;
			Tensor      *output_indexs      = NULL;

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape, &output_classes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape, &output_indexs));

			auto oclasses     = output_classes->template tensor<int,2>();
			auto oscores      = output_scores->template tensor<T,2>();
			auto oindexs      = output_indexs->template tensor<int,2>();

            oclasses.setZero();
            oscores.setZero();
            oindexs.setConstant(-1);

            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;

                    for(auto k=0; k<boxes_nr; ++k) {
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);
                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oindexs(i,index) = j;
                    }
                }
            }
		}
	private:
		float threshold_;
};
REGISTER_KERNEL_BUILDER(Name("BoxesMatch").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesMatchOp<CPUDevice, float>);

/*
 * 将boxes先与pred_bboxes匹配（通过pred_labels指定）如果IOU大于指定的阀值则为预测正确，没有与pred_bboxes匹配的bboxes与gboxes匹配，
 * IOU最大且不小于threadshold的标记为相应的label, 即1个gboxes最多与一个boxes相对应
 * boxes:[batch_size,N,4](y,x,h,w)
 * plabels:[batch_size,N]预测的类别
 * gboxes:[batch_size,M,4]
 * glabels:[batch_size,M] 0表示背景
 * glens:[batch_size] 用于表明gboxes中的有效boxes数量
 * output_labels:[batch_size,N]
 * output_scores:[batch_size,N]
 * output_indexs:[batch_size,N] #gbboxes的index
 * threashold: IOU threshold
 */
REGISTER_OP("BoxesMatchWithPred")
    .Attr("T: {float, double,int32}")
	.Attr("threshold:float")
	.Attr("is_binary_plabels:bool=False")
    .Input("boxes: T")
    .Input("plabels: int32")
    .Input("pprobs: T")
    .Input("gboxes: T")
	.Input("glabels:int32")
	.Input("glens:int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("output_indexs:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
		    auto boxes_shape = c->input(0);
			shape_inference::ShapeHandle outshape;
			c->Subshape(boxes_shape,0,2,&outshape);
			c->set_output(0, outshape);
			c->set_output(1, outshape);
			c->set_output(2, outshape);
			return Status::OK();
			});

template <typename Device, typename T>
class BoxesMatchWithPredOp: public OpKernel {
    private:
        struct PInfo {
            int index;
            float probs;
        };
	public:
		explicit BoxesMatchWithPredOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold_));
			OP_REQUIRES_OK(context, context->GetAttr("is_binary_plabels", &is_binary_plabels_));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &_boxes   = context->input(0);
			const Tensor &_plabels = context->input(1);
			const Tensor &_pprobs = context->input(2);
			const Tensor &_gboxes  = context->input(3);
			const Tensor &_glabels = context->input(4);
			const Tensor &_glens   = context->input(5);

			OP_REQUIRES(context, _boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _gboxes.dims() == 3, errors::InvalidArgument("gboxes data must be 3-dimensional"));
			OP_REQUIRES(context, _glabels.dims() == 2, errors::InvalidArgument("glabels data must be 2-dimensional"));
			OP_REQUIRES(context, _plabels.dims() == 2, errors::InvalidArgument("plabels data must be 2-dimensional"));
			OP_REQUIRES(context, _pprobs.dims() == 2, errors::InvalidArgument("pprobs data must be 2-dimensional"));
			OP_REQUIRES(context, _glens.dims() == 1, errors::InvalidArgument("glens data must be 1-dimensional"));

			auto          boxes    = _boxes.tensor<T,3>();
			auto          plabels  = _plabels.tensor<int,2>();
			auto          pprobs   = _pprobs.tensor<T,2>();
			auto          gboxes   = _gboxes.tensor<T,3>();
			auto          glabels  = _glabels.tensor<int,2>();
			auto          glens    = _glens.tensor<int,1>();

			const int batch_nr  = _boxes.dim_size(0);
			const int boxes_nr  = _boxes.dim_size(1);
			const int gboxes_nr = _gboxes.dim_size(1);
            Eigen::Tensor<bool,2> process_mask(batch_nr,glabels.dimension(1));
			int dims_2d[2] = {batch_nr,boxes_nr};
			TensorShape  outshape;
			Tensor      *output_classes     = NULL;
			Tensor      *output_scores      = NULL;
			Tensor      *output_indexs      = NULL;

            if(plabels.dimension(1) != boxes.dimension(1)) {
                cout<<"Error plabels dimension 1"<<endl;
            }

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape, &output_classes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape, &output_indexs));

			auto oclasses     = output_classes->template tensor<int,2>();
			auto oscores      = output_scores->template tensor<T,2>();
			auto oindexs      = output_indexs->template tensor<int,2>();

            process_mask.setZero();
            oclasses.setZero();
            oscores.setZero();
            oindexs.setConstant(-1);

            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;
                    vector<PInfo> infos(boxes_nr);
                    for(auto k=0; k<boxes_nr; ++k) {
                        infos[k].index = k;
                        infos[k].probs = pprobs(i,k);
                    }
                    sort(infos.begin(),infos.end(),[](const auto& lhv,const auto& rhv){ return rhv.probs<lhv.probs;});

                    for(auto _k=0; _k<boxes_nr; ++_k) {
                        const auto k = infos[_k].index;
                        const auto plabel = plabels(i,k);
                        if((is_binary_plabels_ && (plabel == 0)) || ((!is_binary_plabels_) && (plabel != glabels(i,j))))
                            continue;
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);
                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oindexs(i,index) = j;
                        process_mask(i,j) = true;
                    }
                }
            }
            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;
                    if(process_mask(i,j)) continue;

                    for(auto k=0; k<boxes_nr; ++k) {
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);
                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oindexs(i,index) = j;
                    }
                }
            }
		}
	private:
		float threshold_;
        bool is_binary_plabels_ = false;
};
REGISTER_KERNEL_BUILDER(Name("BoxesMatchWithPred").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesMatchWithPredOp<CPUDevice, float>);
/*
 * 将boxes先与pred_bboxes匹配（通过pred_labels指定）如果IOU大于指定的阀值则为预测正确，没有与pred_bboxes匹配的bboxes与gboxes匹配，
 * 与上面的版本相比会多输出一个boxex encode
 * IOU最大且不小于threadshold的标记为相应的label, 即1个gboxes最多与一个boxes相对应
 * boxes:[batch_size,N,4](y,x,h,w)
 * plabels:[batch_size,N]预测的类别
 * gboxes:[batch_size,M,4]
 * glabels:[batch_size,M] 0表示背景
 * glens:[batch_size] 用于表明gboxes中的有效boxes数量
 * output_labels:[batch_size,N]
 * output_scores:[batch_size,N]
 * threashold: IOU threshold
 */
REGISTER_OP("BoxesMatchWithPred2")
    .Attr("T: {float, double,int32}")
	.Attr("threshold:float")
 	.Attr("prio_scaling: list(float)")
    .Input("boxes: T")
    .Input("plabels: int32")
    .Input("gboxes: T")
	.Input("glabels:int32")
	.Input("glens:int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("output_encode:T")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
		    auto boxes_shape = c->input(0);
			shape_inference::ShapeHandle outshape;
			c->Subshape(boxes_shape,0,2,&outshape);
			c->set_output(0, outshape);
			c->set_output(1, outshape);
			c->set_output(2, boxes_shape);
			return Status::OK();
			});

template <typename Device, typename T>
class BoxesMatchWithPred2Op: public OpKernel {
	public:
		explicit BoxesMatchWithPred2Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold_));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &_boxes   = context->input(0);
			const Tensor &_plabels = context->input(1);
			const Tensor &_gboxes  = context->input(2);
			const Tensor &_glabels = context->input(3);
			const Tensor &_glens   = context->input(4);
			auto          boxes    = _boxes.tensor<T,3>();
			auto          plabels  = _plabels.tensor<int,2>();
			auto          gboxes   = _gboxes.tensor<T,3>();
			auto          glabels  = _glabels.tensor<int,2>();
			auto          glens    = _glens.tensor<int,1>();

			OP_REQUIRES(context, _boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _gboxes.dims() == 3, errors::InvalidArgument("gboxes data must be 3-dimensional"));
			OP_REQUIRES(context, _glabels.dims() == 2, errors::InvalidArgument("glabels data must be 2-dimensional"));
			OP_REQUIRES(context, _plabels.dims() == 2, errors::InvalidArgument("plabels data must be 2-dimensional"));
			OP_REQUIRES(context, _glens.dims() == 1, errors::InvalidArgument("glens data must be 1-dimensional"));

			const int batch_nr  = _boxes.dim_size(0);
			const int boxes_nr  = _boxes.dim_size(1);
			const int gboxes_nr = _gboxes.dim_size(1);
            Eigen::Tensor<bool,2> process_mask(batch_nr,glabels.dimension(1));
			int dims_2d[2] = {batch_nr,boxes_nr};
			int dims_3d[3] = {batch_nr,boxes_nr,4};
			TensorShape  outshape;
			TensorShape  outshape1;
			Tensor      *output_classes     = NULL;
			Tensor      *output_scores      = NULL;
			Tensor      *output_encode      = NULL;

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape);
			TensorShapeUtils::MakeShape(dims_3d, 3, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape, &output_classes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_encode));

			auto oclasses     = output_classes->template tensor<int,2>();
			auto oscores      = output_scores->template tensor<T,2>();
			auto oencodes     = output_encode->template tensor<T,3>();

            process_mask.setZero();
            oclasses.setZero();
            oscores.setZero();
            oencodes.setZero();

            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;
                    Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);

                    for(auto k=0; k<boxes_nr; ++k) {
                        const auto plabel = plabels(i,k);
                        if(plabel != glabels(i,j)) continue;
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oencodes.chip(i,0).chip(index,0) = encode_one_boxes<T>(gbox,boxes.chip(i,0).chip(index,0),prio_scaling);
                        process_mask(i,j) = true;
                    }
                }
            }
            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;
                    if(process_mask(i,j)) continue;
                    Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);

                    for(auto k=0; k<boxes_nr; ++k) {
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oencodes.chip(i,0).chip(index,0) = encode_one_boxes<T>(gbox,boxes.chip(i,0).chip(index,0),prio_scaling);
                    }
                }
            }
		}
	private:
		float threshold_;
		vector<float> prio_scaling;
};
REGISTER_KERNEL_BUILDER(Name("BoxesMatchWithPred2").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesMatchWithPred2Op<CPUDevice, float>);

/*
 * 将boxes先与pred_bboxes匹配（通过pred_labels指定）如果IOU大于指定的阀值则为预测正确，没有与pred_bboxes匹配的bboxes与gboxes匹配，
 * IOU最大且不小于threadshold的标记为相应的label, 即1个gboxes最多与一个boxes相对应
 * boxes:[batch_size,N,4](y,x,h,w)
 * plabels:[batch_size,N]预测的类别
 * gboxes:[batch_size,M,4]
 * glabels:[batch_size,M] 0表示背景
 * glens:[batch_size] 用于表明gboxes中的有效boxes数量
 * output_labels:[batch_size,N]
 * output_scores:[batch_size,N]
 * output_indexs:[batch_size,N] #gbboxes的index
 * threashold: IOU threshold
 */
REGISTER_OP("BoxesMatchWithPredV3")
    .Attr("T: {float, double,int32}")
	.Attr("threshold:float")
	.Attr("is_binary_plabels:bool=False")
	.Attr("sort_by_probs:bool=True")
    .Input("boxes: T")
    .Input("plabels: int32")
    .Input("pprobs: T")
    .Input("gboxes: T")
	.Input("glabels:int32")
	.Input("glens:int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("output_indexs:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
		    auto boxes_shape = c->input(0);
			shape_inference::ShapeHandle outshape;
			c->Subshape(boxes_shape,0,2,&outshape);
			c->set_output(0, outshape);
			c->set_output(1, outshape);
			c->set_output(2, outshape);
			return Status::OK();
			});

template <typename Device, typename T>
class BoxesMatchWithPredV3Op: public OpKernel {
    private:
        struct PInfo {
            int index;
            float probs;
        };
	public:
		explicit BoxesMatchWithPredV3Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold_));
			OP_REQUIRES_OK(context, context->GetAttr("is_binary_plabels", &is_binary_plabels_));
			OP_REQUIRES_OK(context, context->GetAttr("sort_by_probs", &sort_by_probs_));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &_boxes   = context->input(0);
			const Tensor &_plabels = context->input(1);
			const Tensor &_pprobs = context->input(2);
			const Tensor &_gboxes  = context->input(3);
			const Tensor &_glabels = context->input(4);
			const Tensor &_glens   = context->input(5);

			OP_REQUIRES(context, _boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _gboxes.dims() == 3, errors::InvalidArgument("gboxes data must be 3-dimensional"));
			OP_REQUIRES(context, _glabels.dims() == 2, errors::InvalidArgument("glabels data must be 2-dimensional"));
			OP_REQUIRES(context, _plabels.dims() == 2, errors::InvalidArgument("plabels data must be 2-dimensional"));
			OP_REQUIRES(context, _pprobs.dims() == 2, errors::InvalidArgument("pprobs data must be 2-dimensional"));
			OP_REQUIRES(context, _glens.dims() == 1, errors::InvalidArgument("glens data must be 1-dimensional"));

			auto          boxes    = _boxes.tensor<T,3>();
			auto          plabels  = _plabels.tensor<int,2>();
			auto          pprobs   = _pprobs.tensor<T,2>();
			auto          gboxes   = _gboxes.tensor<T,3>();
			auto          glabels  = _glabels.tensor<int,2>();
			auto          glens    = _glens.tensor<int,1>();

			const int batch_nr  = _boxes.dim_size(0);
			const int boxes_nr  = _boxes.dim_size(1);
			const int gboxes_nr = _gboxes.dim_size(1);
			int dims_2d[2] = {batch_nr,boxes_nr};
			TensorShape  outshape;
			Tensor      *output_classes     = NULL;
			Tensor      *output_scores      = NULL;
			Tensor      *output_indexs      = NULL;

            if(plabels.dimension(1) != boxes.dimension(1)) {
                cout<<"Error plabels dimension 1"<<endl;
            }

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape, &output_classes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape, &output_indexs));

			auto oclasses     = output_classes->template tensor<int,2>();
			auto oscores      = output_scores->template tensor<T,2>();
			auto oindexs      = output_indexs->template tensor<int,2>();

            oclasses.setZero();
            oscores.setZero();
            oindexs.setConstant(-1);

            for(auto i=0; i<batch_nr; ++i) {
                for(auto j=0; j<glens(i); ++j) {
                    auto max_scores = -1.0;
                    auto index      = -1;
                    vector<PInfo> infos(boxes_nr);
                    for(auto k=0; k<boxes_nr; ++k) {
                        infos[k].index = k;
                        infos[k].probs = pprobs(i,k);
                    }
                    if(sort_by_probs_)
                        sort(infos.begin(),infos.end(),[](const auto& lhv,const auto& rhv){ return rhv.probs<lhv.probs;});

                    for(auto _k=0; _k<boxes_nr; ++_k) {
                        const auto k = infos[_k].index;
                        const auto plabel = plabels(i,k);
                        if((is_binary_plabels_ && (plabel == 0)) || ((!is_binary_plabels_) && (plabel != glabels(i,j))))
                            continue;
                        if(oclasses(i,k) != 0) continue;

                        Eigen::Tensor<T,1,Eigen::RowMajor> gbox = gboxes.chip(i,0).chip(j,0);
                        Eigen::Tensor<T,1,Eigen::RowMajor> box = boxes.chip(i,0).chip(k,0);
                        auto jaccard = bboxes_jaccardv1(gbox,box);

                        if((jaccard>threshold_) && (jaccard>max_scores)) {
                            max_scores = jaccard;
                            index = k;
                        }
                    }
                    if(index>=0) {
                        oclasses(i,index) = glabels(i,j);
                        oscores(i,index) = max_scores;
                        oindexs(i,index) = j;
                    }
                }
            }
		}
	private:
		float threshold_;
        bool is_binary_plabels_ = false;
        bool sort_by_probs_ = false;
};
REGISTER_KERNEL_BUILDER(Name("BoxesMatchWithPredV3").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesMatchWithPredV3Op<CPUDevice, float>);
/*
 * 如果boxes与gboxes的iou>threshold[0], 并且交叉面积占gboxes的面积大小threshold[1]的为正样本，其余的为负样本
 * bottom_boxes:[1,X,4]/[batch_size,X,4](ymin,xmin,ymax,xmax) 候选box,相对坐标
 * bottom_gboxes:[batch_size,Y,4](ymin,xmin,ymax,xmax)ground truth box相对坐标
 * bottom_glabels:[batch_size,Y] 0为背景
 * bottom_glength:[batch_size] 为每一个batch中gboxes的有效数量
 * output_labels:[batch_size,X], 当前anchorbox的标签，背景为0,不为背景时为相应最大jaccard得分,-1表示忽略
 * output_scores:[batch_size,X], 当前anchorbox与groundtruthbox的jaccard得分，当jaccard得分高于threshold时就不为背影
 * output_indict:[batch_size,X], 当anchorbox有效时，与它对应的gboxes(从0开始)序号,无效时为-1
 */
REGISTER_OP("MatcherV2")
    .Attr("T: {float,double,int32,int64}")
	.Attr("threshold:list(float)")
    .Input("bottom_boxes: T")
    .Input("bottom_gboxes: T")
    .Input("bottom_glabels: int32")
    .Input("bottom_glength: int32")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("indict:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
            const auto input_shape0 = c->input(0);
            const auto input_shape1 = c->input(1);
            const auto batch_size = c->Dim(input_shape1,0);
            const auto boxes_nr  = c->Dim(input_shape0,1);
            auto shape = c->MakeShape({batch_size,boxes_nr});

            for(auto i=0; i<3; ++i)
			    c->set_output(i, shape);
			return Status::OK();
			});

template <typename Device, typename T>
class MatcherV2Op: public OpKernel {
};
template <typename T>
class MatcherV2Op<CPUDevice,T>: public OpKernel {
	public:
		explicit MatcherV2Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold_));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("MatcherV2");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			const Tensor &_bottom_gsize   = context->input(3);
			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,2>();
			auto          bottom_gsize    = _bottom_gsize.template tensor<int,1>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gsize.dims() == 1, errors::InvalidArgument("gsize data must be 1-dimensional"));

			int           dims_2d[2]            = {int(batch_size),int(data_nr)};
			TensorShape   outshape1;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_indict         = NULL;

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_indict));

			auto output_labels_tensor         =  output_labels->template tensor<int,2>();
			auto output_scores_tensor         =  output_scores->template tensor<T,2>();
			auto output_indict_tensor         =  output_indict->template tensor<int,2>();

            MatcherV2Unit<CPUDevice,T> encode_unit(threshold_);
            auto shard = [&](int64 start,int64 limit){
                for(auto i=start; i<limit; ++i) {

                    auto size     = bottom_gsize(i);
                    auto boxes    = bottom_boxes.chip(bottom_boxes.dimension(0)==batch_size?i:0,0);
                    auto _gboxes  = bottom_gboxes.chip(i,0);
                    auto _glabels = bottom_glabels.chip(i,0);
                    Eigen::array<long,2> offset={0,0};
                    Eigen::array<long,2> extents={size,4};
                    Eigen::array<long,1> offset1={0};
                    Eigen::array<long,1> extents1={size};
                    auto gboxes             = _gboxes.slice(offset,extents);
                    auto glabels            = _glabels.slice(offset1,extents1);
                    auto out_labels         = output_labels_tensor.chip(i,0);
                    auto out_scores         = output_scores_tensor.chip(i,0);
                    auto out_indices        = output_indict_tensor.chip(i,0);
                    auto res                = encode_unit(boxes,gboxes,glabels);

                    out_labels          =  std::get<0>(res);
                    out_scores          =  std::get<1>(res);
                    out_indices         =  std::get<2>(res);
                }
            };
            list<future<void>> results;
            for(auto i=0; i<batch_size; ++i) {
                results.emplace_back(async(launch::async,[i,&shard](){ shard(i,i+1);}));
            }
        }
	private:
		vector<float> threshold_;
};
REGISTER_KERNEL_BUILDER(Name("MatcherV2").Device(DEVICE_CPU).TypeConstraint<float>("T"), MatcherV2Op<CPUDevice, float>);


#pragma once

#include <functional>
#include <mdw/formula/ArenaAllocator.hpp>
#include <mdw/formula/Expression.hpp>
#include <mdw/formula/IContext.hpp>
#include <mdw/formula/cache/KnownType.hpp>
#include <mdw/Tracer.hpp>
#include <boost/unordered_map.hpp>

namespace mdw { namespace formula {

  template <class OutputType> class UnaryCachedByAddress
  {
  public:
    class Expression: public TypedExpression<OutputType>
    {
      typedef typename TypeTraits<OutputType>::ReturnType ReturnType;
      typedef typename __TypeTraits<ReturnType>::cached_type CachedType;
      typedef boost::unordered_map<const void*, std::pair<bool, CachedType> > Cache;
    public:

      Expression(ExpressionType iType,
                 const TypedExpression<OutputType>& iChild,
                 const FactByAddress& iFact):
        TypedExpression<OutputType>(iType), _child(iChild), _fact(iFact), _latestContextId(-1)
      {}

      ReturnType evaluate(IContext& ioContext) const
      {
        if (_isWorthCaching && !ioContext.isNaN())
        {
          if (_latestContextId != ioContext.getUniqueId())
          {
            FORMULA_DEBUG("Need to clean up cache due to new IContext");
            _cache.clear();
            _latestContextId = ioContext.getUniqueId();
          }
          const void *aFact = NULL;
          try {
            aFact = _fact.compute(ioContext);
            if (ioContext.isNaN())
            {
              ioContext.ignoreNaN();
              FORMULA_DEBUG("Missing fact while computing fact: "
                      << _fact.getExpression().toString());
              return _child.evaluate(ioContext);
            }
          } catch (...) {
            FORMULA_DEBUG("Exception while computing fact: " << _fact.getExpression().toString());
            return _child.evaluate(ioContext);
          }
          typename Cache::iterator anIt = _cache.find(aFact);
          if (anIt != _cache.end())
          {
            if (anIt->second.first)
            {
              ioContext.setNaN();
            }
            //FORMULA_DEBUG("Used cached value for " << _child.toString());
            return __TypeTraits<ReturnType>::FromCached(anIt->second.second);
          } else {
            ReturnType aResult = _child.evaluate(ioContext);
            //FORMULA_DEBUG("New cached value for " << _child.toString());
            _cache.insert(std::make_pair(aFact, std::make_pair(ioContext.isNaN(),
                                                              __TypeTraits<ReturnType>::ToCached(aResult))));
            return aResult;
          }
        } else {
          FORMULA_DEBUG("Not cached or invalid context for " << _child.toString());
          return _child.evaluate(ioContext);
        }
      }

      size_t complexity() const {
        if (_isWorthCaching)
        {
          return _fact.getExpression().complexity() + 2;
        } else {
          return _child.complexity() + 1;
        }
      }

      /*void enableCache(bool iCache)
      {
        _isWorthCaching = iCache;
      }*/

      std::string toString() const {
        return _child.toString();
      }

      const TypedExpression<OutputType>& getChild() const
      {
        return _child;
      }

    private:
      const TypedExpression<OutputType>& _child;
      const FactByAddress& _fact;
      static const bool _isWorthCaching = true; // Not sure how to set it yet

      mutable Cache _cache;
      mutable int _latestContextId;
    };
  };

}}


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

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

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

#ifndef BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_MIDPOINTS_HPP
#define BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_MIDPOINTS_HPP

// Renamed from "intermediate" to "midpoints"

#include <cstddef>
#include <iterator>

#include <boost/range.hpp>

#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace midpoints {

template <typename Src, typename Dst, std::size_t Dimension, std::size_t DimensionCount>
struct calculate_coordinate
{
    static inline void apply(Src const& p1, Src const& p2, Dst& p)
    {
        geometry::set<Dimension>(p,
                    (geometry::get<Dimension>(p1) + geometry::get<Dimension>(p2)) / 2.0);
        calculate_coordinate<Src, Dst, Dimension + 1, DimensionCount>::apply(p1, p2, p);
    }
};

template <typename Src, typename Dst, std::size_t DimensionCount>
struct calculate_coordinate<Src, Dst, DimensionCount, DimensionCount>
{
    static inline void apply(Src const&, Src const&, Dst&)
    {
    }
};

template<typename Range, typename Iterator>
struct range_midpoints
{
    static inline void apply(Range const& range,
            bool start_and_end, Iterator out)
    {
        typedef typename point_type<Range>::type point_type;
        typedef typename boost::range_iterator<Range const>::type iterator_type;

        iterator_type it = boost::begin(range);

        if (start_and_end)
        {
            *out++ = *it;
        }

        iterator_type prev = it++;
        for (; it != boost::end(range); prev = it++)
        {
            point_type p;
            calculate_coordinate
                <
                    point_type,
                    point_type,
                    0,
                    dimension<point_type>::type::value
                >::apply(*prev, *it, p);
            *out++ = p;
        }

        if (start_and_end)
        {
            *out++ = *prev;
        }
    }
};

}} // namespace detail::midpoints
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template <typename GeometryTag, typename G, typename Iterator>
struct midpoints  {};

template <typename G, typename Iterator>
struct midpoints<ring_tag, G, Iterator>
        : detail::midpoints::range_midpoints<G, Iterator> {};

template <typename G, typename Iterator>
struct midpoints<linestring_tag, G, Iterator>
        : detail::midpoints::range_midpoints<G, Iterator> {};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

/*!
    \brief Calculate midpoints of a geometry
    \ingroup midpoints
 */
template<typename Geometry, typename Iterator>
inline void midpoints(Geometry const& geometry,
        bool start_and_end, Iterator out)
{
    concepts::check<Geometry const>();

    dispatch::midpoints
        <
            typename tag<Geometry>::type,
            Geometry,
            Iterator
        >::apply(geometry, start_and_end, out);
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_EXTENSIONS_ALGORITHMS_MIDPOINTS_HPP


#define BOOST_TEST_MODULE pcraster dal space_step_coordinate_mapper
#include <boost/test/unit_test.hpp>
#include "dal_DataSpace.h"
#include "dal_SpaceStepCoordinateMapper.h"

BOOST_AUTO_TEST_CASE(test)
{
  using namespace dal;

  {
    SpaceStepCoordinateMapper mapper(1, 74.85, -0.30);

    std::vector<size_t> rows;
    rows.push_back(1);
    rows.push_back(250);
    rows.push_back(1);

    DataSpace space;
    space.addDimension(Dimension(Space, rows));
    DataSpaceAddress address = space.address();

    address.setCoordinate<size_t>(0, 1);
    BOOST_CHECK_EQUAL(mapper.toString(space, address, 0),  "74.85");
  }
}


//
// Created by foxfire on 2018-05-05.
//

#ifndef ASTEROIDS5_SPACE_OBJECT_HPP
#define ASTEROIDS5_SPACE_OBJECT_HPP

#define _USE_MATH_DEFINES
#include <Eigen/Core>

/**
 * @class space_object
 * @breif The base class of all space objects
 */
class space_object
{
public:
	space_object(){}
	virtual ~space_object(){}

	/**
	 * Object position
	 */
	Eigen::Vector2f pos;
	/**
	 * Object velocity
	 */
	Eigen::Vector2f vel;
	/**
	 * The circle radius used for collision detection
	 */
	float radius;
};

#endif //ASTEROIDS5_SPACE_OBJECT_HPP