#include <iostream>
#include <cmath>

#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>

#include "lattice.h"
#include "stringlib.h"
#include "filelib.h"
#include "ttables.h"
#include "tdict.h"
#include "em_utils.h"

namespace po = boost::program_options;
using namespace std;

bool InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("iterations,i",po::value<unsigned>()->default_value(5),"Number of iterations of EM training")
        ("beam_threshold,t",po::value<double>()->default_value(-4),"log_10 of beam threshold (-10000 to include everything, 0 max)")
        ("no_null_word,N","Do not generate from the null token")
        ("variational_bayes,v","Add a symmetric Dirichlet prior and infer VB estimate of weights")
        ("alpha,a", po::value<double>()->default_value(0.01), "Hyperparameter for optional Dirichlet prior")
        ("no_add_viterbi,V","Do not add Viterbi alignment points (may generate a grammar where some training sentence pairs are unreachable)");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help,h", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (argc < 2 || conf->count("help")) {
    cerr << "Usage " << argv[0] << " [OPTIONS] corpus.fr-en\n";
    cerr << dcmdline_options << endl;
    return false;
  }
  return true;
}

int main(int argc, char** argv) {
  po::variables_map conf;
  if (!InitCommandLine(argc, argv, &conf)) return 1;
  const string fname = argv[argc - 1];
  const int ITERATIONS = conf["iterations"].as<unsigned>();
  const double BEAM_THRESHOLD = pow(10.0, conf["beam_threshold"].as<double>());
  const bool use_null = (conf.count("no_null_word") == 0);
  const WordID kNULL = TD::Convert("<eps>");
  const bool add_viterbi = (conf.count("no_add_viterbi") == 0);
  const bool variational_bayes = (conf.count("variational_bayes") > 0);
  const double alpha = conf["alpha"].as<double>();
  if (variational_bayes && alpha <= 0.0) {
    cerr << "--alpha must be > 0\n";
    return 1;
  }

  TTable tt;
  TTable::Word2Word2Double was_viterbi;
  for (int iter = 0; iter < ITERATIONS; ++iter) {
    const bool final_iteration = (iter == (ITERATIONS - 1));
    cerr << "ITERATION " << (iter + 1) << (final_iteration ? " (FINAL)" : "") << endl;
    ReadFile rf(fname);
    istream& in = *rf.stream();
    double likelihood = 0;
    double denom = 0.0;
    int lc = 0;
    bool flag = false;
    string line;
    while(true) {
      getline(in, line);
      if (!in) break;
      ++lc;
      if (lc % 1000 == 0) { cerr << '.'; flag = true; }
      if (lc %50000 == 0) { cerr << " [" << lc << "]\n" << flush; flag = false; }
      string ssrc, strg;
      ParseTranslatorInput(line, &ssrc, &strg);
      Lattice src, trg;
      LatticeTools::ConvertTextToLattice(ssrc, &src);
      LatticeTools::ConvertTextToLattice(strg, &trg);
      if (src.size() == 0 || trg.size() == 0) {
        cerr << "Error: " << lc << "\n" << line << endl;
        assert(src.size() > 0);
        assert(trg.size() > 0);
      }
      denom += trg.size();
      vector<double> probs(src.size() + 1);
      const double src_logprob = -log(src.size() + 1);
      for (int j = 0; j < trg.size(); ++j) {
        const WordID& f_j = trg[j][0].label;
        double sum = 0;
        if (use_null) {
          probs[0] = tt.prob(kNULL, f_j);
          sum += probs[0];
        }
        for (int i = 1; i <= src.size(); ++i) {
          probs[i] = tt.prob(src[i-1][0].label, f_j);
          sum += probs[i];
        }
        if (final_iteration) {
          if (add_viterbi) {
            WordID max_i = 0;
            double max_p = -1;
            if (use_null) {
              max_i = kNULL;
              max_p = probs[0];
            }
            for (int i = 1; i <= src.size(); ++i) {
              if (probs[i] > max_p) {
                max_p = probs[i];
                max_i = src[i-1][0].label;
              }
            }
            was_viterbi[max_i][f_j] = 1.0;
          }
        } else {
          if (use_null)
            tt.Increment(kNULL, f_j, probs[0] / sum);
          for (int i = 1; i <= src.size(); ++i)
            tt.Increment(src[i-1][0].label, f_j, probs[i] / sum);
        }
        likelihood += log(sum) + src_logprob;
      }
    }

    // log(e) = 1.0
    double base2_likelihood = likelihood / log(2);

    if (flag) { cerr << endl; }
    cerr << "  log_e likelihood: " << likelihood << endl;
    cerr << "  log_2 likelihood: " << base2_likelihood << endl;
    cerr << "   cross entropy: " << (-base2_likelihood / denom) << endl;
    cerr << "      perplexity: " << pow(2.0, -base2_likelihood / denom) << endl;
    if (!final_iteration) {
      if (variational_bayes)
        tt.NormalizeVB(alpha);
      else
        tt.Normalize();
    }
  }
  for (TTable::Word2Word2Double::iterator ei = tt.ttable.begin(); ei != tt.ttable.end(); ++ei) {
    const TTable::Word2Double& cpd = ei->second;
    const TTable::Word2Double& vit = was_viterbi[ei->first];
    const string& esym = TD::Convert(ei->first);
    double max_p = -1;
    for (TTable::Word2Double::const_iterator fi = cpd.begin(); fi != cpd.end(); ++fi)
      if (fi->second > max_p) max_p = fi->second;
    const double threshold = max_p * BEAM_THRESHOLD;
    for (TTable::Word2Double::const_iterator fi = cpd.begin(); fi != cpd.end(); ++fi) {
      if (fi->second > threshold || (vit.count(fi->first) > 0)) {
        cout << esym << ' ' << TD::Convert(fi->first) << ' ' << log(fi->second) << endl;
      }
    }
  }
  return 0;
}


/*=============================================================================================================
                                                IntrogressionSimulations.cpp
===============================================================================================================

 Simulations of genetic rescue

 C++-code accompanying:

		(ms. in prep).

 Written by:
        F.J.H. de Haas
       	Theoretical Biology Group
        University of British Columbia
        the Netherlands

 Program version
		xx/xx/xxxx	:

=============================================================================================================*/

#include <boost/dynamic_bitset.hpp>
#include <assert.h>
#include <stdlib.h>
#include <ctime>
#include <iomanip>
#include <chrono>
#include <string>
#include "random.h"
#include "IntrogressionSimulations.h"

class Individual{
  public:
    Individual(const boost::dynamic_bitset<> &INITGENOME) : genome(INITGENOME) {;}

    Individual(Individual* parent1, Individual* parent2, const Parameters &pars){
        Individual* parent[2] = {parent1, parent2};
        const int NLOCI = parent[0]->genome.size();
        genome.resize(NLOCI);

        // Recombination & Mutation
        boost::dynamic_bitset<> temp1, temp2, temp3, r_localbit, m_localbit;
        r_localbit.resize(NLOCI);
        m_localbit.resize(NLOCI);

        Make_localbit(r_localbit, pars.r_global);
        Make_localbit(m_localbit, pars.m_global);

        temp1 = parent[0]->genome & r_localbit;
        temp2 = parent[1]->genome & r_localbit.flip();
        temp3 = temp1 | temp2;

        temp1 = temp3 & m_localbit;
        temp2 = temp3 | m_localbit;
        genome = temp2 & temp1.flip();
    }

    inline bool rescue(const Parameters &pars){if(genome[pars.index[0]]==1){return true;} else{return false;} ;}

    bool Genotype(const int &locus) {return genome[locus]; }

    int GenotypeCount() {return genome.count();}

    private:
    void Make_localbit(boost::dynamic_bitset<> &local, const boost::dynamic_bitset<> &global)
    {
        const int NLOCI = genome.size();
        const int global_size = global.size();

        assert(local.size() == NLOCI);
        assert(global_size > NLOCI);
        int start = rnd::integer(global_size - NLOCI);

        for (int i = 0; i < NLOCI; ++i)
        {
            local[i] = global[start + i];
        }
    };

    boost::dynamic_bitset<> genome;
};

void WriteToDataBlock(std::vector<Individual*> &population, const Parameters &pars, DataBlock* &SimData){
    // Popsize
    const int populationsize = population.size();
    SimData->popsize.push_back(populationsize);
    std::vector<int> count[2];
    count[0].resize(pars.NLOCI,0);
    count[1].resize(pars.NLOCI,0);
    int temp0 = 0, temp1 = 0, ind0 = 0, ind1 = 0;
    for(Individual* ind : population){
        if(ind->rescue(pars)){
            ++ind1;
            temp1 += ind->GenotypeCount() - 1;
        }
        else{
            ++ind0;
            temp0 += ind->GenotypeCount();
        }
        // Genome allele frequencies
        for(int j = 0; j < pars.NLOCI; ++j){
            ++count[ind->Genotype(j)][j];
        }
    }

    SimData->allele0.push_back(count[0]);
    SimData->allele1.push_back(count[1]);
    SimData->major0.push_back(ind0);
    SimData->major1.push_back(ind1);
    SimData->introgressed0.push_back((double)temp0/(double)((pars.NLOCI-1)*ind0));
    SimData->introgressed1.push_back((double)temp1/(double)((pars.NLOCI-1)*ind1));
}

