#include "libs/ml_models/logistic_regression.h"
#include "shared_operations.hpp"
#include "utils/missing_values.hpp"
#include <opencv2/core.hpp>
#include <opencv2/ml.hpp>
#include <boost/assign.hpp>
#include <map>

namespace mlmodels
{

logistic_regression::args::args(double lr, int iters,
        int min_bs, regularization_t r, train_method_t tm) :
    learning_rate(lr), iterations(iters), min_batch_size(min_bs),
    regular(r), train_method(tm)
{
}

logistic_regression::args::regularization_t
    logistic_regression::args::string2reg(const std::string& from)
{
    static const std::map<std::string, regularization_t> mapper =
        boost::assign::map_list_of("disable", regularization_t::DISABLE)
                        ("reg_l1", regularization_t::REG_L1)
                        ("reg_l2", regularization_t::REG_L2);

    auto i = mapper.find(from);
    if (i == std::end(mapper)) {
        throw std::runtime_error{"invalid entry for logistic regaression; " + from};
    } else {
        return i->second;
    }

}

logistic_regression::args::train_method_t
    logistic_regression::args::string2train(const std::string& from)
{
    static const std::map<std::string, train_method_t> mapper =
        boost::assign::map_list_of("batch", BATCH)
                        ("min-batch", MIN_BATCH);
    auto i = mapper.find(from);
    if (i == std::end(mapper)) {
        throw std::runtime_error{"invalid logistic regression method " + from};
    }
    return i->second;

}

cv::ml::LogisticRegression::RegKinds
    convert(logistic_regression::args::regularization_t from);

cv::ml::LogisticRegression::Methods
    convert(logistic_regression::args::train_method_t from);

cv::ml::LogisticRegression::RegKinds
    convert(logistic_regression::args::regularization_t from)
{
    switch (from) {
        case logistic_regression::args::DISABLE:
           return  cv::ml::LogisticRegression::REG_DISABLE;
        case logistic_regression::args::REG_L1:
            return cv::ml::LogisticRegression::REG_L1;
        case logistic_regression::args::REG_L2:
           return  cv::ml::LogisticRegression::REG_L2;
    }
}

cv::ml::LogisticRegression::Methods
    convert(logistic_regression::args::train_method_t from)
{
    switch (from) {
        case logistic_regression::args::BATCH:
            return cv::ml::LogisticRegression::BATCH;
        case logistic_regression::args::MIN_BATCH:
            return cv::ml::LogisticRegression::MINI_BATCH;
    }
}

struct logistic_reg_impl : detail::general_model<cv::ml::LogisticRegression>
{
    using base_t = detail::general_model<cv::ml::LogisticRegression>;
    using base_t::base_t;
    logistic_reg_impl(const logistic_regression::args& args) :
        base_t{}
    {
        handle->setLearningRate(args.learning_rate);
        handle->setIterations(args.iterations);
        handle->setMiniBatchSize(args.min_batch_size);
        handle->setRegularization(convert(args.regular));
        handle->setTrainMethod(convert(args.train_method));
    }

    bool train(const training_data& samples,
            const class_data& lables)
    {
        return base_t::class_train(samples, lables, CV_32F, CV_32F);
    }

};

logistic_regression::model_type
    logistic_regression::load(const char* from)
{
    return model_type{std::make_shared<logistic_reg_impl>(logistic_reg_impl::load(from))};
}

logistic_regression::model_type
logistic_regression::train(const training_data& data,
        const class_data& classes, const args& a) const
{
    model_type model{std::make_shared<logistic_reg_impl>(a)};
    if (model->train(data, classes)) {
        return model;
    } else {
        static const model_type err = model_type{};
        return err;
    }
}

value_type logistic_regression::predict(model_type model,
                const class_data& samples) const
{
    if (model) {
        return model->predict(samples);
    }
    return missing_value<value_type>();
}

bool logistic_regression::predict(model_type model,
        const testing_data& data,
        testing_result& predications) const
{
    if (model) {
        return model->predict(data, predications);
    } else {
        return false;
    }
}

class_data classifier_LG_model::test(model_type model,
        const testing_data& data, int classes) const
{
    if (model && !data.empty()) {
        return model->test(data, classes);
    }
    return class_data{};
}

class_data regression_LR_model::test(model_type model,
        const testing_data& data, int ) const
{
    if (model && !data.empty()) {
        return model->test(data, -1);
    }
    return class_data{};
}

void save_model(logistic_regression::model_type model, const char* path)
{
    if (model) {
        model->save(path);
    }
}

}   // end of namespace mlmodels


/*=============================================================================
Copyright (c) 2016 Paul W. Bible

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

#include <boost/unordered_set.hpp>
#include <boost/foreach.hpp>

//! The SetUtilities namespace provides useful opperations on generic boost::unordered_set
/*!
	SetUtilities provides useful operations on boost::unordered_set.
	set_intersection is optimized to traverse the smaller set and test exisitence in the larger set.
*/
namespace SetUtilities{

	//!set UTILITY funciton to determine if a set contains an element
	template<typename Type>
	static bool
		set_contains(const boost::unordered_set<Type> &mySet,
		               Type element)
	{
		return mySet.find(element) != mySet.end();
	}

	//!set UTILITY funciton convert a vector to a set
	template<typename Type>
	static boost::unordered_set<Type>
		convert_vector(const std::vector<Type> &myVec)
	{
		boost::unordered_set<Type> s(myVec.begin(),myVec.end());
		return s;
	}

	//!set OPERATION intersection for boost::unordered_set
	template<typename Type>
	static boost::unordered_set<Type>
		set_intersection(const boost::unordered_set<Type> &setA,
		                 const boost::unordered_set<Type> &setB)
	{
		//iterate the smaller set O(n)
		if(setA.size() <= setB.size()){
			boost::unordered_set<Type> intersectionSet;
			BOOST_FOREACH(const Type &element, setA){
				//find element in O(1)
				if(set_contains(setB,element)){
					intersectionSet.insert(element);
				}
			}
			return intersectionSet;
		}else{
			return set_intersection(setB,setA);
		}
	}

	//!set OPERATION union for boost::unordered_set
	template<typename Type>
	static boost::unordered_set<Type>
		set_union(const boost::unordered_set<Type> &setA,
		          const boost::unordered_set<Type> &setB)
	{
		if(setA.size() <= setB.size()){
			boost::unordered_set<Type> unionSet = setB;
			unionSet.insert(setA.begin(),setA.end());
			return unionSet;
		}else{
			return set_union(setB,setA);
		}
	}

	//!set OPERATION difference A - B
	template<typename Type>
	static boost::unordered_set<Type>
		set_difference(const boost::unordered_set<Type> &setA,
		               const boost::unordered_set<Type> &setB)
	{
		boost::unordered_set<Type> differenceSet;
		BOOST_FOREACH(const Type &element,setA){
			if(!set_contains(setB,element)){
				differenceSet.insert(element);
			}
		}
		return differenceSet;
	}

};
#endif


/*
 Copyright (C) 2019 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

/*! \file qle/termstructures/strippedoptionletadapter.hpp
    \brief Convert a StrippedOptionletBase in to an OptionletVolatilityStructure
    \ingroup termstructures
*/

#ifndef quantext_stripped_optionlet_adapter_h
#define quantext_stripped_optionlet_adapter_h

#include <algorithm>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <ql/math/interpolation.hpp>
#include <ql/termstructures/interpolatedcurve.hpp>
#include <ql/termstructures/volatility/flatsmilesection.hpp>
#include <ql/termstructures/volatility/interpolatedsmilesection.hpp>
#include <ql/termstructures/volatility/optionlet/optionletvolatilitystructure.hpp>
#include <ql/termstructures/volatility/optionlet/strippedoptionletbase.hpp>
#include <ql/utilities/dataformatters.hpp>
#include <vector>

namespace QuantExt {

/*! Adapter class for turning a StrippedOptionletBase into an OptionletVolatilityStructure.

    The class takes two template parameters indicating the interpolation in the time and strike direction respectively.

    The class can take a QuantLib::StrippedOptionletBase that has only one strike column. In this case, the strike
    interpolation is ignored and the volatility at one of the pillar tenors, and any strike, is merely the passed
    in volatility. In this case, the smile sections are flat. All of this enables the StrippedOptionletAdapter to
    represent a stripped ATM optionlet curve. The single strike in the QuantLib::StrippedOptionletBase is ignored.

    \ingroup termstructures
*/
template <class TimeInterpolator, class SmileInterpolator>
class StrippedOptionletAdapter : public QuantLib::OptionletVolatilityStructure, public QuantLib::LazyObject {

public:
    /*! Constructor that does not take a reference date. The settlement days is derived from \p sob and the term
        structure will be a \e moving term structure.
    */
    StrippedOptionletAdapter(const boost::shared_ptr<QuantLib::StrippedOptionletBase>& sob,
                             const TimeInterpolator& ti = TimeInterpolator(),
                             const SmileInterpolator& si = SmileInterpolator());

    /*! Constructor taking an explicit \p referenceDate and the term structure will therefore be not \e moving.
     */
    StrippedOptionletAdapter(const QuantLib::Date& referenceDate,
                             const boost::shared_ptr<QuantLib::StrippedOptionletBase>& sob,
                             const TimeInterpolator& ti = TimeInterpolator(),
                             const SmileInterpolator& si = SmileInterpolator());

    //! \name TermStructure interface
    //@{
    QuantLib::Date maxDate() const;
    //@}

    //! \name VolatilityTermStructure interface
    //@{
    QuantLib::Rate minStrike() const;
    QuantLib::Rate maxStrike() const;
    //@}

    //! \name OptionletVolatilityStructure interface
    //@{
    QuantLib::VolatilityType volatilityType() const;
    QuantLib::Real displacement() const;
    //@}

    //! \name LazyObject interface
    //@{
    void update();
    void performCalculations() const;
    //@}

    //! \name Observer interface
    //@{
    void deepUpdate();
    //@}

    //! \name Inspectors
    //@{
    boost::shared_ptr<QuantLib::StrippedOptionletBase> optionletBase() const;
    //@}

protected:
    //! \name OptionletVolatilityStructure interface
    //@{
    boost::shared_ptr<QuantLib::SmileSection> smileSectionImpl(QuantLib::Time optionTime) const;
    QuantLib::Volatility volatilityImpl(QuantLib::Time length, QuantLib::Rate strike) const;
    //@}

private:
    //! Base optionlet object that provides the stripped optionlet volatilities
    boost::shared_ptr<QuantLib::StrippedOptionletBase> optionletBase_;

    //! The interpolation object in the time direction
    TimeInterpolator ti_;

    //! The interpolation object in the strike direction
    SmileInterpolator si_;

    /*! The optionlet volatility vs strike section at each optionlet fixing date
        It is \c mutable so that we can call \c enableExtrapolation in \c performCalculations.
    */
    mutable std::vector<QuantLib::Interpolation> strikeSections_;

    //! Flag that indicates if optionletBase_ has only one strike for all option tenors
    bool oneStrike_;