bool ItteratePopulation(std::vector<Individual*> &population, const Parameters &pars){
    std::vector<Individual*>::iterator it;

    // Birth
    const int nparents = population.size();
    const double popgrowthrate = 1.0 + (pars.BIRTHRATE-1.0) * (1.0 - ((double)nparents / (double)pars.K));
    assert(popgrowthrate >= 0.0);
    const int noffspring = rnd::poisson((double)nparents * popgrowthrate);
    std::vector<Individual*> offspring(noffspring);
    std::vector<Individual*> offspring2;

    // Random mating
    for(it = offspring.begin(); it != offspring.end(); ++it){
        *it = new Individual(population[rnd::integer(nparents)],population[rnd::integer(nparents)],pars);
    }

    // Death (selection)
    bool rescue = false;
    for(it = offspring.begin(); it != offspring.end(); ++it){
        if((*it)->rescue(pars)) {
            rescue = true;
            if(rnd::uniform() < pars.DEATHRATEA){
                offspring2.push_back(*it);
            }
        }
        else{
            if(rnd::uniform() < pars.DEATHRATEa){
                offspring2.push_back(*it);
            }
        }
    }

    // Cleanup
    for(Individual* ind : population) delete ind;
    population.clear();
    population = offspring2;

    return rescue;
}

bool RunSimulation(const Parameters &SimPars, SimData &SimulationData){

    // Set local parameters
    DataBlock* SimData = new DataBlock;

    // Initialize population
    std::vector<Individual*> population(SimPars.NINIT[0]+SimPars.NINIT[1]);
    for(int i = 0; i < SimPars.NINIT[0]; ++i){
        population[i] = new Individual(SimPars.INIT_GENOME[0]);
    }
    for(int i = SimPars.NINIT[0]; i < SimPars.NINIT[0]+SimPars.NINIT[1];++i){
        population[i] = new Individual(SimPars.INIT_GENOME[1]);
    }

    // Run simulation
    WriteToDataBlock(population, SimPars, SimData);

    for (int i = 0; i < SimPars.NGEN; ++i){
        if(ItteratePopulation(population, SimPars)==false){
            for (Individual* i: population) delete i;
            delete SimData;
            ++SimulationData.nofixcounter;
            return false;
            }
        else{
            WriteToDataBlock(population, SimPars, SimData);
            }
    }

    // Cleanup and Write DataBlock
    for (Individual* i: population) delete i;
    SimulationData.push_back_protect(SimData);
    return true;
}

Parameters::Parameters(int argc, char *argv[]){
    MUTATIONRATE = 0.0;
    BIRTHRATE = std::atof(argv[1]);
    DEATHRATEA = std::atof(argv[2]);
    DEATHRATEa = std::atof(argv[3]);
    NLOCI = std::atoi(argv[4]);
    NINIT[0] = std::atoi(argv[5]);
    NINIT[1] = std::atoi(argv[6]);
    NGEN = std::atoi(argv[7]);
    NREP = std::atoi(argv[8]);
    RECOMBINATIONRATE = std::atof(argv[9]);
    K = std::atoi(argv[10]);

    Initialize();
}

Parameters::Parameters(const Rcpp::List &parslist){
    MUTATIONRATE = 0.0;
    BIRTHRATE = parslist["b"];
    DEATHRATEA = parslist["dA"];
    DEATHRATEa = parslist["da"];
    NLOCI = parslist["nloci"];
    NINIT[0] = parslist["ninit0"];
    NINIT[1] = parslist["ninit1"];
    NGEN = parslist["ngen"];
    NREP = parslist["nrep"];
    RECOMBINATIONRATE = parslist["rec"];
    K = parslist["k"];

    Initialize();
}

void Parameters::Initialize(){

    //Genetic architecture of selection
    index.clear();
    index.resize(NLOCI);
    index[0] = std::floor((double)NLOCI/2.0);

    std::vector<int> index;
    boost::dynamic_bitset<> INITGENOME0(NLOCI);
    boost::dynamic_bitset<> INITGENOME1(NLOCI);
    INITGENOME1.set();
    INIT_GENOME[0] = INITGENOME0;
    INIT_GENOME[1] = INITGENOME1;

    //Recombination and mutation
    const int GLOBALMAX = 100000;
    r_global.resize(GLOBALMAX);
    boost::dynamic_bitset<> a(1);
    a[0] = rnd::bernoulli(0.5);
    for (int i = 0; i < GLOBALMAX; ++i)
    {
        if (rnd::bernoulli(RECOMBINATIONRATE) == true)
        a.flip();
        r_global[i] = a[0];
    }
    m_global.resize(GLOBALMAX);
    for (int i = 0; i < GLOBALMAX; ++i)
    {
        m_global[i] = rnd::bernoulli(MUTATIONRATE);
    }
}

/*
double AdditiveFitness(const Parameters &pars){
    double viability = 1.0;
    for(int i = 0; i < pars.NPLOIDY; ++i){
        for(int j = 0; j < pars.NLOCAL_ADAPTED_LOCI+1; ++j){
            if(genome[i][pars.index[j]] == true){viability += pars.SC_GENOME[pars.index[j]];}
        }
    }
    return viability;
}
*/


#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/with_custodian_and_ward.hpp>

#include <smtbx/refinement/least_squares.h>
#include <smtbx/refinement/weighting_schemes.h>

namespace smtbx { namespace refinement { namespace least_squares {
  namespace boost_python {

  template <typename FloatType,
    class OneMillerIndexLinearisation>
  struct wrapper {
    template <class ObjectType,
      class NormalEquations,
      template<typename> class WeightingSchemeType>
    static void def_init_(boost::python::class_<ObjectType> &klass) {
      using namespace boost::python;
      klass.def(
        init<
        NormalEquations &, // normal_equations
        cctbx::xray::observations<FloatType> const &, // reflections
        af::const_ref<std::complex<FloatType> > const &, // f_mask
        WeightingSchemeType<FloatType> const &, // weighting_scheme
        boost::optional<FloatType>, // scale_factor
        OneMillerIndexLinearisation &, // f_calc_function
        scitbx::sparse::matrix<FloatType> const &,
        // jacobian_transpose_matching_grad_fc
        cctbx::xray::extinction_correction<FloatType> const &, // exti
        // objective_only=false, may_parallelise=false, use_openpm=false
        optional<bool, bool, bool>
        >((arg("normal_equations"), arg("reflections"), arg("f_mask"),
          arg("weighting_scheme"), arg("scale_factor"),
          arg("f_calc_function"), arg("jacobian_transpose_matching_grad_fc"),
          arg("extinction"), arg("objective_only") = false,
          arg("may_parallelise") = false, arg("use_openmp") = false)));
    }

    template <class ObjectType>
    static void wrap_init(char const *name,
      boost::python::class_<ObjectType> &klass)
    {
      typedef
        lstbx::normal_equations::non_linear_ls_with_separable_scale_factor<
        FloatType,
        scitbx::matrix::sum_of_symmetric_rank_1_updates>
        NormalEquations_BLAS2;
      def_init_<ObjectType, NormalEquations_BLAS2, mainstream_shelx_weighting>(klass);
      def_init_<ObjectType, NormalEquations_BLAS2, unit_weighting            >(klass);
      def_init_<ObjectType, NormalEquations_BLAS2, sigma_weighting           >(klass);

      typedef
        lstbx::normal_equations::non_linear_ls_with_separable_scale_factor<
        FloatType,
        scitbx::matrix::rank_n_update>
        NormalEquations_BLAS3;
      def_init_<ObjectType, NormalEquations_BLAS3, mainstream_shelx_weighting>(klass);
      def_init_<ObjectType, NormalEquations_BLAS3, unit_weighting            >(klass);
      def_init_<ObjectType, NormalEquations_BLAS3, sigma_weighting           >(klass);
    }

    struct normal_equation_building {
      typedef build_normal_equations<FloatType> wt;

      static void wrap(char const *name) {
        using namespace boost::python;
        class_<wt> klass(name, no_init);
        wrap_init<wt>(name, klass);
        klass
          .def("observables", &wt::observables)
          .def("f_calc", &wt::f_calc)
          .def("weights", &wt::weights)
          .add_static_property("available_threads",
            &wt::get_available_threads,
            &wt::set_available_threads)
          .def("hasOpenMP", &wt::has_openmp)
            ;
      }
    };

    struct design_matrix_building {
      typedef build_design_matrix<FloatType> wt;

      static void wrap(char const *name) {
        using namespace boost::python;
        class_<wt> klass(name, no_init);
        wrap_init<wt>(name, klass);
        klass
          .def("observables", &wt::observables)
          .def("f_calc", &wt::f_calc)
          .def("weights", &wt::weights)
          .add_static_property("available_threads",
            &wt::get_available_threads,
            &wt::set_available_threads)
          .def("hasOpenMP", &wt::has_openmp)
          .def("design_matrix", &wt::design_matrix)
          ;
      }
    };
  };

  void wrap_least_squares() {
    using namespace boost::python;
    typedef wrapper<double,
      structure_factors::direct::one_h::std_trigonometry<
        double,
        structure_factors::direct::one_h::modulus_squared>
    > wrapper_t;
    wrapper_t::normal_equation_building::wrap("build_normal_equations");
    wrapper_t::design_matrix_building::wrap("build_design_matrix");
  }

}}}}


// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "base58.h"

#include "hash.h"
#include "uint256.h"
#include "util.h"
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <string>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>
#include <boost/assign/list_of.hpp>
#include "utilstrencodings.h"
#include <boost/foreach.hpp>

using namespace std;

/** All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
static const char* pszBase32Vague = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567abcdefghijklmnopqrstuvwxyz0189";
static const char* pszBase32Clear = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUVWXYZOLBG";

bool DecodeBase32(const char* psz, std::vector<unsigned char>& vch)
{
    // Skip leading spaces.
    while (*psz && isspace(*psz))
        psz++;

    const char* v = strchr(pszBase32Vague, *psz);
    if (v == NULL)
        return false;
    int c = v - pszBase32Vague;
    const char* ch = strchr(pszBase32, *(pszBase32Clear + c));
    int buffer = ch - pszBase32;
    int bufferLen = 5;

    while (*psz && !isspace(*psz)) {
        while (bufferLen < 8) {
            if (!*++psz)
                break;
            buffer <<= 5;
            const char* v = strchr(pszBase32Vague, *psz);
            if (v == NULL)
                return false;
            c = v - pszBase32Vague;
            const char* ch = strchr(pszBase32, *(pszBase32Clear + c));
            buffer += ch - pszBase32;
            bufferLen += 5;

        }
        if (!*psz)
            break;
        bufferLen -= 8;
        vch.push_back(buffer >> bufferLen);
        buffer -= buffer >> bufferLen << bufferLen;
    } // Skip trailing spaces.
    while (isspace(*psz))
        psz++;
    if (*psz != 0)
        return false;
    return true;
}

std::string ToStandardB32String(const std::string str)
{
    const char* psz = str.c_str();
    std::string strOut;
    while (*psz) {
        if (isspace(*psz)) {
            psz++;
            continue;
    }
        const char* v = strchr(pszBase32Vague, *psz);
        if (v == NULL) {
            psz++;
            continue;
        }
        int c = v - pszBase32Vague;
        strOut.push_back(*(pszBase32Clear + c));
        psz++;
    }
    return strOut;
}

std::string EncodeBase32(const unsigned char* pbegin, const unsigned char* pend)
{
    std::string str;
    int buffer = *pbegin;
    int head = buffer;
    int bufferLen = 8;
    while (pbegin != pend) {
        if (bufferLen < 5) {
            if (++pbegin != pend) {
                buffer <<= 8;
                buffer += *pbegin;
                bufferLen += 8;
            } else if (bufferLen != 0) {
                buffer <<= 5 - bufferLen;
                buffer += head >> (3 + bufferLen);
                bufferLen = 5;
            }
        }
        if (bufferLen != 0) {
            bufferLen -= 5;
            str += pszBase32[buffer >> bufferLen];
            buffer -= buffer >> bufferLen << bufferLen;
        }
    }
    return str;
}

std::string EncodeBase32(const int i)
{
    std::string str = "";
    int t = i;
    if (t == 0)
        str += pszBase32[0];
    while (t > 0) {
        str += pszBase32[ t % 32];
        t = t / 32;
}
    return std::string(str.rbegin(), str.rend());
}

int DecodeBase32ToInt(const char* psz)
{
    while (*psz && isspace(*psz))
        psz++;
    const char* v = strchr(pszBase32Vague, *psz);
    int c = v - pszBase32Vague;
    const char* ch = strchr(pszBase32, *(pszBase32Clear + c));
    int r = ch - pszBase32;
    while (*psz && !isspace(*psz)) {
        if (!*++psz)
            break;
        if ((int64_t) (r * 32) > INT_MAX || r * 32 < r) // overflow
            return -1;
        r = r * 32;
        const char* v = strchr(pszBase32Vague, *psz);
        if (v == NULL)
            return false;
        c = v - pszBase32Vague;
        const char* ch = strchr(pszBase32, *(pszBase32Clear + c));
        r += ch - pszBase32;
    } // Skip trailing spaces.
    while (isspace(*psz))
        psz++;
    if (*psz != 0)
        return -1;
    return r;
}

int DecodeBase32ToInt(const std::string& str)
{
    return DecodeBase32ToInt(str.c_str());
}

std::string EncodeBase32(const std::vector<unsigned char>& vch)
{
    return EncodeBase32(&vch[0], &vch[0] + vch.size());
}

bool DecodeBase32(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase32(str.c_str(), vchRet);
}

std::string EncodeBase32Check(const std::vector<unsigned char>& vchIn)
{
    // add 4-byte hash check to the end
    std::vector<unsigned char> vch(vchIn);
    uint256 hash = Hash(vch.begin(), vch.end());
    vch.insert(vch.end(), (unsigned char*) &hash, (unsigned char*) &hash + 4);
    return EncodeBase32(vch);
}

bool DecodeBase32Check(const char* psz, std::vector<unsigned char>& vchRet)
{
    if (!DecodeBase32(psz, vchRet) ||
        (vchRet.size() < 4)) {
        vchRet.clear();
        return false;
    }

    // re-calculate the checksum, insure it matches the included 4-byte checksum
    uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4);
    if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) {
        vchRet.clear();
        return false;
    }
    vchRet.resize(vchRet.size() - 4);
    return true;
}

bool DecodeBase32Check(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase32Check(str.c_str(), vchRet);
}

int CompareVch(const std::vector<unsigned char>& vch1, const std::vector<unsigned char>& vch2)
{
    unsigned int s1 = vch1.size();
    unsigned int s2 = vch2.size();
    for (unsigned int i = 0; i < min(s1, s2); i++) {
        if (vch1.at(i) < vch2.at(i))
            return -1;
        if (vch1.at(i) > vch2.at(i))
            return 1;
}
    if (s1 < s2)
        return -1;
    else if (s1 > s2)
        return 1;
    else
        return 0;
}

int CompareBase32(const std::string& s1, const std::string& s2)
{
    std::vector<unsigned char> vch1;
    std::vector<unsigned char> vch2;
    if (!DecodeBase32(s1, vch1))
        return -1;
    if (!DecodeBase32(s2, vch2))
        return 1;
    return CompareVch(vch1, vch2);

}

CBase32Data::CBase32Data()
{
    vchVersion.clear();
    vchData.clear();
}

void CBase32Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize)
{
    vchVersion = vchVersionIn;
    vchData.resize(nSize);
    if (!vchData.empty())
        memcpy(&vchData[0], pdata, nSize);
}

void CBase32Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend)
{
    SetData(vchVersionIn, (void*) pbegin, pend - pbegin);
}

bool CBase32Data::SetString(const char* psz, unsigned int nVersionBytes)
{
    std::vector<unsigned char> vchTemp;
    bool rc32 = DecodeBase32Check(psz, vchTemp);
    if ((!rc32) || (vchTemp.size() < nVersionBytes)) {
        vchData.clear();
        vchVersion.clear();
        return false;
    }
    vchVersion.assign(vchTemp.end() - nVersionBytes, vchTemp.end());
    vchData.resize(vchTemp.size() - nVersionBytes);
    if (!vchData.empty())
        memcpy(&vchData[0], &vchTemp[0], vchData.size());
    OPENSSL_cleanse(&vchTemp[0], vchData.size());
    return true;
}

bool CBase32Data::SetString(const std::string& str)
{
    return SetString(str.c_str());
}

std::string CBase32Data::ToString() const
{
    std::vector<unsigned char> vch = vchVersion;
    vch.insert(vch.begin(), vchData.begin(), vchData.end());
    return EncodeBase32Check(vch);
}

int CBase32Data::CompareTo(const CBase32Data& b32) const
{
    if (vchVersion < b32.vchVersion)
        return -1;
    if (vchVersion > b32.vchVersion)
        return 1;
    if (vchData < b32.vchData)
        return -1;
    if (vchData > b32.vchData)
        return 1;
    return 0;
}

namespace
{

class CBitcoinAddressVisitor : public boost::static_visitor<bool>
{
private:
    CBitcoinAddress* addr;

public:
    CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}

    bool operator()(const CPubKey& id) const { return addr->Set(id); }
    bool operator()(const CKeyID& id) const { return addr->Set(id); }
    bool operator()(const CScript& script) const { return addr->Set(script); }
    bool operator()(const CScriptID& id) const { return addr->Set(id); }
    bool operator()(const CNoDestination& no) const { return false; }
};

} // anon namespace

bool CBitcoinAddress::Set(const CKeyID& id)
{
    if(!id.IsValid())
        return false;
    std::string str;
    std::vector<unsigned char> tmp =id.GetData();
    std::vector<unsigned char>::const_iterator pc= tmp.begin();
    while (pc < tmp.end()){
            str += *pc++;
    }
    const char* sch = (const char*)str.c_str();;
    if(id.IsEvenY())
        SetData(Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_2), sch, 32);
    else
        SetData(Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_3), sch, 32);
    return true;
}

bool CBitcoinAddress::Set(const CPubKey& id)
{
    if(!id.IsValid())
        return false;
    SetData(Params().Base32Prefix((CChainParams::Base32Type) * id.begin()), id.begin() + 1, id.size() - 1);
    return true;
}

bool CBitcoinAddress::Set(const CScriptID& id)
{
    SetData(Params().Base32Prefix(CChainParams::SCRIPTHASH_ADDRESS), &id, 20);
    return true;
}

bool CBitcoinAddress::Set(const CScript& script)
{
    if (script.size() == 0)
        return false;
    SetData(Params().Base32Prefix(CChainParams::SCRIPT_ADDRESS), (void*) &script[0], script.size());
    return true;
}

bool CBitcoinAddress::Set(const CTxDestination& dest)
{
    return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
}

bool CBitcoinAddress::IsValid() const
{
    return IsValid(Params());
}

bool CBitcoinAddress::IsValid(const CChainParams& params) const
{
    bool fCorrectSize = (vchVersion == params.Base32Prefix(CChainParams::PUBKEY_ADDRESS_2) ||
            vchVersion == params.Base32Prefix(CChainParams::PUBKEY_ADDRESS_3)) ? vchData.size() == 32 : vchData.size() == 20;
    bool fKnownVersion = vchVersion == params.Base32Prefix(CChainParams::PUBKEY_ADDRESS_2) ||
            vchVersion == params.Base32Prefix(CChainParams::PUBKEY_ADDRESS_3) ||
            vchVersion == params.Base32Prefix(CChainParams::SCRIPTHASH_ADDRESS);
    bool fCorrectScript = vchVersion == params.Base32Prefix(CChainParams::SCRIPT_ADDRESS);
    return (fCorrectSize && fKnownVersion) || fCorrectScript;
}

std::string CBitcoinAddress::ToString() const
{
    return IsValid() ? CBase32Data::ToString() : "";
}

CTxDestination CBitcoinAddress::Get() const
{
    if (!IsValid())
        return CNoDestination();
    if (vchVersion == Params().Base32Prefix(CChainParams::SCRIPT_ADDRESS)) {
        std::vector<unsigned char> s;

        BOOST_FOREACH(const unsigned char & ch, vchData)
        {
            s.push_back(ch);
        }
        return CScript(s.begin(), s.end());
    }
    if (vchVersion == Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_2) || vchVersion == Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_3)) {
        std::vector<unsigned char> id(vchData.begin(), vchData.end());
        id.insert(id.begin(), vchVersion.begin(), vchVersion.end());
        return CKeyID(id);
    } else if (vchVersion == Params().Base32Prefix(CChainParams::SCRIPTHASH_ADDRESS)) {
        uint160 id;
        memcpy(&id, &vchData[0], 20);
        return CScriptID(id);
    } else
        return CNoDestination();
}

bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
{
    unsigned char chHeader = 0;
    if (vchVersion == Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_2))
        chHeader = 2;
    else if (vchVersion == Params().Base32Prefix(CChainParams::PUBKEY_ADDRESS_3))
        chHeader = 3;
    if (!IsValid() || chHeader == 0)
        return false;
    std::vector<unsigned char> id(vchData.begin(), vchData.end());
    id.insert(id.begin(), vchVersion.begin(), vchVersion.end());
    keyID = CKeyID(id);
    return true;
}

bool CBitcoinAddress::IsScript() const
{
    return IsValid() && vchVersion == Params().Base32Prefix(CChainParams::SCRIPTHASH_ADDRESS);
}

void CBitcoinSecret::SetKey(const CKey& vchSecret)
{
    assert(vchSecret.IsValid());
        SetData(Params().Base32Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size());
}

CKey CBitcoinSecret::GetKey()
{
    CKey ret;
    assert(vchData.size() == 32);
    ret.Set(vchData.begin(), vchData.begin() + 32, true); // always get compressed key
    return ret;
}

bool CBitcoinSecret::IsValid() const
{
    bool fExpectedFormat = vchData.size() == 32;
    bool fCorrectVersion = vchVersion == Params().Base32Prefix(CChainParams::SECRET_KEY);
    return fExpectedFormat && fCorrectVersion;
}

bool CBitcoinSecret::SetString(const char* pszSecret)
{
    return CBase32Data::SetString(pszSecret) && IsValid();
}

bool CBitcoinSecret::SetString(const std::string& strSecret)
{
    return SetString(strSecret.c_str());
}

bool StringToScriptPubKey(const string& str, CScript& script)
{
    CBitcoinAddress address = CBitcoinAddress(str);
    if (!address.IsValid()) {
        return false;
    }
    script = GetScriptForDestination(address.Get());
    return true;
}

bool ScriptPubKeyToString(const CScript& script, string& str)
{
    txnouttype type;
    vector<CTxDestination> addresses;
    unsigned int wRequired;
    ExtractDestinations(script, type, addresses, wRequired);
    if (addresses.size() == 0)
        return false;
    switch((int)type)
    {
        case TX_PUBKEY:
        case TX_SCRIPTHASH:
            str = CBitcoinAddress(addresses[0]).ToString();
            break;
        case TX_SCRIPT:
        case TX_MULTISIG:
            str = CBitcoinAddress(script).ToString();
            break;
        default:
            return false;
    }
    return true;
}

bool B32Equal(const std::string str1, const std::string str2)
{
    return ToStandardB32String(str1) == ToStandardB32String(str2);
}


//----------------------------------------------------------------------------
// Copyright (C) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the Server Side Public License, version 1,
// as published by the author.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// Server Side Public License for more details.
//
// You should have received a copy of the Server Side Public License
// along with this program. If not, see
// <https://github.com/NilFoundation/plugin/blob/master/LICENSE_1_0.txt>.
//----------------------------------------------------------------------------

#define BOOST_TEST_MODULE post_fallback_circuit_test

#include <boost/test/data/monomorphic.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>

#include <nil/filecoin/storage/proofs/post/fallback/circuit.hpp>

#include "../../core/merkle/generate_tree.hpp"

using namespace nil::filecoin;

BOOST_AUTO_TEST_SUITE(post_fallback_circuit_test_suite)

template<typename MerkleTreeType>
void test_fallback_post_circuit(std::size_t expected_constraints) {
    const auto rng = XorShiftRng::from_seed(crate::TEST_SEED);

    std::size_t leaves = 64 * get_base_tree_count<Tree>();
    std::size_t sector_size = leaves * NODE_SIZE;

    const auto randomness = typename MerkleTreeType::hash_type::digest_type::random(rng);
    const auto prover_id = typename MerkleTreeType::hash_type::digest_type::random(rng);

    const auto pub_params = fallback::PublicParams {
        sector_size : sector_size as u64,
        challenge_count : 20,
        challenged_nodes : 1,
    };

    std::vector<SectorId> sectors;
    auto trees = BTreeMap();

    // Construct and store an MT using a named store.
    const auto temp_dir = tempfile::tempdir();
    const auto temp_path = temp_dir.path();

    for (std::size_t i = 0; i < 5; i++) {
        sectors.push(i.into());
        auto data, tree;
        const std::tie(data, tree) = merkletree::generate_tree<Tree>(rng, leaves, Some(temp_path.to_path_buf()));
        trees.insert(i.into(), tree);
    }

    const auto candidates =
        fallback::generate_candidates::<Tree>(&pub_params, &sectors, &trees, prover_id, randomness, );

    const auto candidate = &candidates[0];
    const auto tree = trees.remove(&candidate.sector_id);
    const auto comm_r_last = tree.root();
    const auto comm_c = typename MerkleTreeType::hash_type::digest_type::random(rng);
    const auto comm_r = <typename MerkleTreeType::hash_type>::Function::hash2(&comm_c, &comm_r_last);

    const auto pub_inputs = fallback::PublicInputs {
        randomness,
        sector_id : candidate.sector_id,
        prover_id,
        comm_r,
        partial_ticket : candidate.partial_ticket,
        sector_challenge_index : 0,
    };

    const auto priv_inputs = fallback::PrivateInputs::<Tree> {
        tree,
        comm_c,
        comm_r_last,
    };

    const auto proof = ElectionPoSt::<Tree>::prove(&pub_params, &pub_inputs, &priv_inputs).expect("proving failed");

    const auto is_valid = ElectionPoSt::<Tree>::verify(&pub_params, &pub_inputs, &proof).expect("verification failed");
    BOOST_CHECK(is_valid);

    // actual circuit test

    const auto paths = proof.paths()
                    .iter()
                    .map(| p |
                         {p.iter()
                              .map(| v | {(v .0.iter().copied().map(Into::into).map(Some).collect(), Some(v .1), )})
                              .collect::<Vec<_>>()})
                    .collect();
    std::vector<_> leafs = proof.leafs().iter().map(| l | Some((*l).into())).collect();

    auto cs = TestConstraintSystem<algebra::curves::bls12<381>>();

    const auto instance = ElectionPoStCircuit::<Tree> {
        leafs,
        paths,
        comm_r : Some(comm_r.into()),
        comm_c : Some(comm_c.into()),
        comm_r_last : Some(comm_r_last.into()),
        partial_ticket : Some(candidate.partial_ticket),
        randomness : Some(randomness.into()),
        prover_id : Some(prover_id.into()),
        sector_id : Some(candidate.sector_id.into())
    };

    instance.synthesize(cs).expect("failed to synthesize circuit");

    BOOST_CHECK(cs.is_satisfied(), "constraints not satisfied");

    BOOST_ASSERT_MSG(cs.num_inputs() == 23, "wrong number of inputs");
    BOOST_ASSERT_MSG(cs.num_constraints() == expected_constraints, "wrong number of constraints");
    BOOST_CHECK_EQUAL(cs.get_input(0, "ONE"), Fr::one());

    const auto generated_inputs =
        ElectionPoStCompound::<Tree>::generate_public_inputs(&pub_inputs, &pub_params, None);
    const auto expected_inputs = cs.get_inputs();

    for (((input, label), generated_input) : expected_inputs.iter().skip(1).zip(generated_inputs.iter())) {
        BOOST_ASSERT_MSG(input == generated_input, std::string(label));
    }

    BOOST_ASSERT_MSG(generated_inputs.len() == expected_inputs.len() - 1, "inputs are not the same length");
}

BOOST_AUTO_TEST_CASE(test_fallback_post_circuit_pedersen) {
    test_fallback_post_circuit<LCTree<PedersenHasher, U8, U0, U0>>(388520);
}

BOOST_AUTO_TEST_CASE(test_fallback_post_circuit_poseidon) {
    test_fallback_post_circuit<LCTree<PoseidonHasher, U8, U0, U0>>(22940);
}

BOOST_AUTO_TEST_SUITE_END()


#include <Eigen/Dense>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <HFTrain.h>
#include <HFTest.h>
#include <boost/algorithm/string.hpp>

DEFINE_bool(train, false, "Train a forest");
DEFINE_bool(test, false, "Test image of folder");
DEFINE_bool(learn_transitions, false, "Learn transitions between leaf nodes of a forest");
DEFINE_bool(save_forest_map, false, "Save map from poses to leaf ids");
DEFINE_string(input, "", "Input file containing training vectors from patches forwarded to the Deep Net");
DEFINE_string(output, ".", "Set output folder of the forest" );
DEFINE_int32(trees, 3, "Set Number of trees. Default: 3");
DEFINE_int32(min_samples, 30, "Set minimum samples of nodes");
DEFINE_int32(tests_per_node, 30, "Random tests per node");
DEFINE_int32(thresholds_per_test, 10, "Random thresholds per test");
DEFINE_int32(threads_per_tree, 8, "Set number of CPU cores per tree. Usually above 8 there is no speedup, use the rest for parallel trees.");
DEFINE_int32(threads_for_parallel_trees, 1, "Set number of parallel trees. Total threads is threads_per_tree * threads_per_parallel_trees");
DEFINE_int32(start_tree_no, 0, "Start tree number X, i.e. treeX.dat");

DEFINE_int32(patch_size_in_voxels, -1, "Patch Size in Voxels used in Patch extraction");
DEFINE_double(voxel_size_in_m, -1, "Voxel size in meters, used in Patch extraction");

DEFINE_string(detector_options_file, "", "Filename containing the detector options. Automatically generated by the generate_scripts.sh");
DEFINE_string(output_folder, "", "Output folder to save the results of the test images");

// debugging
DEFINE_bool(show_scene, false, "Show the clustered scene with the plane removed if the option is set");
DEFINE_bool(visualize_hypotheses, false, "Show all hypotheses generated *for debugging*");

int main(int argc, char** argv)
{
    google::InitGoogleLogging(argv[0]);

    #ifndef GFLAGS_GFLAGS_H_
      namespace gflags = google;
    #endif

    gflags::ParseCommandLineFlags(&argc, &argv, true);

    if(FLAGS_train){

        CHECK_GT(FLAGS_input.size(), 0) << "No input file specified";
        CHECK_GT(FLAGS_output.size(), 0) << "No output folder specified";
        CHECK_GT(FLAGS_trees, 0) << "You should train at least one tree";
        CHECK_GT(FLAGS_min_samples, 0) << "min_samples should be greater than zero";
        CHECK_GT(FLAGS_tests_per_node, 0) << "There should be at least one test per node";
        CHECK_GT(FLAGS_thresholds_per_test, 0) << "There should be at least one threshold per test";
        CHECK_GT(FLAGS_threads_per_tree, 0) << "You should use at least one thread per tree";
        CHECK_GT(FLAGS_threads_for_parallel_trees, 0) << "You should use at least one thread for parallel trees";
        CHECK_GT(FLAGS_patch_size_in_voxels, 0) << "You should specify the Patch Size in Voxels (--patch_size_in_voxels)";
        CHECK_GT(FLAGS_voxel_size_in_m, 0) << "Shoud should specify the Voxel Size in Meters (--voxel_size_in_m)";
        HFTrain hf;
        hf.setNumTrees(FLAGS_trees);
        hf.setMinSamples(FLAGS_min_samples);
        hf.setTestsPerNode(FLAGS_tests_per_node);
        hf.setThresPerTest(FLAGS_thresholds_per_test);
        hf.setInputPatchesFilename(FLAGS_input);
        hf.setOutputFolder(FLAGS_output);
        hf.setStartTreeNo(FLAGS_start_tree_no);
        hf.setNumberOfThreads(FLAGS_threads_per_tree, FLAGS_threads_for_parallel_trees);
        hf.setPatchSizeInVoxels(FLAGS_patch_size_in_voxels);
        hf.setVoxelSizeInM(FLAGS_voxel_size_in_m);
        hf.train();
    }

    else if(FLAGS_test){

        HFTest ht;
        ht.DetectObjects();

    }

    return 0;
}


#include <vector>
#include <map>
#include <string>
#include <boost/python.hpp>

#include "SSDR.h"

SSDR::Output ssdrOutput;

double build(int numMinBones, int numMaxInfluences, int numMaxIterations, boost::python::list bindVertices, boost::python::list animVertices, int numVertices, int numFrames)
{
    SSDR::Parameter ssdrParam;
    ssdrParam.numMaxInfluences = numMaxInfluences;
    ssdrParam.numMaxIterations = numMaxIterations;
    ssdrParam.numMinBones = numMinBones;

    SSDR::Input ssdrInput;
    ssdrInput.numVertices = numVertices;
    ssdrInput.numExamples = numFrames;
    ssdrInput.bindModel.resize(ssdrInput.numVertices);
    ssdrInput.sample.resize(ssdrInput.numVertices * numFrames);
    for (int vid = 0; vid < ssdrInput.numVertices; ++vid)
    {
        ssdrInput.bindModel[vid].x = boost::python::extract<float>(bindVertices[vid * 3 + 0]);
        ssdrInput.bindModel[vid].y = boost::python::extract<float>(bindVertices[vid * 3 + 1]);
        ssdrInput.bindModel[vid].z = boost::python::extract<float>(bindVertices[vid * 3 + 2]);
    }
    for (int f = 0; f < numFrames; ++f)
    {
        for (int vid = 0; vid < ssdrInput.numVertices; ++vid)
        {
            ssdrInput.sample[f * numVertices + vid].x = boost::python::extract<float>(animVertices[(f * numVertices + vid) * 3 + 0]);
            ssdrInput.sample[f * numVertices + vid].y = boost::python::extract<float>(animVertices[(f * numVertices + vid) * 3 + 1]);
            ssdrInput.sample[f * numVertices + vid].z = boost::python::extract<float>(animVertices[(f * numVertices + vid) * 3 + 2]);
        }
    }

    double sqe = SSDR::Decompose(ssdrOutput, ssdrInput, ssdrParam);
    return std::sqrt(sqe / ssdrInput.numVertices);
}

boost::python::list getSkinningWeight()
{
    boost::python::list skinningWeight;
    for (auto it = ssdrOutput.weight.begin(); it != ssdrOutput.weight.end(); ++it)
    {
        skinningWeight.append(*it);
    }
    return skinningWeight;
}

boost::python::list getSkinningIndex()
{
    boost::python::list skinningIndex;
    for (auto it = ssdrOutput.index.begin(); it != ssdrOutput.index.end(); ++it)
    {
        skinningIndex.append(*it);
    }
    return skinningIndex;
}

int getNumBones()
{
    return ssdrOutput.numBones;
}

boost::python::list getBoneTranslation(int boneIdx, int frame)
{
    boost::python::list retval;
    MVector v = ssdrOutput.boneTrans[frame * ssdrOutput.numBones + boneIdx].getTranslation(MSpace::kTransform);
    retval.append(v.x);
    retval.append(v.y);
    retval.append(v.z);
    return retval;
}

boost::python::list getBoneRotation(int boneIdx, int frame)
{
    boost::python::list retval;
    double x, y, z, w;
    ssdrOutput.boneTrans[frame * ssdrOutput.numBones + boneIdx].getRotationQuaternion(x, y, z, w);
    retval.append(x);
    retval.append(y);
    retval.append(z);
    retval.append(w);
    return retval;
}

BOOST_PYTHON_MODULE(ssdr)
{
    boost::python::def("build", &build);
    boost::python::def("getSkinningWeight", &getSkinningWeight);
    boost::python::def("getSkinningIndex", &getSkinningIndex);
    boost::python::def("getNumBones", &getNumBones);
    boost::python::def("getBoneTranslation", &getBoneTranslation);
    boost::python::def("getBoneRotation", &getBoneRotation);
}


#ifndef __METHOD_H__
#define __METHOD_H__

#include <vector>
#include <string>

#include <armadillo>

#include <plink/snp_row.hpp>
#include <shared_ptr/shared_ptr.hpp>

class pairfile;
class resultfile;
class genotype_matrix;
typedef shared_ptr<genotype_matrix> genotype_matrix_ptr;

/**
 * Smallest number of samples with a specific genotype for case/control data,
 * for the normal statistical tests to be reasonably valid.
 */