    //! Method to populate oneStrike_ on initialisation
    void populateOneStrike();
};

template <class TimeInterpolator, class SmileInterpolator>
StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::StrippedOptionletAdapter(
    const boost::shared_ptr<QuantLib::StrippedOptionletBase>& sob, const TimeInterpolator& ti,
    const SmileInterpolator& si)
    : OptionletVolatilityStructure(sob->settlementDays(), sob->calendar(), sob->businessDayConvention(),
                                   sob->dayCounter()),
      optionletBase_(sob), ti_(ti), si_(si), strikeSections_(optionletBase_->optionletMaturities()) {
    registerWith(optionletBase_);
    populateOneStrike();
}

template <class TimeInterpolator, class SmileInterpolator>
StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::StrippedOptionletAdapter(
    const QuantLib::Date& referenceDate, const boost::shared_ptr<QuantLib::StrippedOptionletBase>& sob,
    const TimeInterpolator& ti, const SmileInterpolator& si)
    : OptionletVolatilityStructure(referenceDate, sob->calendar(), sob->businessDayConvention(), sob->dayCounter()),
      optionletBase_(sob), ti_(ti), si_(si), strikeSections_(optionletBase_->optionletMaturities()) {
    registerWith(optionletBase_);
    populateOneStrike();
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::Date StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::maxDate() const {
    return optionletBase_->optionletFixingDates().back();
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::Rate StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::minStrike() const {

    // If only one strike
    if (oneStrike_) {
        if (volatilityType() == QuantLib::ShiftedLognormal) {
            return displacement() > 0.0 ? -displacement() : 0.0;
        } else {
            return QL_MIN_REAL;
        }
    }

    // Return the minimum strike over all optionlet tenors
    QuantLib::Rate minStrike = optionletBase_->optionletStrikes(0).front();
    for (QuantLib::Size i = 1; i < optionletBase_->optionletMaturities(); ++i) {
        minStrike = std::min(optionletBase_->optionletStrikes(i).front(), minStrike);
    }
    return minStrike;
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::Rate StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::maxStrike() const {

    // If only one strike, there is no max strike
    if (oneStrike_) {
        return QL_MAX_REAL;
    }

    // Return the maximum strike over all optionlet tenors
    QuantLib::Rate maxStrike = optionletBase_->optionletStrikes(0).back();
    for (QuantLib::Size i = 1; i < optionletBase_->optionletMaturities(); ++i) {
        maxStrike = std::max(optionletBase_->optionletStrikes(i).back(), maxStrike);
    }
    return maxStrike;
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::VolatilityType StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::volatilityType() const {
    return optionletBase_->volatilityType();
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::Real StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::displacement() const {
    return optionletBase_->displacement();
}

template <class TimeInterpolator, class SmileInterpolator>
inline void StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::update() {

    // Need this or some tests with ObservationMode::Mode::Disable fail
    // e.g. */SensitivityAnalysisTest/testPortfolioSensitivityDisableObs
    optionletBase_->update();

    TermStructure::update();
    LazyObject::update();
}

template <class TimeInterpolator, class SmileInterpolator>
inline void StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::performCalculations() const {

    // Some localised typedefs and using declarations to make the code more readable
    using QuantLib::Rate;
    using QuantLib::Size;
    using QuantLib::Volatility;
    using std::vector;

    // If only one strike, no strike section to update
    if (oneStrike_) {
        return;
    }

    for (Size i = 0; i < optionletBase_->optionletMaturities(); ++i) {
        const vector<Rate>& strikes = optionletBase_->optionletStrikes(i);
        const vector<Volatility>& vols = optionletBase_->optionletVolatilities(i);
        strikeSections_[i] = si_.interpolate(strikes.begin(), strikes.end(), vols.begin());
        // Extrapolation can be enabled here. Range checks are performed in the volatility methods.
        strikeSections_[i].enableExtrapolation();
    }
}

template <class TimeInterpolator, class SmileInterpolator>
inline void StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::deepUpdate() {
    optionletBase_->update();
    update();
}

template <class TimeInterpolator, class SmileInterpolator>
inline boost::shared_ptr<QuantLib::StrippedOptionletBase>
StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::optionletBase() const {
    return optionletBase_;
}

template <class TimeInterpolator, class SmileInterpolator>
inline boost::shared_ptr<QuantLib::SmileSection>
StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::smileSectionImpl(QuantLib::Time optionTime) const {

    // Some localised typedefs and using declarations to make the code more readable
    using boost::lambda::_1;
    using boost::lambda::_2;
    using QuantLib::close;
    using QuantLib::Null;
    using QuantLib::Rate;
    using QuantLib::Real;
    using QuantLib::Size;
    using QuantLib::Volatility;
    using QuantLib::io::ordinal;
    using std::equal;
    using std::sqrt;
    using std::vector;

    // Leave ATM rate as Null<Real>() for now but could interpolate optionletBase_->atmOptionletRates()?
    Real atmRate = Null<Real>();

    // If one strike, return a flat smile section
    if (oneStrike_) {
        Volatility vol = volatility(optionTime, optionletBase_->optionletStrikes(0)[0], true);
        return boost::make_shared<QuantLib::FlatSmileSection>(optionTime, vol, optionletBase_->dayCounter(), atmRate,
                                                              volatilityType(), displacement());
    }

    // This method can only return a valid value if the strikes are the same for all optionlet dates
    const vector<Rate>& strikes = optionletBase_->optionletStrikes(0);
    for (Size i = 1; i < optionletBase_->optionletMaturities(); ++i) {
        const vector<Rate>& compStrikes = optionletBase_->optionletStrikes(i);
        QL_REQUIRE(equal(strikes.begin(), strikes.end(), compStrikes.begin(), boost::bind(close, _1, _2)),
                   "The strikes at the " << ordinal(i)
                                         << " optionlet date do not equal those at the first optionlet date");
    }

    // Store standard deviation at each strike
    vector<Real> stdDevs;
    for (Size i = 0; i < strikes.size(); i++) {
        stdDevs.push_back(sqrt(blackVariance(optionTime, strikes[i], true)));
    }

    // Return the smile section.
    return boost::make_shared<QuantLib::InterpolatedSmileSection<SmileInterpolator> >(
        optionTime, strikes, stdDevs, atmRate, si_, optionletBase_->dayCounter(), volatilityType(), displacement());
}

template <class TimeInterpolator, class SmileInterpolator>
inline QuantLib::Volatility
StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::volatilityImpl(QuantLib::Time optionTime,
                                                                              QuantLib::Rate strike) const {

    // Some localised typedefs and using declarations to make the code more readable
    using QuantLib::Interpolation;
    using QuantLib::Size;
    using QuantLib::Time;
    using QuantLib::Volatility;
    using std::vector;

    calculate();

    vector<Volatility> vols(optionletBase_->optionletMaturities());
    for (Size i = 0; i < optionletBase_->optionletMaturities(); ++i) {
        vols[i] = oneStrike_ ? optionletBase_->optionletVolatilities(i)[0] : strikeSections_[i](strike);
    }

    vector<Time> fixingTimes = optionletBase_->optionletFixingTimes();
    Interpolation ti = ti_.interpolate(fixingTimes.begin(), fixingTimes.end(), vols.begin());

    // Extrapolation can be enabled at this level. The range checks will have already been performed in
    // the public OptionletVolatilityStructure::volatility method that calls this `Impl`
    ti.enableExtrapolation();

    return ti(optionTime);
}

template <class TimeInterpolator, class SmileInterpolator>
inline void StrippedOptionletAdapter<TimeInterpolator, SmileInterpolator>::populateOneStrike() {
    oneStrike_ = true;
    for (QuantLib::Size i = 0; i < optionletBase_->optionletMaturities(); ++i) {
        if (optionletBase_->optionletStrikes(i).size() > 1) {
            oneStrike_ = false;
            return;
        }
    }
}

} // namespace QuantExt

#endif


//          Copyright Carl Philipp Reh 2009 - 2018.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef FCPPT_MATH_IS_ZERO_BOOST_UNITS_HPP_INCLUDED
#define FCPPT_MATH_IS_ZERO_BOOST_UNITS_HPP_INCLUDED

#include <fcppt/math/is_zero.hpp>
#include <fcppt/config/external_begin.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/unit.hpp>
#include <fcppt/config/external_end.hpp>

namespace fcppt
{
namespace math
{

/**
\brief Integrates boost.units with #fcppt::math::is_zero.

\ingroup fcpptboost
*/
template<
	typename Unit,
	typename Type
>
struct is_zero_impl<
	boost::units::quantity<
		Unit,
		Type
	>
>
{
	static
	bool
	get(
		boost::units::quantity<
			Unit,
			Type
		> const &_value
	)
	{
		return
			fcppt::math::is_zero(
				_value.value()
			);
	}
};

}
}

#endif


/* boost random/uniform_smallint.hpp header file
 *
 * Copyright Jens Maurer 2000-2001
 * 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)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id$
 *
 * Revision history
 *  2001-04-08  added min<max assertion (N. Becker)
 *  2001-02-18  moved to individual header files
 */

#ifndef BOOST_RANDOM_UNIFORM_SMALLINT_HPP
#define BOOST_RANDOM_UNIFORM_SMALLINT_HPP

#include <istream>
#include <iosfwd>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/limits.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/random/detail/config.hpp>
#include <boost/random/detail/operators.hpp>
#include <boost/random/detail/signed_unsigned_tools.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/detail/workaround.hpp>

namespace boost {
namespace random {

// uniform integer distribution on a small range [min, max]

/**
 * The distribution function uniform_smallint models a \random_distribution.
 * On each invocation, it returns a random integer value uniformly distributed
 * in the set of integer numbers {min, min+1, min+2, ..., max}. It assumes
 * that the desired range (max-min+1) is small compared to the range of the
 * underlying source of random numbers and thus makes no attempt to limit
 * quantization errors.
 *
 * Let \f$r_{\mathtt{out}} = (\mbox{max}-\mbox{min}+1)\f$ the desired range of
 * integer numbers, and
 * let \f$r_{\mathtt{base}}\f$ be the range of the underlying source of random
 * numbers. Then, for the uniform distribution, the theoretical probability
 * for any number i in the range \f$r_{\mathtt{out}}\f$ will be
 * \f$\displaystyle p_{\mathtt{out}}(i) = \frac{1}{r_{\mathtt{out}}}\f$.
 * Likewise, assume a uniform distribution on \f$r_{\mathtt{base}}\f$ for
 * the underlying source of random numbers, i.e.
 * \f$\displaystyle p_{\mathtt{base}}(i) = \frac{1}{r_{\mathtt{base}}}\f$.
 * Let \f$p_{\mathtt{out\_s}}(i)\f$ denote the random
 * distribution generated by @c uniform_smallint. Then the sum over all
 * i in \f$r_{\mathtt{out}}\f$ of
 * \f$\displaystyle
 * \left(\frac{p_{\mathtt{out\_s}}(i)}{p_{\mathtt{out}}(i)} - 1\right)^2\f$
 * shall not exceed
 * \f$\displaystyle \frac{r_{\mathtt{out}}}{r_{\mathtt{base}}^2}
 * (r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
 * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$.
 *
 * The template parameter IntType shall denote an integer-like value type.
 *
 * @xmlnote
 * The property above is the square sum of the relative differences
 * in probabilities between the desired uniform distribution
 * \f$p_{\mathtt{out}}(i)\f$ and the generated distribution
 * \f$p_{\mathtt{out\_s}}(i)\f$.
 * The property can be fulfilled with the calculation
 * \f$(\mbox{base\_rng} \mbox{ mod } r_{\mathtt{out}})\f$, as follows:
 * Let \f$r = r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}}\f$.
 * The base distribution on \f$r_{\mathtt{base}}\f$ is folded onto the
 * range \f$r_{\mathtt{out}}\f$. The numbers i < r have assigned
 * \f$\displaystyle
 * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor+1\f$
 * numbers of the base distribution, the rest has only \f$\displaystyle
 * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor\f$.
 * Therefore,
 * \f$\displaystyle p_{\mathtt{out\_s}}(i) =
 * \left(\left\lfloor\frac{r_{\mathtt{base}}}
 * {r_{\mathtt{out}}}\right\rfloor+1\right) /
 * r_{\mathtt{base}}\f$ for i < r and \f$\displaystyle p_{\mathtt{out\_s}}(i) =
 * \left\lfloor\frac{r_{\mathtt{base}}}
 * {r_{\mathtt{out}}}\right\rfloor/r_{\mathtt{base}}\f$ otherwise.
 * Substituting this in the
 * above sum formula leads to the desired result.
 * @endxmlnote
 *
 * Note: The upper bound for
 * \f$(r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
 * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$ is
 * \f$\displaystyle \frac{r_{\mathtt{out}}^2}{4}\f$.  Regarding the upper bound
 * for the square sum of the relative quantization error of
 * \f$\displaystyle \frac{r_\mathtt{out}^3}{4r_{\mathtt{base}}^2}\f$, it
 * seems wise to either choose \f$r_{\mathtt{base}}\f$ so that
 * \f$r_{\mathtt{base}} > 10r_{\mathtt{out}}^2\f$ or ensure that
 * \f$r_{\mathtt{base}}\f$ is
 * divisible by \f$r_{\mathtt{out}}\f$.
 */
template<class IntType = int>
class uniform_smallint
{
public:
    typedef IntType input_type;
    typedef IntType result_type;

    class param_type
    {
    public:

        typedef uniform_smallint distribution_type;

        /** constructs the parameters of a @c uniform_smallint distribution. */
        param_type(IntType min_arg = 0, IntType max_arg = 9)
          : _min(min_arg), _max(max_arg)
        {
            BOOST_ASSERT(_min <= _max);
        }

        /** Returns the minimum value. */
        IntType a() const { return _min; }
        /** Returns the maximum value. */
        IntType b() const { return _max; }

        /** Writes the parameters to a @c std::ostream. */
        BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
        {
            os << parm._min << " " << parm._max;
            return os;
        }

        /** Reads the parameters from a @c std::istream. */
        BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
        {
            is >> parm._min >> std::ws >> parm._max;
            return is;
        }

        /** Returns true if the two sets of parameters are equal. */
        BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
        { return lhs._min == rhs._min && lhs._max == rhs._max; }

        /** Returns true if the two sets of parameters are different. */
        BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)

    private:
        IntType _min;
        IntType _max;
    };

    /**
     * Constructs a @c uniform_smallint. @c min and @c max are the
     * lower and upper bounds of the output range, respectively.
     */
    explicit uniform_smallint(IntType min_arg = 0, IntType max_arg = 9)
      : _min(min_arg), _max(max_arg) {}

    /**
     * Constructs a @c uniform_smallint from its parameters.
     */
    explicit uniform_smallint(const param_type& parm)
      : _min(parm.a()), _max(parm.b()) {}

    /** Returns the minimum value of the distribution. */
    result_type a() const { return _min; }
    /** Returns the maximum value of the distribution. */
    result_type b() const { return _max; }
    /** Returns the minimum value of the distribution. */
    result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _min; }
    /** Returns the maximum value of the distribution. */
    result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _max; }

    /** Returns the parameters of the distribution. */
    param_type param() const { return param_type(_min, _max); }
    /** Sets the parameters of the distribution. */
    void param(const param_type& parm)
    {
        _min = parm.a();
        _max = parm.b();
    }

    /**
     * Effects: Subsequent uses of the distribution do not depend
     * on values produced by any engine prior to invoking reset.
     */
    void reset() { }

    /** Returns a value uniformly distributed in the range [min(), max()]. */
    template<class Engine>
    result_type operator()(Engine& eng) const
    {
        typedef typename Engine::result_type base_result;
        return generate(eng, boost::is_integral<base_result>());
    }

    /** Returns a value uniformly distributed in the range [param.a(), param.b()]. */
    template<class Engine>
    result_type operator()(Engine& eng, const param_type& parm) const
    { return uniform_smallint(parm)(eng); }

    /** Writes the distribution to a @c std::ostream. */
    BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, uniform_smallint, ud)
    {
        os << ud._min << " " << ud._max;
        return os;
    }

    /** Reads the distribution from a @c std::istream. */
    BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, uniform_smallint, ud)
    {
        is >> ud._min >> std::ws >> ud._max;
        return is;
    }

    /**
     * Returns true if the two distributions will produce identical
     * sequences of values given equal generators.
     */
    BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(uniform_smallint, lhs, rhs)
    { return lhs._min == rhs._min && lhs._max == rhs._max; }

    /**
     * Returns true if the two distributions may produce different
     * sequences of values given equal generators.
     */
    BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(uniform_smallint)

private:

    // \cond show_private
    template<class Engine>
    result_type generate(Engine& eng, boost::mpl::true_) const
    {
        // equivalent to (eng() - eng.min()) % (_max - _min + 1) + _min,
        // but guarantees no overflow.
        typedef typename Engine::result_type base_result;
        typedef typename boost::make_unsigned<base_result>::type base_unsigned;
        typedef typename boost::make_unsigned<result_type>::type range_type;
        range_type range = random::detail::subtract<result_type>()(_max, _min);
        base_unsigned base_range =
            random::detail::subtract<result_type>()((eng.max)(), (eng.min)());
        base_unsigned val =
            random::detail::subtract<base_result>()(eng(), (eng.min)());
        if(range >= base_range) {
            return boost::random::detail::add<range_type, result_type>()(
                static_cast<range_type>(val), _min);
        } else {
            base_unsigned modulus = static_cast<base_unsigned>(range) + 1;
            return boost::random::detail::add<range_type, result_type>()(
                static_cast<range_type>(val % modulus), _min);
        }
    }

    template<class Engine>
    result_type generate(Engine& eng, boost::mpl::false_) const
    {
        typedef typename Engine::result_type base_result;
        typedef typename boost::make_unsigned<result_type>::type range_type;
        range_type range = random::detail::subtract<result_type>()(_max, _min);
        base_result val = boost::uniform_01<base_result>()(eng);
        // what is the worst that can possibly happen here?
        // base_result may not be able to represent all the values in [0, range]
        // exactly.  If this happens, it will cause round off error and we
        // won't be able to produce all the values in the range.  We don't
        // care about this because the user has already told us not to by
        // using uniform_smallint.  However, we do need to be careful
        // to clamp the result, or floating point rounding can produce
        // an out of range result.
        range_type offset = static_cast<range_type>(val * (static_cast<base_result>(range) + 1));
        if(offset > range) return _max;
        return boost::random::detail::add<range_type, result_type>()(offset , _min);
    }
    // \endcond

    result_type _min;
    result_type _max;
};

} // namespace random

using random::uniform_smallint;

} // namespace boost