const unsigned int METHOD_SMALLEST_CELL_SIZE_BINOMIAL = 5;

/**
 * Smallest number of samples with a specific genotype for quantative data,
 * for the normal statistical tests to be reasonably valid.
 */
const unsigned int METHOD_SMALLEST_CELL_SIZE_NORMAL = 10;

/**
 * Represents additional data that is required by the method.
 */
struct method_data
{
    /**
     * The phenotype or outcome.
     */
    arma::vec phenotype;

    /**
     * The covariates.
     */
    arma::mat covariate_matrix;

    /**
     * Missing samples are indicated by non-zero values.
     */
    arma::uvec missing;

    /**
    * The number of interactions to correct for.
    */
    unsigned int num_interactions;

    /**
     * The number of single snps.
     */
    unsigned int num_single;

    /**
     * If true print all parameter estimates.
     */
    bool print_params;

    /**
     * The probability that any one snp is associated.
     */
    float single_prior;

    /**
     * Threshold for filtering variant pairs.
     */
    double threshold;

    /**
     * Use fast less robust matrix inversion.
     */
    bool fast_inversion;
};

/**
 * Pointer to method data.
 */
typedef shared_ptr<method_data> method_data_ptr;

class method_type
{
public:
    /**
     * Constructor.
     */
    method_type(method_data_ptr data)
        : m_data( data ),
          m_num_ok_samples( 0 )
    {
    }

    virtual ~method_type()
    {
    }

    /**
     * Returns the additional data.
     */
    method_data_ptr get_data()
    {
        return m_data;
    }

    virtual void set_num_ok_samples(size_t ok_samples)
    {
        m_num_ok_samples = ok_samples;
    }

    /**
     * Return the number of samples that could be used in
     * the last call to run.
     *
     * @param row1 The first variant.
     * @param row2 The second variant.
     *
     * @return The number of samples that could be used.
     */
    virtual size_t num_ok_samples(const snp_row &row1, const snp_row &row2)
    {
        return m_num_ok_samples;
    }

    /**
     * Return the column names that will be written by this method.
     */
    virtual std::vector<std::string> init() = 0;

    /**
     *
     * @param row1 The first genotype.
     * @param row2 The second genotype.
     * @param output The results for this method, the size will be the same
     *               as the length of the header, -9 is interpreted as missing.
     *               You can assume that all values are initialized to -9.
     *
     * @return The value of the test statistic, should return -9 if not computed or missing.
     */
    virtual double run(const snp_row &row1, const snp_row &row2, float *output) = 0;

private:
    /**
     * Additional data required by the method.
     */
    method_data_ptr m_data;

    /**
     * The number of samples
     */
    size_t m_num_ok_samples;
};