#endif // BOOST_RANDOM_UNIFORM_SMALLINT_HPP


#ifndef EFANNA_HASHING_INDEX_H_
#define EFANNA_HASHING_INDEX_H_

#include "algorithm/base_index.hpp"
#include <boost/dynamic_bitset.hpp>
#include <time.h>
//for Debug
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_map>
#include <map>
#include <sstream>
#include <set>
#include <bitset>
//#define MAX_RADIUS 6

namespace efanna{
struct HASHINGIndexParams : public IndexParams
{
	HASHINGIndexParams(int codelen, int tablelen, char*& BaseCodeFile, char*& QueryCodeFile, int index_method=1)
	{
		init_index_type = HASHING;
		ValueType len;
		len.int_val = codelen;
		extra_params.insert(std::make_pair("codelen",len));
		ValueType tablen;
		tablen.int_val = tablelen;
		extra_params.insert(std::make_pair("tablelen",tablen));
		ValueType bcf;
		bcf.str_pt = BaseCodeFile;
		extra_params.insert(std::make_pair("bcfile",bcf));
		ValueType qcf;
		qcf.str_pt = QueryCodeFile;
		extra_params.insert(std::make_pair("qcfile",qcf));
		ValueType method;
		method.int_val = index_method;
		extra_params.insert(std::make_pair("method",method));
	}
};

template <typename DataType>
class HASHINGIndex : public InitIndex<DataType>
{
public:

	typedef InitIndex<DataType> BaseClass;

	typedef std::vector<unsigned int> Code;
	typedef std::vector<Code> Codes;
	typedef std::unordered_map<unsigned int, std::vector<unsigned int> > HashBucket;
	typedef std::vector<HashBucket> HashTable;

	typedef std::vector<unsigned long> Code64;
	typedef std::vector<Code64> Codes64;
	typedef std::unordered_map<unsigned long, std::vector<unsigned int> > HashBucket64;
	typedef std::vector<HashBucket64> HashTable64;

	HASHINGIndex(const Matrix<DataType>& dataset, const Distance<DataType>* d, const IndexParams& params = HASHINGIndexParams(0,NULL,NULL)) :
		BaseClass(dataset,d,params)
	{

		ExtraParamsMap::const_iterator it = params_.extra_params.find("codelen");
		if(it != params_.extra_params.end()){
			codelength = (it->second).int_val;
			std::cout << "use  "<<codelength<< " bit code"<< std::endl;
		}
		else{
			std::cout << "error: no code length setting" << std::endl;
		}

		it = params_.extra_params.find("tablelen");
		if(it != params_.extra_params.end()){
			tablelen = (it->second).int_val;
			if(tablelen>64){
				std::cout<<"max table length: 54; " <<std::endl;
				tablelen = 64;
			}
		}
		else{
			std::cout << "error: no table lenth setting, will use 32 bits table" << std::endl;
			tablelen = 32;
		}
		std::cout << "use  "<<tablelen<< " bits tables"<< std::endl;

		if(tablelen<=36){
			radius = 11;
		}else if(tablelen<=44){
			radius = 9;
		}else if(tablelen<=52){
			radius = 8;
		}else{
			radius = 7;
		}
		std::cout << "search hamming radius "<<radius<< std::endl;

		it = params_.extra_params.find("bcfile");
		if(it != params_.extra_params.end()){
			char* fpath = (it->second).str_pt;
			std::string str(fpath);
			std::cout << "Loading base code from " << str << std::endl;

			LoadCode(fpath, BaseCodeOrig);
		}
		else{
			std::cout << "error: no base code file" << std::endl;
		}

		it = params_.extra_params.find("qcfile");
		if(it != params_.extra_params.end()){
			char* fpath = (it->second).str_pt;
			std::string str(fpath);
			std::cout << "Loading query code from " << str << std::endl;

			LoadCode(fpath, QueryCodeOrig);

		}
		else{
			std::cout << "error: no query code file" << std::endl;
		}

		it = params_.extra_params.find("method");
		if(it != params_.extra_params.end()){
			index_method = (it->second).int_val;
		}
		if(index_method > 1){
			index_method = 1;
		}

		if(tablelen > 16){
			upbits = 16;
		}else{
			upbits = tablelen - 2;
		}

	}

	/**
	 * Builds the index
	 */

	void LoadCode(char* filename, Codes& base){
		unsigned tableNum = codelength / 32;
		unsigned lastLen = codelength % 32;

		std::stringstream ss;
		unsigned j;
		for(j = 0; j < tableNum; j++){

			ss << filename << "_" << j+1 ;
			std::string codeFile;
			ss >> codeFile;
			ss.clear();

			std::ifstream in(codeFile.c_str(), std::ios::binary);
			if(!in.is_open()){std::cout<<"open file " << filename <<" error"<< std::endl;return;}

			int codeNum;
			in.read((char*)&codeNum,4);
			if (codeNum != 1){
				std::cout<<"codefile  "<< j << " error!"<<std::endl;
			}

			unsigned len;
			in.read((char*)&len,4);
			unsigned num;
			in.read((char*)&num,4);

			Code b;
			for(unsigned i = 0; i < num; i++){
				unsigned int codetmp;
				in.read((char*)&codetmp,4);
				b.push_back(codetmp);
			}
			base.push_back(b);
			in.close();
		}

		if(lastLen > 0){
			int shift = 32 - lastLen;
			ss << filename << "_" << j+1 ;
			std::string codeFile;
			ss >> codeFile;
			ss.clear();

			std::ifstream in(codeFile.c_str(), std::ios::binary);
			if(!in.is_open()){std::cout<<"open file " << filename <<" error"<< std::endl;return;}

			int codeNum;
			in.read((char*)&codeNum,4);
			if (codeNum != 1){
				std::cout<<"codefile  "<< j << " error!"<<std::endl;
			}

			unsigned len;
			in.read((char*)&len,4);
			unsigned num;
			in.read((char*)&num,4);

			Code b;
			for(unsigned i = 0; i < num; i++){
				unsigned int codetmp;
				in.read((char*)&codetmp,4);
				codetmp = codetmp >> shift;
				b.push_back(codetmp);
			}
			base.push_back(b);
			in.close();
		}

	}