/**
 * Runs the given method on the genotype file, traversing
 * the given list of SNPs.
 *
 * @param method A method to run.
 * @param genotype_matix Genotypes for all SNPs.
 * @param pairs The pairs to test.
 * @param result The result file.
 */
void run_method(method_type &method, genotype_matrix_ptr genotype_matrix, pairfile &pairs, resultfile &result);

#endif /* End of __METHOD_H__ */


// Copyright <disenone>

#include <cassert>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <utility>
#include <boost/range/irange.hpp>

#include "cross.hpp"

namespace aoi { namespace cross {

#define MOVE_DIRECTION_LEFT 0
#define MOVE_DIRECTION_RIGHT 1

#define IfInXZRadiusSquare(dx, dz, x0, z0, x1, z1, radius_square) \
  dx = x0 - x1; \
  dz = z0 - z1; \
  if (dx * dx + dz * dz <= radius_square) \

#define IfNotInXZRadiusSquare(dx, dz, x0, z0, x1, z1, radius_square) \
  dx = x0 - x1; \
  dz = z0 - z1; \
  if (dx * dx + dz * dz > radius_square) \

//--------------------------------------------------------------------------------------------------
class KHashDeleter {
 public:
  void operator()(khash_t(SensorHashMap) *ptr) {
    kh_destroy(SensorHashMap, ptr);
  }
};

Sensor::Sensor(Nuid _sensor_id, float _radius, PlayerAoi *_pplayer)
    : sensor_id(_sensor_id), radius(_radius),
      radius_square(_radius * _radius), pplayer(_pplayer),
      left_x(COORD_TYPE_GUARD_LEFT, _pplayer->pos.x - _radius, _pplayer, this),
      right_x(COORD_TYPE_GUARD_RIGHT, _pplayer->pos.x + _radius, _pplayer, this),
      left_z(COORD_TYPE_GUARD_LEFT, _pplayer->pos.z - radius, _pplayer, this),
      right_z(COORD_TYPE_GUARD_RIGHT, _pplayer->pos.z + radius, _pplayer, this),
      aoi_player_candidates(kh_init(SensorHashMap), KHashDeleter()) {
    kh_resize(SensorHashMap, aoi_player_candidates.get(), 100);
  }

inline void Sensor::AddCandidate(PlayerAoi* other_pplayer) {
  if (pplayer->nuid == other_pplayer->nuid) return;

  auto candidates = aoi_player_candidates.get();
  if (kh_get(SensorHashMap, candidates, other_pplayer->nuid) == kh_end(candidates)) {
    int ret;
    auto k = kh_put(SensorHashMap, candidates, other_pplayer->nuid, &ret);
    assert(ret != -1);
    kh_value(candidates, k) = other_pplayer;

    if (other_pplayer->detected_by) {
      (*other_pplayer->detected_by)[pplayer->nuid].push_back(sensor_id);
    }
  }
}

inline void Sensor::RemoveCandidate(PlayerAoi* other_pplayer) {
  auto candidates = aoi_player_candidates.get();
  auto k = kh_get(SensorHashMap, candidates, other_pplayer->nuid);
  if (k != kh_end(candidates)) {
    kh_del(SensorHashMap, candidates, k);

    if (other_pplayer->detected_by) {
      auto &sensor_ids = (*other_pplayer->detected_by)[pplayer->nuid];
      sensor_ids.erase(std::find(sensor_ids.begin(), sensor_ids.end(), sensor_id));
    }
  }
}

inline void Sensor::PrintCandidate() {
  printf("Player %lu Sensor %lu candidates: ", pplayer->nuid, sensor_id);
  PlayerAoi *val;
  kh_foreach_value(aoi_player_candidates.get(), val,
    printf("%lu ", val->nuid);
  )
  printf("\n");
}
//--------------------------------------------------------------------------------------------------
void CoordNode::PrintLog() {
  printf("(");
  printf("%p, ", this);
  switch (type) {
    case COORD_TYPE_PLAYER:
      printf("player");
      break;
    case COORD_TYPE_GUARD_LEFT:
      printf("left");
      break;
    case COORD_TYPE_GUARD_RIGHT:
      printf("right");
      break;
  }
  printf(" %lu, %f", pplayer->nuid, value);
  printf("), ");
}

//--------------------------------------------------------------------------------------------------
inline void ListInsertBefore(CoordNode **list, CoordNode *pos, CoordNode *ptr) {
  if (!(*list)) {
    ptr->next = nullptr;
    ptr->prev = nullptr;
    *list = ptr;
  } else {
    if (pos->prev) {
      pos->prev->next = ptr;
    }
    ptr->prev = pos->prev;
    pos->prev = ptr;
    ptr->next = pos;
    if (*list == pos) *list = ptr;
  }
}

//--------------------------------------------------------------------------------------------------
inline void ListInsertAfter(CoordNode **list, CoordNode *pos, CoordNode *ptr) {
  if (!(*list)) {
    ptr->next = nullptr;
    ptr->prev = nullptr;
    *list = ptr;
  } else {
    if (pos->next) {
      pos->next->prev = ptr;
    }
    ptr->next = pos->next;
    pos->next = ptr;
    ptr->prev = pos;
  }
}

//--------------------------------------------------------------------------------------------------
inline void ListRemove(CoordNode **list, CoordNode *pos) {
  if (!pos->prev && !pos->next) {
    *list = nullptr;
  } else {
    if (pos->prev) {
      pos->prev->next = pos->next;
    }
    if (pos->next) {
      pos->next->prev = pos->prev;
    }
    if (*list == pos) *list = pos->next;
  }
}

//--------------------------------------------------------------------------------------------------
inline void MoveIn(CoordNode *player_node, CoordNode *sensor_node) {
  if (player_node->pplayer->nuid == sensor_node->pplayer->nuid) return;
  const auto &pos = player_node->pplayer->pos;
  const auto &other_pos = sensor_node->pplayer->pos;
  auto radius = sensor_node->psensor->radius;

  if (fabs(pos.x - other_pos.x) < radius && fabs(pos.z - other_pos.z) < radius) {
    sensor_node->psensor->AddCandidate(player_node->pplayer);
  }
}

inline void MoveOut(CoordNode *player_node, CoordNode *sensor_node) {
  sensor_node->psensor->RemoveCandidate(player_node->pplayer);
}

#define MOVE_CROSS_ID(dir, type1, type2) ((dir << 16) + (type1 << 8) + type2)

inline void MoveCross(Uint32 dir, CoordNode *moving_node, CoordNode *static_node) {
  Uint32 cross_id = MOVE_CROSS_ID(dir, (Uint32)moving_node->type, (Uint32)static_node->type);

  switch (cross_id) {
    case MOVE_CROSS_ID(MOVE_DIRECTION_LEFT, COORD_TYPE_PLAYER, COORD_TYPE_GUARD_RIGHT):
    case MOVE_CROSS_ID(MOVE_DIRECTION_RIGHT, COORD_TYPE_PLAYER, COORD_TYPE_GUARD_LEFT):
      MoveIn(moving_node, static_node);
      break;

    case MOVE_CROSS_ID(MOVE_DIRECTION_LEFT, COORD_TYPE_GUARD_LEFT, COORD_TYPE_PLAYER):
    case MOVE_CROSS_ID(MOVE_DIRECTION_RIGHT, COORD_TYPE_GUARD_RIGHT, COORD_TYPE_PLAYER):
      MoveIn(static_node, moving_node);
      break;

    case MOVE_CROSS_ID(MOVE_DIRECTION_LEFT, COORD_TYPE_PLAYER, COORD_TYPE_GUARD_LEFT):
    case MOVE_CROSS_ID(MOVE_DIRECTION_RIGHT, COORD_TYPE_PLAYER, COORD_TYPE_GUARD_RIGHT):
      MoveOut(moving_node, static_node);
      break;

    case MOVE_CROSS_ID(MOVE_DIRECTION_LEFT, COORD_TYPE_GUARD_RIGHT, COORD_TYPE_PLAYER):
    case MOVE_CROSS_ID(MOVE_DIRECTION_RIGHT, COORD_TYPE_GUARD_LEFT, COORD_TYPE_PLAYER):
      MoveOut(static_node, moving_node);
      break;
  }
}

//--------------------------------------------------------------------------------------------------
void ListUpdateNode(CoordNode **list, CoordNode *pnode) {
  float value = pnode->value;

  if (pnode->next && pnode->next->value < value) {
    // move right
    auto cur_node = pnode->next;
    while (1) {
      MoveCross(MOVE_DIRECTION_RIGHT, pnode, cur_node);
      if (!cur_node->next || cur_node->next->value >= value) break;
      cur_node = cur_node->next;
    }

    ListRemove(list, pnode);
    ListInsertAfter(list, cur_node, pnode);

  } else if (pnode->prev && pnode->prev->value > value) {
    // move left
    auto cur_node = pnode->prev;
    while (1) {
      MoveCross(MOVE_DIRECTION_LEFT, pnode, cur_node);
      if (!cur_node->prev || cur_node->prev->value <= value) break;
      cur_node = cur_node->prev;
    }

    ListRemove(list, pnode);
    ListInsertBefore(list, cur_node, pnode);
  }
}

//--------------------------------------------------------------------------------------------------
CrossAoi::CrossAoi(float map_bound_xmin, float map_bound_xmax, float map_bound_zmin,
                   float map_bound_zmax, size_t beacon_x, size_t beacon_z, float beacon_radius) {
  if (beacon_x == 0 && beacon_z == 0) return;

  assert(map_bound_xmax > map_bound_xmin);
  assert(map_bound_zmax > map_bound_zmin);
  float step_x = (map_bound_xmax - map_bound_xmin) / beacon_x / 2;
  float step_z = (map_bound_zmax - map_bound_zmin) / beacon_z / 2;
  for (auto x : boost::irange(beacon_x)) {
    for (auto z : boost::irange(beacon_z)) {
      float pos_x = map_bound_xmin + step_x * (x * 2 + 1);
      float pos_z = map_bound_zmin + step_z * (z * 2 + 1);
      auto nuid = GenNuid();
      AddPlayerNoBeacon(nuid, pos_x, 0, pos_z);
      AddSensorNoBeacon(nuid, GenNuid(), beacon_radius);
      auto &beacon = *player_map_[nuid];
      beacon.SetFlag_Beacon();
      beacon.detected_by.reset(new boost::unordered_map<Nuid, std::vector<Nuid>>());
      beacons.push_back(&beacon);
    }
  }
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::AddPlayer(Nuid nuid, float x, float y, float z) {
  if (beacons.empty()) {
    AddPlayerNoBeacon(nuid, x, y, z);
    return;
  }

  auto piter = player_map_.find(nuid);
  if (piter != player_map_.end()) {
    AddPlayerNoBeacon(nuid, x, y, z);
    return;
  }

  auto ret = player_map_.emplace(nuid, new PlayerAoi(nuid, x, y, z));
  auto &player = *ret.first->second;
  player.SetFlag_New();

  // 找最近的 beacon
  PlayerAoi* best_beacon = nullptr;
  float min_dist = std::numeric_limits<float>::max();
  for (auto pbeacon : beacons) {
    float dx = pbeacon->pos.x - x;
    float dz = pbeacon->pos.z - z;
    float dist = dx * dx + dz * dz;
    if (dist < min_dist) {
      min_dist = dist;
      best_beacon = pbeacon;
    }
  }
  assert(best_beacon);

  // 复制 detected_by
  for (auto &elem : *best_beacon->detected_by) {
    auto other_nuid = elem.first;
    auto &other_player = *player_map_[other_nuid];
    for (auto sensor_id : elem.second) {
      for (auto &sensor : other_player.sensors) {
        if (sensor.sensor_id == sensor_id) {
          sensor.AddCandidate(&player);
        }
      }
    }
  }
  if (!best_beacon->sensors.empty()) {
    for (auto &sensor : best_beacon->sensors) {
      sensor.AddCandidate(&player);
    }
  }

  ListInsertBefore(&coord_list_x_, &best_beacon->node_x, &player.node_x);
  ListInsertBefore(&coord_list_z_, &best_beacon->node_z, &player.node_z);
  UpdatePos(nuid, x, y, z);
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::AddPlayerNoBeacon(Nuid nuid, float x, float y, float z) {
  auto piter = player_map_.find(nuid);

  if (piter == player_map_.end()) {
    auto ret = player_map_.emplace(
      std::piecewise_construct,
      std::forward_as_tuple(nuid),
      std::forward_as_tuple(new PlayerAoi(nuid, x, y, z)));
    auto &player = *ret.first->second;
    player.SetFlag_New();
    ListInsertBefore(&coord_list_x_, coord_list_x_, &player.node_x);
    ListInsertBefore(&coord_list_z_, coord_list_z_, &player.node_z);
  } else {
    piter->second->UnsetFlag_Removed();
  }
  UpdatePos(nuid, x, y, z);
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::RemovePlayer(Nuid nuid) {
  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  player.SetFlag_Removed();
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::_RemovePlayer(Nuid nuid) {
  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  std::vector<Nuid> sensor_ids;
  for (auto siter = player.sensors.rbegin(); siter != player.sensors.rend(); ++siter) {
    sensor_ids.push_back(siter->sensor_id);
  }

  for (auto sensor_id : sensor_ids) {
    RemoveSensor(nuid, sensor_id);
  }

  ListRemove(&coord_list_x_, &player.node_x);
  ListRemove(&coord_list_z_, &player.node_z);
  player_map_.erase(nuid);
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::AddSensor(Nuid nuid, Nuid sensor_id, float radius) {
  if (beacons.empty()) {
    return AddSensorNoBeacon(nuid, sensor_id, radius);
  }

  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  // 找最近的 beacon
  PlayerAoi* best_beacon = nullptr;
  float min_dist = std::numeric_limits<float>::max();
  for (auto pbeacon : beacons) {
    float dx = pbeacon->pos.x - player.pos.x;
    float dz = pbeacon->pos.z - player.pos.z;
    float dist = dx * dx + dz * dz;
    if (dist < min_dist) {
      min_dist = dist;
      best_beacon = pbeacon;
    }
  }
  assert(best_beacon);

  auto &best_sensor = best_beacon->sensors[0];
  float dr = best_sensor.radius - radius;
  if (dr * dr + min_dist > radius) {
    return AddSensorNoBeacon(nuid, sensor_id, radius);
  }

  player.sensors.emplace_back(sensor_id, radius, &player);
  auto &sensor = player.sensors[player.sensors.size() - 1];
  auto size = kh_size(best_sensor.aoi_player_candidates.get());
  kh_resize(SensorHashMap, sensor.aoi_player_candidates.get(), size);
  PlayerAoi *val;
  kh_foreach_value(best_sensor.aoi_player_candidates.get(), val,
    sensor.AddCandidate(val);
  )
  sensor.AddCandidate(best_beacon);

  ListInsertBefore(&coord_list_x_, &best_sensor.left_x, &sensor.left_x);
  ListInsertAfter(&coord_list_x_, &best_sensor.right_x, &sensor.right_x);

  ListInsertBefore(&coord_list_z_, &best_sensor.left_z, &sensor.left_z);
  ListInsertAfter(&coord_list_z_, &best_sensor.right_z, &sensor.right_z);

  UpdateSensorPos(player, &sensor);
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::AddSensorNoBeacon(Nuid nuid, Nuid sensor_id, float radius) {
  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  player.sensors.emplace_back(sensor_id, radius, &player);
  auto &sensor = player.sensors[player.sensors.size() - 1];

  ListInsertBefore(&coord_list_x_, &player.node_x, &sensor.left_x);
  ListInsertAfter(&coord_list_x_, &player.node_x, &sensor.right_x);

  ListInsertBefore(&coord_list_z_, &player.node_z, &sensor.left_z);
  ListInsertAfter(&coord_list_z_, &player.node_z, &sensor.right_z);

  UpdateSensorPos(player, &sensor);
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::RemoveSensor(Nuid nuid, Nuid sensor_id) {
  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  if (player.sensors.size() > 1) {
    auto &last_sensor = player.sensors[player.sensors.size() - 1];
    if (last_sensor.sensor_id != sensor_id) {
      for (auto &sensor : player.sensors) {
        if (sensor.sensor_id == sensor_id) {
          std::swap(sensor, last_sensor);
          break;
        }
      }
    }
  }

  auto &last_sensor = player.sensors[player.sensors.size() - 1];
  if (last_sensor.sensor_id != sensor_id) return;

  ListRemove(&coord_list_x_, &last_sensor.left_x);
  ListRemove(&coord_list_x_, &last_sensor.right_x);
  ListRemove(&coord_list_z_, &last_sensor.left_z);
  ListRemove(&coord_list_z_, &last_sensor.right_z);
  player.sensors.pop_back();
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::UpdatePos(Nuid nuid, float x, float y, float z) {
  auto piter = player_map_.find(nuid);
  if (piter == player_map_.end()) return;

  auto &player = *piter->second;
  player.pos.Set(x, y, z);
  player.SetFlag_Dirty();

  player.node_x.value = player.pos.x;
  ListUpdateNode(&coord_list_x_, &player.node_x);

  player.node_z.value = player.pos.z;
  ListUpdateNode(&coord_list_z_, &player.node_z);

  if (!player.sensors.empty()) {
    for (auto &sensor : player.sensors) {
      UpdateSensorPos(player, &sensor);
    }
  }
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::UpdateSensorPos(const PlayerAoi &player, Sensor *psensor) {
  auto radius = psensor->radius;
  psensor->right_x.value = player.pos.x + radius;
  ListUpdateNode(&coord_list_x_, &psensor->right_x);

  psensor->left_x.value = player.pos.x - radius;
  ListUpdateNode(&coord_list_x_, &psensor->left_x);

  psensor->right_z.value = player.pos.z + radius;
  ListUpdateNode(&coord_list_z_, &psensor->right_z);

  psensor->left_z.value = player.pos.z - radius;
  ListUpdateNode(&coord_list_z_, &psensor->left_z);
}

//--------------------------------------------------------------------------------------------------
AoiUpdateInfos CrossAoi::Tick() {
  // 全量做一遍 aoi
  AoiUpdateInfos update_infos;
  PlayerPtrList remove_list;

  for (auto& elem : player_map_) {
    auto& player = *elem.second;
    if (player.GetFlag_Beacon()) continue;

    if (player.GetFlag_Removed()) {
      remove_list.push_back(&player);
      continue;
    }

    if (!player.sensors.empty()) {
      auto update_info = _UpdatePlayerAoi(cur_aoi_map_idx_, &player);
      if (!update_info.sensor_update_list.empty()) {
        update_infos.emplace(update_info.nuid, std::move(update_info));
      }
    }

    player.UnsetFlag_New();
  }

  for (auto pptr : remove_list) {
    _RemovePlayer(pptr->nuid);
  }

  for (auto& elem : player_map_) {
    auto& player = *elem.second;
    player.last_pos = player.pos;
  }
  cur_aoi_map_idx_ = 1 - cur_aoi_map_idx_;
  return update_infos;
}

//--------------------------------------------------------------------------------------------------
AoiUpdateInfo CrossAoi::_UpdatePlayerAoi(Uint32 cur_aoi_map_idx,
                                          PlayerAoi* pptr) {
  AoiUpdateInfo aoi_update_info;
  aoi_update_info.nuid = pptr->nuid;
  Uint32 new_aoi_map_idx = 1 - cur_aoi_map_idx;

  for (auto& sensor : pptr->sensors) {
    auto& old_aoi = sensor.aoi_players[cur_aoi_map_idx];
    auto& new_aoi = sensor.aoi_players[new_aoi_map_idx];
    _CalcAoiPlayers(*pptr, sensor, &new_aoi);

    SensorUpdateInfo update_info;

    auto& enters = update_info.enters;
    auto& leaves = update_info.leaves;
    float radius_square = sensor.radius_square;

    _CheckLeave(pptr, radius_square, old_aoi, &leaves);
    _CheckEnter(pptr, radius_square, new_aoi, &enters);

    if (enters.empty() && leaves.empty()) {
      continue;
    }

    update_info.sensor_id = sensor.sensor_id;
    aoi_update_info.sensor_update_list.push_back(std::move(update_info));
  }

  return aoi_update_info;
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::_CalcAoiPlayers(const PlayerAoi& player, const Sensor& sensor,
                                PlayerPtrList* aoi_map) {
  aoi_map->clear();
  auto candidates = sensor.aoi_player_candidates.get();
  aoi_map->reserve(kh_size(candidates));

  auto pos = player.pos;
  auto radius = sensor.radius;
  auto radius_suqare = radius * radius;
  float dx, dz;

  PlayerAoi* other_ptr;
  std::vector<PlayerAoi*> remove_players;
  kh_foreach_value(candidates, other_ptr,
    if (other_ptr->GetFlag_Beacon()) continue;
    if (other_ptr->GetFlag_Removed()) {
      remove_players.push_back(other_ptr);
      continue;
    }
    IfInXZRadiusSquare(dx, dz, other_ptr->pos.x, other_ptr->pos.z, pos.x, pos.z, radius_suqare) {
      aoi_map->emplace_back(other_ptr);
    }
  )
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::_CheckLeave(PlayerAoi* pptr, float radius_square,
                             const PlayerPtrList &aoi_players, PlayerNuids *leaves) {
  const auto &player_pos = pptr->pos;
  float dx, dz;
  float pos_x = player_pos.x;
  float pos_z = player_pos.z;
  for (auto old_player_ptr : aoi_players) {
    if (old_player_ptr->GetFlag_Removed()) {
      leaves->push_back(old_player_ptr->nuid);
    } else {
      IfNotInXZRadiusSquare(dx, dz, old_player_ptr->pos.x, old_player_ptr->pos.z,
                            pos_x, pos_z, radius_square) {
        leaves->push_back(old_player_ptr->nuid);
      }
    }
  }
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::_CheckEnter(PlayerAoi* pptr, float radius_square,
                             const PlayerPtrList &aoi_players, PlayerNuids *enters) {
  const auto &player_last_pos = pptr->last_pos;
  float pos_x = player_last_pos.x;
  float pos_z = player_last_pos.z;

  if (pptr->GetFlag_New()) {
    enters->reserve(aoi_players.size());
    for (auto new_player_ptr : aoi_players) {
      enters->push_back(new_player_ptr->nuid);
    }
    return;
  }

  float dx, dz;
  for (auto new_player_ptr : aoi_players) {
    IfNotInXZRadiusSquare(dx, dz, new_player_ptr->last_pos.x, new_player_ptr->last_pos.z,
                          pos_x, pos_z, radius_square) {
      enters->push_back(new_player_ptr->nuid);
    }
  }
}

//--------------------------------------------------------------------------------------------------
void CrossAoi::_PrintNodeList(CoordNode *list) {
  printf("[");
  auto cur_node = list;
  while (cur_node) {
    cur_node->PrintLog();
    cur_node = cur_node->next;
  }
  printf("]");
}

void CrossAoi::PrintAllNodeList() {
  printf("x_list: ");
  _PrintNodeList(coord_list_x_);
  printf("\nz_list: ");
  _PrintNodeList(coord_list_z_);
  printf("\n");
}

}  // namespace cross
}  // namespace aoi