	void ConvertCode(Codes& baseOrig, Codes& base, int tablelen){

		unsigned pNum = baseOrig[0].size();
		unsigned nTableOrig = baseOrig.size();

		for (unsigned i=0; i<nTableOrig; i++){
			Code table(pNum);
			std::fill(table.begin(), table.end(), 0);
			base.push_back(table);
		}

		int shift = 32 - tablelen;
		for (unsigned i = 0; i < pNum; i++) {
			for (unsigned j = 0; j < nTableOrig; j++) {
				base[j][i] = baseOrig[j][i] >> shift;
			}
		}
	}

	/*void ConvertCode3(Codes& baseOrig, Codes& base, int tablelen){

		unsigned pNum = baseOrig[0].size();
		unsigned nTableOrig = baseOrig.size();

		for (unsigned i=0; i<nTableOrig; i++){
			Code table(pNum);
			std::fill(table.begin(), table.end(), 0);
			base.push_back(table);
		}

		if(tablelen == 32){
			for (unsigned i = 0; i < pNum; i++) {
				for (unsigned j = 0; j < nTableOrig; j++) {
					base[j][i] = baseOrig[j][i];
				}
			}
		}else{
			for (unsigned i = 0; i < pNum; i++) {
				for (unsigned j = 0; j < nTableOrig; j++) {
					base[j][i] = baseOrig[j][i] & ((1 << tablelen) - 1);
				}
			}
		}
	}*/

	void ConvertCode1(Codes& baseOrig, Codes& base, int tablelen){

		unsigned pNum = baseOrig[0].size();
		unsigned nTableOrig = baseOrig.size();

		/*unsigned tableNum = codelength / tablelen;
		unsigned lastLen = codelength % tablelen;

		if(lastLen > 0){
			for (unsigned i=0; i<tableNum+1; i++){
				Code table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}else{
			for (unsigned i=0; i<tableNum; i++){
				Code table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}*/
		unsigned tableNum = codelength / tablelen;

		for (unsigned i=0; i<tableNum; i++){
			Code table(pNum);
			std::fill(table.begin(), table.end(), 0);
			base.push_back(table);
		}

		for (unsigned i = 0; i < pNum; i++) {
			unsigned int tableidx = 0;
			unsigned int codePre = 0;
			unsigned int lenPre = 0;
			unsigned int codeRemain = 0;
			unsigned int remain = 0;
			for (unsigned j = 0; j < nTableOrig-1; j++) {
				remain = 32;
				codeRemain = baseOrig[j][i];
				//std::cout <<"Orig"<<i<<" "<< std::bitset<32>(codeRemain) << std::endl;
				if(lenPre > 0){
					unsigned int need = tablelen-lenPre;
					base[tableidx][i] = codePre << need;
					remain = remain - need;
					base[tableidx][i] += codeRemain >> remain;
					tableidx ++;
					codeRemain = codeRemain & ((1 << remain) - 1);
				}
				while(remain >= (unsigned) tablelen){
					remain = remain - tablelen;
					base[tableidx][i] = codeRemain >> remain;
					//std::cout << std::bitset<32>(base[tableidx][i]) << std::endl;
					tableidx ++;
					codeRemain = codeRemain & ((1 << remain) - 1);
				}
				lenPre = remain;
				codePre = codeRemain;
			}

			codeRemain = baseOrig[nTableOrig-1][i];
			remain = 32;
			if(lenPre > 0){
				unsigned int need = tablelen-lenPre;
				base[tableidx][i] = codePre << need;
				remain = remain - need;
				base[tableidx][i] += codeRemain >> remain;
				tableidx ++;
				codeRemain = codeRemain & ((1 << remain) - 1);
			}
			while(remain >= (unsigned) tablelen){
				remain = remain - tablelen;
				base[tableidx][i] = codeRemain >> remain;
				tableidx ++;
				codeRemain = codeRemain & ((1 << remain) - 1);
			}

			/*remain = codelength - 32*(nTableOrig - 1);
			if(remain < (unsigned) tablelen){
				std::cout << "Not implemented! " << std::endl;
			}
			if (lenPre + remain > (unsigned) tablelen){
				if(lenPre > 0){
					unsigned int need = tablelen-lenPre;
					base[tableidx][i] = codePre << need;
					remain = remain - need;
					base[tableidx][i] += codeRemain >> remain;
					tableidx ++;
					codeRemain = codeRemain & ((1 << remain) - 1);
				}
				while(remain >= (unsigned) tablelen){
					remain = remain - tablelen;
					base[tableidx][i] = codeRemain >> remain;
					//std::cout << std::bitset<32>(base[tableidx][i]) << std::endl;
					tableidx ++;
					codeRemain = codeRemain & ((1 << remain) - 1);
				}
				if(remain > 0){
					codeRemain = baseOrig[nTableOrig-1][i];
					base[tableidx][i] = codeRemain & ((1<< tablelen)-1);
				}
			}else{
				if(lenPre > 0){
					unsigned int need = tablelen-lenPre;
					base[tableidx][i] = codePre << need;
				}
				base[tableidx][i] += codeRemain;
			}*/

		}
	}

	/*void ConvertCode2(Codes& baseOrig, Codes& base, int tablelen){

		unsigned pNum = baseOrig[0].size();
		unsigned nTableOrig = baseOrig.size();

		unsigned tableNum = codelength / tablelen;
		unsigned lastLen = codelength % tablelen;

		if(lastLen > 0){
			for (unsigned i=0; i<tableNum+1; i++){
				Code table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}else{
			for (unsigned i=0; i<tableNum; i++){
				Code table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}

		int shift = 32 - tablelen;
		for (unsigned i = 0; i < pNum; i++) {
			for (unsigned j = 0; j < nTableOrig; j++) {
				base[j][i] = baseOrig[j][i] >> shift;
			}
		}

		if (shift > 0){
			for (unsigned i = 0; i < pNum; i++) {
				unsigned tableidx = nTableOrig;
				unsigned codePre = 0;
				unsigned lenPre = 0;
				unsigned codeRemain = 0;
				unsigned remain = 0;
				unsigned j = 0;
				unsigned need = tablelen;
				while (j < nTableOrig-1) {
					if(lenPre > 0){
						need = need-lenPre;
						base[tableidx][i] = codePre << need;
						lenPre = 0;
					}
					remain = shift;
					codeRemain = baseOrig[j][i];
					codeRemain = codeRemain & ((1 << shift) - 1);
					j++;
					while(need > 0 && remain <= need && j < nTableOrig-1){
						need = need - remain;
						base[tableidx][i] += codeRemain << need;
						remain = shift;
						codeRemain = baseOrig[j][i];
						codeRemain = codeRemain & ((1 << shift) - 1);
						j++;
					}
					if(remain > need){
						lenPre = remain - need;
						if(need > 0){
							codePre = codeRemain & ((1 << lenPre) - 1);
							base[tableidx][i] += codeRemain >> lenPre;
						}else{
							codePre = codeRemain;
						}
						tableidx++;
						need = tablelen;
					}
				}

				if(lenPre > 0){
					need = tablelen-lenPre;
					base[tableidx][i] = codePre << need;
					lenPre = 0;
				}
				remain = shift;
				codeRemain = baseOrig[nTableOrig-1][i];
				codeRemain = codeRemain & ((1 << shift) - 1);
				if(remain >= need){
					remain = remain - need;
					base[tableidx][i] += codeRemain >> remain;
					tableidx++;
					need = tablelen;
					codeRemain = codeRemain & ((1 << remain) - 1);
				}

				if(remain > 0){
					codeRemain = baseOrig[nTableOrig-1][i];
					base[tableidx][i] = codeRemain & ((1 << tablelen) - 1);
				}
			}
		}

	}*/

	void ConvertCode64(Codes& baseOrig, Codes64& base, int tablelen){

		unsigned pNum = baseOrig[0].size();
		unsigned nTableOrig = baseOrig.size();

		unsigned tableNum = codelength / tablelen;
		for (unsigned i=0; i<tableNum; i++){
			Code64 table(pNum);
			std::fill(table.begin(), table.end(), 0);
			base.push_back(table);
		}

		/*unsigned tableNum = codelength / tablelen;
		unsigned lastLen = codelength % tablelen;

		if(lastLen > 0){
			for (unsigned i=0; i<tableNum+1; i++){
				Code64 table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}else{
			for (unsigned i=0; i<tableNum; i++){
				Code64 table(pNum);
				std::fill(table.begin(), table.end(), 0);
				base.push_back(table);
			}
		}*/

		for (unsigned i = 0; i < pNum; i++) {
		//for (unsigned i = 0; i < 1; i++) {
			unsigned tableidx = 0;
			unsigned long codePre = 0;
			unsigned lenPre = 0;
			unsigned long codeRemain = 0;
			unsigned remain = 0;
			unsigned j = 0;
			unsigned need = tablelen;
			while (j < nTableOrig && tableidx<tableNum) {
				if(lenPre > 0){
					//std::cout << "pre: "<< lenPre << std::endl;
					need = need-lenPre;
					//if(tableidx==tableNum) std::cout<<"tableidx out 1! "<< "j: "<< j<<std::endl;
					base[tableidx][i] = codePre << need;
					lenPre = 0;
				}
				remain = 32;
				//std::cout << "j: "<< j << std::endl;
				codeRemain = baseOrig[j][i];
				j++;
				while(need > 0 && remain <= need && j < nTableOrig){
					need = need - remain;
					//if(tableidx==tableNum) std::cout<<"tableidx out 2! "<< "j: "<< j<<std::endl;
					base[tableidx][i] += codeRemain << need;
					remain = 32;
					//std::cout << "j: "<< j << std::endl;
					codeRemain = baseOrig[j][i];
					j++;
				}
				if(remain > 0){
					if(remain > need){
						lenPre = remain - need;
						if(need > 0){
							codePre = codeRemain & ((1 << lenPre) - 1);
							//if(tableidx==tableNum) std::cout<<"tableidx out 3! "<< "j: "<< j<<std::endl;
							base[tableidx][i] += codeRemain >> lenPre;
						}else{
							codePre = codeRemain;
						}
						tableidx++;
						need = tablelen;
					}else{
						need = need - remain;
						//if(tableidx==tableNum) std::cout<<"tableidx out 4! "<< "j: "<< j<<std::endl;
						base[tableidx][i] += codeRemain << need;
					}
				}
			}

		}
	}

	void BuildHashTable32(int upbits, int lowbits, Codes& baseAll ,std::vector<HashTable>& tbAll){

		for(size_t h=0; h < baseAll.size(); h++){
			Code& base = baseAll[h];

			HashTable tb;
			for(int i = 0; i < (1 << upbits); i++){
				HashBucket emptyBucket;
				tb.push_back(emptyBucket);
			}

			for(size_t i = 0; i < base.size(); i ++){
				unsigned int idx1 = base[i] >> lowbits;
				unsigned int idx2 = base[i] & ((1 << lowbits) - 1);
				if(tb[idx1].find(idx2) != tb[idx1].end()){
					tb[idx1][idx2].push_back(i);
				}else{
					std::vector<unsigned int> v;
					v.push_back(i);
					tb[idx1].insert(make_pair(idx2,v));
				}
			}
			tbAll.push_back(tb);
		}
	}

	void BuildHashTable64(int upbits, int lowbits, Codes64& baseAll ,std::vector<HashTable64>& tbAll){

		unsigned long One = 1;

		for(size_t h=0; h < baseAll.size(); h++){
			Code64& base = baseAll[h];

			HashTable64 tb;
			for(int i = 0; i < (1 << upbits); i++){
				HashBucket64 emptyBucket;
				tb.push_back(emptyBucket);
			}

			for(size_t i = 0; i < base.size(); i++){
				unsigned int idx1 = base[i] >> lowbits;
				unsigned long idx2 = base[i] & ((One << lowbits) - 1);
				if(tb[idx1].find(idx2) != tb[idx1].end()){
					tb[idx1][idx2].push_back(i);
				}else{
					std::vector<unsigned int> v;
					v.push_back(i);
					tb[idx1].insert(make_pair(idx2,v));
				}
			}
			tbAll.push_back(tb);
		}
	}

	void generateMask32(){
		//i = 0 means the origin code
		HammingBallMask.push_back(0);
		HammingRadius.push_back(HammingBallMask.size());

		if(radius>0){
			//radius 1
			for(int i = 0; i < tablelen; i++){
				unsigned int mask = 1 << i;
				HammingBallMask.push_back(mask);
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>1){
			//radius 2
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					unsigned int mask = (1<<i) | (1<<j);
					HammingBallMask.push_back(mask);
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>2){
			//radius 3
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						unsigned int mask = (1<<i) | (1<<j) | (1<<k);
						HammingBallMask.push_back(mask);
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>3){
			//radius 4
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a);
							HammingBallMask.push_back(mask);
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>4){
			//radius 5
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b);
								HammingBallMask.push_back(mask);
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>5){
			//radius 6
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c);
									HammingBallMask.push_back(mask);
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>6){
			//radius 7
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d);
										HammingBallMask.push_back(mask);
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>7){
			//radius 8
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e);
											HammingBallMask.push_back(mask);
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>8){
			//radius 9
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											for(int f = e+1; f < tablelen; f++){
												unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e)| (1<<f);
												HammingBallMask.push_back(mask);
											}
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>9){
			//radius 10
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											for(int f = e+1; f < tablelen; f++){
												for(int g = f+1; g < tablelen; g++){
													unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e)| (1<<f)| (1<<g);
													HammingBallMask.push_back(mask);
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>10){
			//radius 11
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											for(int f = e+1; f < tablelen; f++){
												for(int g = f+1; g < tablelen; g++){
													for(int h = g+1; h < tablelen; h++){
														unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e)| (1<<f)| (1<<g)| (1<<h);
														HammingBallMask.push_back(mask);
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>11){
			//radius 12
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											for(int f = e+1; f < tablelen; f++){
												for(int g = f+1; g < tablelen; g++){
													for(int h = g+1; h < tablelen; h++){
														for(int l = h+1; h < tablelen; l++){
															unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e)| (1<<f)| (1<<g)| (1<<h)| (1<<l);
															HammingBallMask.push_back(mask);
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

		if(radius>12){
			//radius 13
			for(int i = 0; i < tablelen; i++){
				for(int j = i+1; j < tablelen; j++){
					for(int k = j+1; k < tablelen; k++){
						for(int a = k+1; a < tablelen; a++){
							for(int b = a+1; b < tablelen; b++){
								for(int c = b+1; c < tablelen; c++){
									for(int d = c+1; d < tablelen; d++){
										for(int e = d+1; e < tablelen; e++){
											for(int f = e+1; f < tablelen; f++){
												for(int g = f+1; g < tablelen; g++){
													for(int h = g+1; h < tablelen; h++){
														for(int l = h+1; h < tablelen; l++){
															for(int m = l+1; m < tablelen; m++){
																unsigned int mask = (1<<i) | (1<<j) | (1<<k)| (1<<a)| (1<<b)| (1<<c)| (1<<d)| (1<<e)| (1<<f)| (1<<g)| (1<<h)| (1<<l)| (1<<m);
																HammingBallMask.push_back(mask);
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			HammingRadius.push_back(HammingBallMask.size());
		}

	}

    void generateMask64(){
      //i = 0 means the origin code
      HammingBallMask64.push_back(0);
      HammingRadius.push_back(HammingBallMask64.size());

      unsigned long One = 1;
      if(radius>0){
        //radius 1
        for(int i = 0; i < tablelen; i++){
          unsigned long mask = One << i;
          HammingBallMask64.push_back(mask);
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>1){
        //radius 2
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            unsigned long mask = (One<<i) | (One<<j);
            HammingBallMask64.push_back(mask);
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>2){
        //radius 3
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              unsigned long mask = (One<<i) | (One<<j) | (One<<k);
              HammingBallMask64.push_back(mask);
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>3){
        //radius 4
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a);
                HammingBallMask64.push_back(mask);
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>4){
        //radius 5
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b);
                  HammingBallMask64.push_back(mask);
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>5){
        //radius 6
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c);
                    HammingBallMask64.push_back(mask);
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>6){
        //radius 7
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    for(int d = c+1; d < tablelen; d++){
                      unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c)| (One<<d);
                      HammingBallMask64.push_back(mask);
                    }
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>7){
        //radius 8
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    for(int d = c+1; d < tablelen; d++){
                      for(int e = d+1; e < tablelen; e++){
                        unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c)| (One<<d)| (One<<e);
                        HammingBallMask64.push_back(mask);
                      }
                    }
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>8){
        //radius 9
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    for(int d = c+1; d < tablelen; d++){
                      for(int e = d+1; e < tablelen; e++){
                        for(int f = e+1; f < tablelen; f++){
                          unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c)| (One<<d)| (One<<e)| (One<<f);
                          HammingBallMask64.push_back(mask);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>9){
        //radius 10
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    for(int d = c+1; d < tablelen; d++){
                      for(int e = d+1; e < tablelen; e++){
                        for(int f = e+1; f < tablelen; f++){
                          for(int g = f+1; g < tablelen; g++){
                            unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c)| (One<<d)| (One<<e)| (One<<f)| (One<<g);
                            HammingBallMask64.push_back(mask);
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

      if(radius>10){
        //radius 11
        for(int i = 0; i < tablelen; i++){
          for(int j = i+1; j < tablelen; j++){
            for(int k = j+1; k < tablelen; k++){
              for(int a = k+1; a < tablelen; a++){
                for(int b = a+1; b < tablelen; b++){
                  for(int c = b+1; c < tablelen; c++){
                    for(int d = c+1; d < tablelen; d++){
                      for(int e = d+1; e < tablelen; e++){
                        for(int f = e+1; f < tablelen; f++){
                          for(int g = f+1; g < tablelen; g++){
                            for(int h = g+1; h < tablelen; h++){
                              unsigned long mask = (One<<i) | (One<<j) | (One<<k)| (One<<a)| (One<<b)| (One<<c)| (One<<d)| (One<<e)| (One<<f)| (One<<g)| (One<<h);
                              HammingBallMask64.push_back(mask);
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        HammingRadius.push_back(HammingBallMask64.size());
      }

    }

	void printCode(Codes& base, unsigned codelength = 32){
		unsigned nTable = base.size();
		std::cout << nTable << " tables:" << std::endl;
		for(unsigned i=0; i<nTable;i++){
			unsigned codetmp = base[i][0];
			std::cout << (codetmp >> (codelength -1)) ;
			for(int j=(codelength-1);j>0;j--){
				codetmp = codetmp & ((1 << j) - 1);
				std::cout << (codetmp >> (j-1)) ;
			}
			std::cout <<  std::endl;
		}
	}

	void printCode64(Codes64& base, unsigned codelength = 64){
		unsigned nTable = base.size();
		std::cout << nTable << " tables:" << std::endl;
		unsigned long One = 1;
		for(unsigned i=0; i<nTable;i++){
			unsigned long codetmp = base[i][0];
			std::cout << (codetmp >> (codelength -1)) ;
			for(int j=(codelength-1);j>0;j--){
				codetmp = codetmp & ((One << j) - 1);
				std::cout << (codetmp >> (j-1));
			}
			std::cout <<  std::endl;
		}
	}

	void printCodeBitset(Codes& base){
		unsigned nTable = base.size();
		for(unsigned i =0; i<nTable;i++){
			unsigned codetmp = base[i][0];
			std::cout << std::bitset<32>(codetmp) << std::endl;
		}
	}

	void printCodeBitset64(Codes64& base){
		unsigned nTable = base.size();
		for(unsigned i =0; i<nTable;i++){
			unsigned long codetmp = base[i][0];
			std::cout << std::bitset<64>(codetmp) << std::endl;
		}
	}

	void buildIndexImpl()
	{
		std::cout<<"HASHING building hashing table"<<std::endl;

		if (tablelen <= 32 ){
			switch(index_method){
			case 0:
				ConvertCode(BaseCodeOrig, BaseCode, tablelen);
				ConvertCode(QueryCodeOrig, QueryCode, tablelen);
				break;
			case 1:
				ConvertCode1(BaseCodeOrig, BaseCode, tablelen);
				ConvertCode1(QueryCodeOrig, QueryCode, tablelen);
				break;
			default:
				std::cout<<"no such indexing method"<<std::endl;
			}

			BuildHashTable32(upbits, tablelen-upbits, BaseCode ,htb);
			generateMask32();

			/*std::cout << "BaseCodeOrig:" << std::endl;
			printCode(BaseCodeOrig);
			std::cout << "BaseCode:" << std::endl;
			printCode(BaseCode,tablelen);*/

		}else{

			ConvertCode64(BaseCodeOrig, BaseCode64, tablelen);
			//std::cout << "BaseCode converted!" << std::endl;
			ConvertCode64(QueryCodeOrig, QueryCode64, tablelen);
			//std::cout << "QueryCode converted!" << std::endl;

			BuildHashTable64(upbits, tablelen-upbits, BaseCode64 ,htb64);
			//std::cout << "hash table built!" << std::endl;
			generateMask64();
			//std::cout << "mask generated!" << std::endl;

			/*std::cout << "BaseCodeOrig:" << std::endl;
			printCode(BaseCodeOrig);
			std::cout << "BaseCode64:" << std::endl;
			printCode64(BaseCode64,tablelen);*/

		}

	}

	void locateNeighbors(const Matrix<DataType>& query){
		int lowbits = tablelen - upbits;

		unsigned int MaxCheck=HammingRadius[radius];
		std::cout<<"maxcheck : "<<MaxCheck<<std::endl;

		boost::dynamic_bitset<> tbflag(features_.get_rows(), false);

		VisitBucketNum.clear();
		VisitBucketNum.resize(radius+2);

		for(size_t cur = 0; cur < query.get_rows(); cur++){

			std::vector<unsigned int> pool(SP.search_init_num);
			unsigned int p = 0;
			tbflag.reset();

			unsigned int j = 0;
			for(; j < MaxCheck; j++){
				for(unsigned int h=0; h < QueryCode.size(); h++){
					unsigned int searchcode = QueryCode[h][cur] ^ HammingBallMask[j];
					unsigned int idx1 = searchcode >> lowbits;
					unsigned int idx2 = searchcode - (idx1 << lowbits);

					HashBucket::iterator bucket= htb[h][idx1].find(idx2);
					if(bucket != htb[h][idx1].end()){
						std::vector<unsigned int> vp = bucket->second;
						for(size_t k = 0; k < vp.size() && p < (unsigned int)SP.search_init_num; k++){
							if(tbflag.test(vp[k]))continue;

							tbflag.set(vp[k]);
							pool[p++]=(vp[k]);
						}
						if(p >= (unsigned int)SP.search_init_num)  break;
					}
					if(p >= (unsigned int)SP.search_init_num)  break;
				}
				if(p >= (unsigned int)SP.search_init_num)  break;
			}

			if(p < (unsigned int)SP.search_init_num){
				VisitBucketNum[radius+1]++;
			}else{
				for(int r=0;r<=radius;r++){
					if(j<=HammingRadius[r]){
						VisitBucketNum[r]++;
						break;
					}
				}
			}
		}
	}

	void getNeighbors(size_t K, const Matrix<DataType>& query){

		//return;

		if(gs.size() != features_.get_rows()){

			if((unsigned)SP.search_init_num < K) {
				std::cout << "# of located points not enough!"<<std::endl;
				return;
			}

			if (tablelen <= 32 ){
				getNeighbors32(K,query);
			}else if(tablelen <= 64 ){
				getNeighbors64(K,query);
			}else{
				std::cout<<"table length not supported yet!"<<std::endl;
			}

		}else{
			switch(SP.search_method){
			case 0:
				getNeighborsIEH32_kgraph(K, query);
				break;
			case 1:
				getNeighborsIEH32_nnexp(K, query);
				break;
			default:
				std::cout<<"no such searching method"<<std::endl;
			}
		}

	}

	void getNeighbors32(size_t K, const Matrix<DataType>& query){

		int lowbits = tablelen - upbits;

		unsigned int MaxCheck=HammingRadius[radius];
		std::cout<<"maxcheck : "<<MaxCheck<<std::endl;

		boost::dynamic_bitset<> tbflag(features_.get_rows(), false);

		nn_results.clear();

		VisitBucketNum.clear();
		VisitBucketNum.resize(radius+2);

		for(size_t cur = 0; cur < query.get_rows(); cur++){

			std::vector<unsigned int> pool(SP.search_init_num);
			unsigned int p = 0;
			tbflag.reset();

			unsigned int j = 0;
			for(; j < MaxCheck; j++){
				for(unsigned int h=0; h < QueryCode.size(); h++){
					unsigned int searchcode = QueryCode[h][cur] ^ HammingBallMask[j];
					unsigned int idx1 = searchcode >> lowbits;
					unsigned int idx2 = searchcode & ((1 << lowbits) - 1);

					HashBucket::iterator bucket= htb[h][idx1].find(idx2);
					if(bucket != htb[h][idx1].end()){
						std::vector<unsigned int> vp = bucket->second;
						for(size_t k = 0; k < vp.size() && p < (unsigned int)SP.search_init_num; k++){
							if(tbflag.test(vp[k]))continue;

							tbflag.set(vp[k]);
							pool[p++]=(vp[k]);
						}
						if(p >= (unsigned int)SP.search_init_num)  break;
					}
					if(p >= (unsigned int)SP.search_init_num)  break;
				}
				if(p >= (unsigned int)SP.search_init_num)  break;
			}

			if(p < (unsigned int)SP.search_init_num){
				VisitBucketNum[radius+1]++;
			}else{
				for(int r=0;r<=radius;r++){
					if(j<=HammingRadius[r]){
						VisitBucketNum[r]++;
						break;
					}
				}
			}

			if (p<K){
				int base_n = features_.get_rows();
				while(p < K){
					unsigned int nn = rand() % base_n;
					if(tbflag.test(nn)) continue;
					tbflag.set(nn);
					pool[p++] = (nn);
				}

			}

			std::vector<int> res;
			if((unsigned)SP.search_init_num == K){
				for(unsigned int j = 0; j < K; j++) res.push_back(pool[j]);
			}else{

				std::vector<std::pair<float,size_t>> result;
				for(unsigned int i=0; i<p;i++){
					result.push_back(std::make_pair(distance_->compare(query.get_row(cur), features_.get_row(pool[i]), features_.get_cols()),pool[i]));
				}
				std::partial_sort(result.begin(), result.begin() + K, result.end());
				for(unsigned int j = 0; j < K; j++) res.push_back(result[j].second);
			}
			nn_results.push_back(res);

		}
	}

	void getNeighbors64(size_t K, const Matrix<DataType>& query){
		int lowbits = tablelen - upbits;

		unsigned int MaxCheck=HammingRadius[radius];
		std::cout<<"maxcheck : "<<MaxCheck<<std::endl;

		boost::dynamic_bitset<> tbflag(features_.get_rows(), false);

		nn_results.clear();

		VisitBucketNum.clear();
		VisitBucketNum.resize(radius+2);

		for(size_t cur = 0; cur < query.get_rows(); cur++){

			std::vector<unsigned int> pool(SP.search_init_num);
			unsigned int p = 0;
			tbflag.reset();

			unsigned int j = 0;
			for(; j < MaxCheck; j++){
				for(unsigned int h=0; h < QueryCode64.size(); h++){
					unsigned long searchcode = QueryCode64[h][cur] ^ HammingBallMask64[j];
					unsigned int idx1 = searchcode >> lowbits;
					unsigned long idx2 = searchcode - (( unsigned long)idx1 << lowbits);

					HashBucket64::iterator bucket= htb64[h][idx1].find(idx2);
					if(bucket != htb64[h][idx1].end()){
						std::vector<unsigned int> vp = bucket->second;
						for(size_t k = 0; k < vp.size() && p < (unsigned int)SP.search_init_num; k++){
							if(tbflag.test(vp[k]))continue;

							tbflag.set(vp[k]);
							pool[p++]=(vp[k]);
						}
						if(p >= (unsigned int)SP.search_init_num)  break;
					}
					if(p >= (unsigned int)SP.search_init_num)  break;
				}
				if(p >= (unsigned int)SP.search_init_num)  break;
			}

			if(p < (unsigned int)SP.search_init_num){
				VisitBucketNum[radius+1]++;
			}else{
				for(int r=0;r<=radius;r++){
					if(j<=HammingRadius[r]){
						VisitBucketNum[r]++;
						break;
					}
				}
			}

			if (p<K){
				int base_n = features_.get_rows();
				while(p < K){
					unsigned int nn = rand() % base_n;
					if(tbflag.test(nn)) continue;
					tbflag.set(nn);
					pool[p++] = (nn);
				}

			}

			std::vector<int> res;
			if((unsigned)SP.search_init_num == K){
				for(unsigned int j = 0; j < K; j++) res.push_back(pool[j]);
			}else{
				std::vector<std::pair<float,size_t>> result;
				for(unsigned int i=0; i<p;i++){
					result.push_back(std::make_pair(distance_->compare(query.get_row(cur), features_.get_row(pool[i]), features_.get_cols()),pool[i]));
				}
				std::partial_sort(result.begin(), result.begin() + K, result.end());
				for(unsigned int j = 0; j < K; j++) res.push_back(result[j].second);
			}
			nn_results.push_back(res);
		}
	}

	void getNeighborsIEH32_nnexp(size_t K, const Matrix<DataType>& query){
		int lowbits = tablelen - upbits;

		unsigned int MaxCheck=HammingRadius[radius];
		std::cout<<"maxcheck : "<<MaxCheck<<std::endl;

		boost::dynamic_bitset<> tbflag(features_.get_rows(), false);
		nn_results.clear();

		VisitBucketNum.clear();
		VisitBucketNum.resize(radius+2);

		for(size_t cur = 0; cur < query.get_rows(); cur++){
			tbflag.reset();

			std::vector<int> pool(SP.search_init_num);
			unsigned int p = 0;

			unsigned int j = 0;
			for(; j < MaxCheck; j++){
				for(size_t h=0; h < QueryCode.size(); h++){
					unsigned int searchcode = QueryCode[h][cur] ^ HammingBallMask[j];
					unsigned int idx1 = searchcode >> lowbits;
					unsigned int idx2 = searchcode & ((1 << lowbits) - 1);

					HashBucket::iterator bucket= htb[h][idx1].find(idx2);
					if(bucket != htb[h][idx1].end()){
						std::vector<unsigned int> vp = bucket->second;
						for(size_t k = 0; k < vp.size() && p < (unsigned int)SP.search_init_num; k++){
							if(tbflag.test(vp[k]))continue;

							tbflag.set(vp[k]);
							pool[p++]=(vp[k]);
						}
						if(p >= (unsigned int)SP.search_init_num)  break;
					}
					if(p >= (unsigned int)SP.search_init_num)  break;
				}
				if(p >= (unsigned int)SP.search_init_num)  break;
			}

			if(p < (unsigned int)SP.search_init_num){
				VisitBucketNum[radius+1]++;
			}else{
				for(int r=0;r<=radius;r++){
					if(j<=HammingRadius[r]){
						VisitBucketNum[r]++;
						break;
					}
				}
			}

			int base_n = features_.get_rows();
			while(p < (unsigned int)SP.search_init_num){
				unsigned int nn = rand() % base_n;
				if(tbflag.test(nn)) continue;
				tbflag.set(nn);
				pool[p++] = (nn);
			}

			InitIndex<DataType>::nnExpansion(K, query.get_row(cur), pool, tbflag);
			nn_results.push_back(pool);
		}
	}

	void getNeighborsIEH32_kgraph(size_t K, const Matrix<DataType>& query){
		int lowbits = tablelen - upbits;

		unsigned int MaxCheck=HammingRadius[radius];
		std::cout<<"maxcheck : "<<MaxCheck<<std::endl;

		nn_results.clear();
		boost::dynamic_bitset<> tbflag(features_.get_rows(), false);

		bool bSorted = true;
		unsigned pool_size = SP.search_epoches * SP.extend_to;
		if (pool_size >= (unsigned)SP.search_init_num){
			SP.search_init_num = pool_size;
			bSorted = false;
		}

		VisitBucketNum.clear();
		VisitBucketNum.resize(radius+2);

		for(size_t cur = 0; cur < query.get_rows(); cur++){

			tbflag.reset();
			std::vector<unsigned int> pool(SP.search_init_num);
			unsigned int p = 0;

			unsigned int j = 0;
			for(; j < MaxCheck; j++){
				for(size_t h=0; h < QueryCode.size(); h++){
					unsigned int searchcode = QueryCode[h][cur] ^ HammingBallMask[j];
					unsigned int idx1 = searchcode >> lowbits;
					unsigned int idx2 = searchcode & ((1 << lowbits) - 1);

					HashBucket::iterator bucket= htb[h][idx1].find(idx2);
					if(bucket != htb[h][idx1].end()){
						std::vector<unsigned int> vp = bucket->second;
						for(size_t k = 0; k < vp.size() && p < (unsigned int)SP.search_init_num; k++){
							if(tbflag.test(vp[k]))continue;

							tbflag.set(vp[k]);
							pool[p++]=(vp[k]);
						}
						if(p >= (unsigned int)SP.search_init_num)  break;
					}
					if(p >= (unsigned int)SP.search_init_num)  break;
				}
				if(p >= (unsigned int)SP.search_init_num)  break;
			}

			if(p < (unsigned int)SP.search_init_num){
				VisitBucketNum[radius+1]++;
			}else{
				for(int r=0;r<=radius;r++){
					if(j<=HammingRadius[r]){
						VisitBucketNum[r]++;
						break;
					}
				}
			}

			int base_n = features_.get_rows();
			while(p < (unsigned int)SP.search_init_num){
				unsigned int nn = rand() % base_n;
				if(tbflag.test(nn)) continue;
				tbflag.set(nn);
				pool[p++] = (nn);
			}

			std::vector<std::pair<float,size_t>> result;
			for(unsigned int i=0; i<pool.size();i++){
				result.push_back(std::make_pair(distance_->compare(query.get_row(cur), features_.get_row(pool[i]), features_.get_cols()),pool[i]));
			}
			if(bSorted){
				std::partial_sort(result.begin(), result.begin() + pool_size, result.end());
				result.resize(pool_size);
			}

			std::vector<int> res;
			InitIndex<DataType>::nnExpansion_kgraph(K,  query.get_row(cur), result, res, bSorted);

			nn_results.push_back(res);

		}
	}

	void outputVisitBucketNum(){
		unsigned i=0;
		std::cout<< "Radius " << i <<" bucket num: "<<HammingRadius[i]<< " points num: "<< VisitBucketNum[i]<<std::endl;

		for(i=1; i<HammingRadius.size();i++){
			std::cout<< "Radius " << i <<" bucket num: "<<HammingRadius[i] - HammingRadius[i-1]<< " points num: "<< VisitBucketNum[i]<<std::endl;
		}
		std::cout<< "Radius larger, points num: " << VisitBucketNum[i]<<std::endl;
	}

	void loadGraph(char* filename){
		std::ifstream in(filename,std::ios::binary);
		in.seekg(0,std::ios::end);
		std::ios::pos_type ss = in.tellg();
		size_t fsize = (size_t)ss;
		int dim;
		in.seekg(0,std::ios::beg);
		in.read((char*)&dim, sizeof(int));
		size_t num = fsize / (dim+1) / 4;
		std::cout<<"load g "<<num<<" "<<dim<< std::endl;
		in.seekg(0,std::ios::beg);
		gs.clear();
		for(size_t i = 0; i < num; i++){
			std::vector<unsigned> heap;
			in.read((char*)&dim, sizeof(int));
			for(int j =0; j < dim; j++){
				unsigned id;
				in.read((char*)&id, sizeof(int));
				heap.push_back(id);
			}
			gs.push_back(heap);
		}
		in.close();
	}

protected:
	USING_BASECLASS_SYMBOLS

	int codelength;
	int tablelen;
	int radius;
	int upbits;
	unsigned index_method = 0;

	Codes BaseCodeOrig;
	Codes QueryCodeOrig;

	Codes BaseCode;
	Codes QueryCode;
	std::vector<HashTable> htb;
	std::vector<unsigned int> HammingBallMask;

	Codes64 BaseCode64;
	Codes64 QueryCode64;
	std::vector<HashTable64> htb64;
	std::vector<unsigned long> HammingBallMask64;

	std::vector<unsigned int> HammingRadius;

	// for statistic info
	std::vector<unsigned int> VisitBucketNum;

};
}
#endif


/*
 * This is part of the fl library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2015 Max Planck Society,
 * 				 Autonomous Motion Department,
 * 			     Institute for Intelligent Systems
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file joint_sensor_iid_test.cpp
 * \date August 2015
 * \author Jan Issac (jan.issac@gmail.com)
 */

#include <gtest/gtest.h>
#include "../../typecast.hpp"

#include <Eigen/Dense>

#include <cmath>
#include <iostream>

#include <fl/util/types.hpp>
#include <fl/model/sensor/linear_gaussian_sensor.hpp>
#include <fl/model/sensor/joint_sensor_iid.hpp>

template <typename TestType>
class JointSensorIidTest:
    public testing::Test
{
public:
    enum : signed int
    {
        LocalStateDim = TestType::Parameter::StateDim,
        LocalObsrvDim = TestType::Parameter::ObsrvDim,
        LocalNoiseDim = TestType::Parameter::ObsrvDim,
        Count = TestType::Parameter::LocalModelCount,

        LocalStateSize = fl::TestSize<LocalStateDim, TestType>::Value,
        LocalObsrvSize = fl::TestSize<LocalObsrvDim, TestType>::Value,
        LocalNoiseSize = fl::TestSize<LocalNoiseDim, TestType>::Value,
        Size = fl::TestSize<Count, TestType>::Value
    };

    // Local model types
    typedef Eigen::Matrix<fl::Real, LocalStateSize, 1> LocalState;
    typedef Eigen::Matrix<fl::Real, LocalObsrvSize, 1> LocalObsrv;
    typedef fl::LinearGaussianSensor<LocalObsrv, LocalState> LocalModel;
    typedef typename LocalModel::Noise LocalNoise;

    // Joint model types
    typedef fl::JointSensor<
                fl::MultipleOf<LocalModel, Size>
            > JointModel;

    typedef typename JointModel::Obsrv Obsrv;
    typedef typename JointModel::Noise Noise;
    typedef typename JointModel::State State;

    enum : signed int
    {
        StateDim = LocalStateDim,
        ObsrvDim = LocalObsrvDim * Count,
        NoiseDim = LocalNoiseDim * Count,

        StateSize = fl::TestSize<StateDim, TestType>::Value,
        ObsrvSize = fl::TestSize<ObsrvDim, TestType>::Value,
        NoiseSize = fl::TestSize<NoiseDim, TestType>::Value
    };

    JointSensorIidTest()
        : joint_model(create_joint_sensor(create_local_model())),
          noise_normal_gaussian(NoiseDim)
    { }

    State zero_state() { return State::Zero(LocalStateDim); }
    Obsrv zero_obsrv() { return Obsrv::Zero(LocalObsrvDim); }
    Noise zero_noise() { return Noise::Zero(LocalNoiseDim); }

    State rand_state() { return State::Random(LocalStateDim); }
    Obsrv rand_obsrv() { return Obsrv::Random(LocalObsrvDim); }
    Noise rand_noise() { return noise_normal_gaussian.sample(); }

    LocalModel create_local_model()
    {
        return LocalModel(LocalObsrvDim, LocalStateDim);
    }

    template <typename LocalSensor>
    JointModel create_joint_sensor(LocalSensor&& local_model)
    {
        return JointModel(local_model, Count);
    }

    void init_dimension_test()
    {
        EXPECT_EQ(joint_model.obsrv_dimension(), ObsrvDim);
        EXPECT_EQ(joint_model.noise_dimension(), NoiseDim);
        EXPECT_EQ(joint_model.state_dimension(), StateDim);
    }

    void observation()
    {
        auto y = joint_model.observation(rand_state(), rand_noise());

        EXPECT_EQ(y.size(), ObsrvDim);
        for (int i = 0; i < y.size(); ++i)
        {
            EXPECT_NE(y[i], 0.);
        }
    }

    void nested_joint_model()
    {
        // nested joint model type taking multiple of JointModel
        typedef fl::JointSensor<
                    fl::MultipleOf<JointModel, Size>
                >  OuterJointModel;

        auto nested_joint =
            OuterJointModel(
                create_joint_sensor(create_local_model()), Count);

        EXPECT_EQ(nested_joint.obsrv_dimension(), ObsrvDim * Count);
        EXPECT_EQ(nested_joint.noise_dimension(), NoiseDim * Count);
        EXPECT_EQ(nested_joint.state_dimension(), StateDim);
    }

//    void probability()
//    {
//        auto x = State::Random(StateDim).eval();
//        auto y = Obsrv::Random(ObsrvDim).eval();

//        auto body_prob = body_model.probability(y, x);
//        auto tail_prob = tail_model.probability(y, x);
//        auto body_tail_prob = body_tail_model.probability(y, x);

//        ASSERT_DOUBLE_EQ((body_prob + tail_prob)/2., body_tail_prob);
//    }

//    void log_probability()
//    {
//        auto x = State::Random(StateDim).eval();
//        auto y = Obsrv::Random(ObsrvDim).eval();

//        auto body_prob = body_model.probability(y, x);
//        auto tail_prob = tail_model.probability(y, x);
//        auto body_tail_log_prob = body_tail_model.log_probability(y, x);

//        ASSERT_DOUBLE_EQ(std::log((body_prob + tail_prob)/2.),
//                         body_tail_log_prob);
//    }

protected:
    JointModel joint_model;
    fl::StandardGaussian<Noise> noise_normal_gaussian;
};

template <int ModelCount, int ObsrvDimension, int StateDimension>
struct Dimensions
{
    enum: signed int
    {
        ObsrvDim = ObsrvDimension,
        StateDim = StateDimension,
        LocalModelCount = ModelCount
    };
};

typedef ::testing::Types<
            fl::StaticTest<Dimensions<1, 1, 1>>,
            fl::StaticTest<Dimensions<2, 1, 1>>,
            fl::StaticTest<Dimensions<10, 1, 1>>,
            fl::StaticTest<Dimensions<1, 2, 1>>,
            fl::StaticTest<Dimensions<2, 3, 2>>,
            fl::StaticTest<Dimensions<10, 11, 1>>,
            fl::DynamicTest<Dimensions<1, 1, 1>>,
            fl::DynamicTest<Dimensions<2, 1, 1>>,
            fl::DynamicTest<Dimensions<10, 1, 1>>,
            fl::DynamicTest<Dimensions<1, 2, 1>>,
            fl::DynamicTest<Dimensions<2, 3, 2>>,
            fl::DynamicTest<Dimensions<10, 11, 1>>
        > TestTypes;

TYPED_TEST_CASE(JointSensorIidTest, TestTypes);

TYPED_TEST(JointSensorIidTest, init_dimension)
{
    TestFixture::init_dimension_test();
}

TYPED_TEST(JointSensorIidTest, observation)
{
    TestFixture::observation();
}

TYPED_TEST(JointSensorIidTest, nested_joint_model)
{
    TestFixture::nested_joint_model();
}


/*
 * Tree.cpp
 *
 *  Created on: Sep 20, 2012
 *      Author: Chris
 *
 *   TODO: pruning of tree
 */

#include <iostream>
#include <cmath>
#include <boost/make_shared.hpp>

#include "LUtils.h"
#include "IOHelper.h"
#include "Tree.h"
#include "Node.h"

//Binary tree
Tree::Tree(int n, bool p, bool r, double w, int seed) :
		min_node(n), probability(p), regression(r), w1(w), rseed(seed) {
	root = boost::make_shared<Node>(0, 0.0);
	tree_size = 0;
	max_depth = 100;
	tnodecount = 0;
	entropy_loss = false;
	rng.setSeed(rseed);
}

//most important information
void Tree::printInfo() {
	cout << endl;
	if (regression) {
		cout << "REGRESSION Tree";
	} else {
		cout << "CLASSIFICATION Tree";
	}
	if (probability)
		cout << " - Returning class probabilities";
	cout << " - Minimum node size: " << min_node << endl;
}

//in future use interface mlmodel
//void Tree::train(const Eigen::MatrixXd &X, const Eigen::VectorXd &y, const bool verbose){};
//Eigen::VectorXd Tree::predict(const Eigen::MatrixXd &Xtest, const bool verbose){};

// creates recursively the tree, uses subset of features
void Tree::train(DataFrame &dataframe, const vector<int> &featList,
		const int nrFeat, const bool verbose) {
	//select features randomly
	if (verbose) cout<<"Training tree with random seed:"<<rng.seed<<endl;
	vector<int> featsubset = LUtils::sample(rng, nrFeat, dataframe.nrcols - 1,
			false);
	DataFrame::FeatureResult featResult = dataframe.findBestFeature(featsubset,
			entropy_loss);
	//Create a new root node
	root->feature = featResult.opt_feat;
	root->splitvalue = featResult.opt_split;
	root->impurity = featResult.loss;
	root->nameFeature = dataframe.header.at(featResult.opt_feat);
	root->nrsamples = dataframe.nrrows;
	createBranch(root, dataframe, nrFeat, verbose);
}

void Tree::train(DataFrame &dataframe, const bool verbose) {
	vector<int> featList(dataframe.nrcols - 1);
	for (unsigned i = 0; i < featList.size(); ++i) {
		featList[i] = i;
	}
	train(dataframe, featList, featList.size(), verbose);
}

//recursively insert nodes
void Tree::createBranch(boost::shared_ptr<Node> parentNode, DataFrame &dfsplit,
		const int nrFeat, bool verbose) {

	vector<int> featsubset = LUtils::sample(rng, nrFeat, dfsplit.nrcols - 1,
			false);

	if (verbose) {
		cout << "Feature subset: ";
		for (unsigned i = 0; i < featsubset.size(); ++i) {
			cout << " " << dfsplit.header[featsubset[i]];
		}
		cout << endl;
	}

	DataFrame leftDF;
	DataFrame rightDF;
	dfsplit.splitFrame(parentNode->splitvalue, parentNode->feature, leftDF,
			rightDF);
	tree_size++;
	//LEFT BRANCH
	if (verbose && leftDF.nrrows > 0) {
		cout << "...Creating left branch: Feature: "
				<< dfsplit.header[parentNode->feature] << " Value:"
				<< parentNode->splitvalue << " n:" << leftDF.nrrows
				<< " with prediction:" << leftDF.cm << endl;
		//leftDF.printSummary();
	}
	if (leftDF.nrrows == 0) {
		//happens if one of the nodes is "practically" pure
		if (verbose) {
			cout << "No data in left node, right node:" << rightDF.nrrows
					<< endl;
			cout << "Left node: Parent node is terminal." << endl;
		}
		parentNode->isTerminal = true;
		tnodecount++;
		return;
	} else if (leftDF.nrrows <= min_node || parentNode->depth + 1 > max_depth
			|| leftDF.distinct[leftDF.classCol] < 2) {
		if (verbose)
			cout << "Terminal node, cm: " << leftDF.cm << endl;
		boost::shared_ptr<Node> left = boost::make_shared<Node>(
				parentNode->depth + 1, leftDF.cm);
		left->isTerminal = true;
		left->nrsamples = leftDF.nrrows;
		parentNode->left = left;
		tnodecount++;
	} else {
		DataFrame::FeatureResult featResulta = leftDF.findBestFeature(
				featsubset, entropy_loss);
		boost::shared_ptr<Node> left = boost::make_shared<Node>(
				featResulta.opt_feat, featResulta.opt_split, featResulta.loss,
				parentNode->depth + 1, leftDF.header[featResulta.opt_feat],
				leftDF.nrrows, leftDF.cm);
		parentNode->left = left;
		createBranch(left, leftDF, nrFeat, verbose);
	}

	//RIGHT BRANCH
	if (verbose && rightDF.nrrows > 0) {
		cout << "...Creating right branch: Feature: "
				<< dfsplit.header[parentNode->feature] << " Value:"
				<< parentNode->splitvalue << " n:" << rightDF.nrrows
				<< " with prediction:" << rightDF.cm << endl;
		//rightDF.printSummary();
	}
	if (rightDF.nrrows == 0) {
		//happens if one of the nodes is "practically" pure
		if (verbose) {
			cout << "No data in right node,  left node:" << leftDF.nrrows
					<< endl;
			cout << "Right node: Parent node is terminal." << endl;
		}
		parentNode->isTerminal = true;
		tnodecount++;
		return;
	} else if (rightDF.nrrows <= min_node || parentNode->depth + 1 > max_depth
			|| rightDF.distinct[rightDF.classCol] < 2) {
		if (verbose)
			cout << "Terminal node, cm: " << rightDF.cm << endl;
		boost::shared_ptr<Node> right = boost::make_shared<Node>(
				parentNode->depth + 1, rightDF.cm);
		right->isTerminal = true;

		right->nrsamples = rightDF.nrrows;
		parentNode->right = right;
		tnodecount++;
	} else {
		DataFrame::FeatureResult featResultb = rightDF.findBestFeature(
				featsubset, entropy_loss);
		if (verbose)
			cout << "Terminal node, cm: " << rightDF.cm << endl;
		boost::shared_ptr<Node> right = boost::make_shared<Node>(
				featResultb.opt_feat, featResultb.opt_split, featResultb.loss,
				parentNode->depth + 1, rightDF.header[featResultb.opt_feat],
				rightDF.nrrows, rightDF.cm);
		parentNode->right = right;
		createBranch(right, rightDF, nrFeat, verbose);

	}
	//if we reach this point, we should return
	return;
}

void Tree::showTree(const bool verbose) {
	cout << endl << "###Printing Tree Structure" << endl;
	root->showChildren(verbose);
}

string Tree::tree2string() {
	//cout << endl << "###Printing Simpified Tree Structure" << endl;
	stringstream ss;
	ss << "# rftree"<<endl;
	ss << "2 " << root->feature << " " << root->splitvalue << endl;
	vector<boost::shared_ptr<Node> > parentnodes;
	parentnodes.push_back(root);
	//we have to re-sort the binary tree
	for (int i = 1; i < 5; ++i) {
		vector<boost::shared_ptr<Node> > actualnodes;
		int n_nodes = pow(2, i);
		//cout << "LEVEL:" << i << " NR NODES:" << n_nodes << endl;
		for (int j = 0; j < n_nodes; ++j) {
			boost::shared_ptr<Node> parent = parentnodes[j / 2];
			if (parent->isTerminal) {
							continue;
						}
			boost::shared_ptr<Node> node;
			int id = pow(2, i) + j;
			if (j % 2 == 0) {
				node = parent->left;
			} else {
				node = parent->right;
			}
			if (!node->isTerminal) {
				int leftid = 2 * id;
				ss << leftid << " " << node->feature << " "
						<< node->splitvalue << endl;
				actualnodes.push_back(node);
			} else {
				ss << "0 NA " << node->cm << endl;
				actualnodes.push_back(node);
			}
		}
		parentnodes = actualnodes;
	}
	return ss.str();
}

//prediction for external data
Eigen::VectorXd Tree::predict(DataFrame &testSet, const bool verbose) {
//starting at root level, then call makePredictions recursively
	double pi = 0.0;
	Eigen::VectorXd p(testSet.nrrows);
	for (int obs = 0; obs < testSet.nrrows; obs++) {
		//in some rare case we do have a terminal node at the 0th level
		if (root->isTerminal) {
			pi = root->cm;
		} else if (testSet.matrix(obs, root->feature) < root->splitvalue) {
			//makePrediction leftDF
			pi = makePrediction(testSet, root->left, obs, verbose);
		} else {
			//makePrediction rightDF
			pi = makePrediction(testSet, root->right, obs, verbose);
		}
		if (!probability && !regression) {
			//cout<<"Rounding "<<pi<<" to "<<LUtils::round(pi)<<endl;
			pi = LUtils::round(pi);
		}
		p(obs) = pi;
		if (verbose) {
			cout << "p(" << obs << "): " << p(obs) << " order:"
					<< testSet.order[obs] <<endl;
		}

	}
//original order for p
	Eigen::VectorXd tmp(testSet.nrrows);
	int idx_orig = 0;
	for (int i = 0; i < testSet.nrrows; i++) {
		idx_orig = testSet.order.at(i);
		tmp(idx_orig) = p(i);
	}
	for (int i = 0; i < testSet.nrrows; i++) {
		p(i) = tmp(i);
	}
//after prediction we have to re-establish the original order
	testSet.restoreOrder();
	if (verbose)
		cout << "###Tree size:" << tree_size + 1 << " nodes." << endl;
	return p;

}

//make prediction for a certain row, recursive until terminal node is reached
double Tree::makePrediction(const DataFrame &testset,
		const boost::shared_ptr<Node> localNode, const int obs,
		const bool verbose) {
	double t = 0.0;
	if (localNode->isTerminal) {
		if (verbose) {
			cout << " cm(Node" << localNode->nodeID << "):";
		}
		return localNode->cm;
	}
	if (testset.matrix(obs, localNode->feature) < localNode->splitvalue) {
		//makePrediction leftDF
		t = makePrediction(testset, localNode->left, obs, verbose);
	} else {
		//makePrediction rightDF
		t = makePrediction(testset, localNode->right, obs, verbose);
	}
//if we reach this end we go upwards again...
	return t;
}

Tree::~Tree() {
}


/*
 * for_each_pair.hpp
 *
 *  Created on: Aug 28, 2009
 *      Author: stewie
 */

#ifndef FOR_EACH_PAIR_HPP_
#define FOR_EACH_PAIR_HPP_

#include <boost/next_prior.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>

namespace bn {

namespace util {

/**
 * Applies binary function @a f on all the 2-combinations without
 * repetition of elements in the given ranges.
 *
 * This function calls @a f \f${n \choose 2} = \frac{n^2 - n}{2}\f$ times,
 * where @e n is the length of the range. The behaviour of this template
 * function is equivalent to:
 * @code
 * for (; first != last; ++first) {
 * 	ForwardIterator temp = first;
 * 	for (ForwardIterator it = ++temp; it != last; ++it)
 * 		f(*first, *it);
 * 	}
 * return f;
 * @endcode
 * @param first forward iterator to the initial position of the range
 * @param last forward iterator to the final position of the range
 * @param f binary function object to apply
 * @return the function object
 *
 * @see std::for_each() for a similar iteration algorithm.
 */
template<class ForwardIterator, class Function> Function for_each_pair(
		ForwardIterator first, ForwardIterator last, Function f) {
	for (; first != last; ++first)
		for (ForwardIterator it = boost::next(first); it != last; ++it)
			f(*first, *it);
	return f;
}

template<class ForwardRange, class Function> Function for_each_pair(
		ForwardRange& r, Function f) {
	return for_each_pair(boost::begin(r), boost::end(r), f);
}

template<class ForwardRange, class Function> Function for_each_pair(
		const ForwardRange& r, Function f) {
	return for_each_pair(boost::begin(r), boost::end(r), f);
}

}

}

#endif /* FOR_EACH_PAIR_HPP_ */


#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/lexical_cast.hpp>
using namespace boost::multiprecision;
using boost::lexical_cast;
using namespace std;
int main() {
    string S; cin >> S;
    vector<string> v;
    int n = S.length() - 1;
    cpp_int ans = 0;
    for (int bit = 0; bit < (1 << n); bit++) {
        string t = ""; t += S[0];
        for (int i = 0; i < n; i++) {
            if (bit & (1 << i)) {
                t += '+';
                t += S[1 + i];
            }
            else t += S[1 + i];
        }
        v.push_back(t);
    }
    for (auto x : v) {
        x += '+';
        string str = "";
        for (int i = 0; i < x.length(); i++) {
            if (x[i] == '+') {
                if (str.length() == 1) ans += (int)str[0] - '1' + 1;
                else ans += lexical_cast<cpp_int>(str);
                str = "";
            }
            else str += x[i];
        }
    }
    cout << ans << endl;
}


// Boost.Geometry Index
// Unit Test

// Copyright (c) 2015 Adam Wulkiewicz, Lodz, Poland.

// 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)

#include <rtree/test_rtree.hpp>

#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/polygon.hpp>

template <typename Params>
void test_rtree(unsigned vcount)
{
    typedef bg::model::point<int, 1, bg::cs::cartesian> point_t;

    bgi::rtree<point_t, Params> rt;

    BOOST_CHECK(rt.remove(point_t(0)) == 0);

    for ( unsigned i = 0 ; i < vcount ; ++i )
    {
        rt.insert(point_t(static_cast<int>(i)));
    }

    BOOST_CHECK(rt.size() == vcount);
    BOOST_CHECK(rt.count(point_t(vcount / 2)) == 1);

    for ( unsigned i = 0 ; i < vcount + 3 ; ++i )
    {
        rt.remove(point_t((i + 3) % vcount));
    }

    BOOST_CHECK(rt.size() == 0);
    BOOST_CHECK(rt.count(point_t(vcount / 2)) == 0);

    for ( unsigned i = 0 ; i < vcount ; ++i )
    {
        rt.insert(point_t((i + 5) % vcount));
    }

    BOOST_CHECK(rt.size() == vcount);
    BOOST_CHECK(rt.count(point_t(vcount / 2)) == 1);

    for ( unsigned i = 0 ; i < vcount + 3 ; ++i )
    {
        rt.remove(point_t((i + 7) % vcount));
    }

    BOOST_CHECK(rt.size() == 0);
    BOOST_CHECK(rt.count(point_t(vcount / 2)) == 0);
}

template <int Max, int Min>
void test_rtree_all()
{
    int pow = Max;
    for (int l = 0 ; l < 3 ; ++l)
    {
        pow *= Max;
        int vcount = (pow * 8) / 10;

        //std::cout << Max << " " << Min << " " << vcount << std::endl;

        test_rtree< bgi::linear<Max, Min> >(vcount);
        test_rtree< bgi::quadratic<Max, Min> >(vcount);
        test_rtree< bgi::rstar<Max, Min> >(vcount);
    }
}

int test_main(int, char* [])
{
    test_rtree_all<2, 1>();
    test_rtree_all<4, 1>();
    test_rtree_all<4, 2>();
    test_rtree_all<5, 3>();

    return 0;
}