//---------------------------------------------------------------------------//
//!
//! \file   Utility_CosineInterpolationPolicy_def.hpp
//! \author Luke Kersting
//! \brief  Interpolation policy struct definitions
//!
//---------------------------------------------------------------------------//

#ifndef UTILITY_COSINE_INTERPOLATION_POLICY_DEF_HPP
#define UTILITY_COSINE_INTERPOLATION_POLICY_DEF_HPP

// Std Lib Includes
#include <cmath>

// Boost Includes
#include <boost/mpl/or.hpp>

// FRENSIE Includes
#include "Utility_QuantityTraits.hpp"
#include "Utility_DesignByContract.hpp"

namespace Utility{

// Interpolate between two points
template<bool use_nudge>
template<typename IndepType, typename DepCosineType>
inline DepCosineType LogCosLogHelper<use_nudge>::interpolate( const IndepType indep_var_0,
                                          const IndepType indep_var_1,
                                          const IndepType indep_var,
                                          const DepCosineType raw_dep_var_0,
                                          const DepCosineType raw_dep_var_1 )
{
  // The IndepType must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return CosNudgeHelper<use_nudge>::convertToCosineVar( dep_var_0*
            pow((dep_var_1/dep_var_0),
            log(indep_var/indep_var_0)/log(indep_var_1/indep_var_0)) );
}

// Interpolate between two points using the indep variable ratio (beta)
template<bool use_nudge>
template<typename T, typename DepCosineType>
inline DepCosineType LogCosLogHelper<use_nudge>::interpolate( const T beta,
                                          const DepCosineType raw_dep_var_0,
                                          const DepCosineType raw_dep_var_1 )
{
  // The IndepType must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<T>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( beta >= QuantityTraits<T>::zero() );
  testPrecondition( beta <= QuantityTraits<T>::one() );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return CosNudgeHelper<use_nudge>::convertToCosineVar( dep_var_0*pow((dep_var_1/dep_var_0),beta) );
}

// Interpolate between two points and return the processed value
template<bool use_nudge>
template<typename IndepType, typename DepCosineType>
inline typename QuantityTraits<DepCosineType>::RawType
LogCosLogHelper<use_nudge>::interpolateAndProcess( const IndepType indep_var_0,
                                  const IndepType indep_var_1,
                                  const IndepType indep_var,
                                  const DepCosineType raw_dep_var_0,
                                  const DepCosineType raw_dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return log( getRawQuantity(dep_var_0) ) + log ( dep_var_1/dep_var_0 )*
         log( indep_var/indep_var_0 )/log( indep_var_1/indep_var_0 );
}

// Process the independent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogCosLogHelper<use_nudge>::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogCosLogHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );

  return log( getRawQuantity(indep_var) );
}

// Process the dependent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogCosLogHelper<use_nudge>::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogCosLogHelper<use_nudge>::isDepVarInValidRange( dep_var ) );

  return log( CosNudgeHelper<use_nudge>::convertFromCosineVar( getRawQuantity(dep_var) ) );
}

// Recover the processed independent value
template<bool use_nudge>
template<typename T>
inline T LogCosLogHelper<use_nudge>::recoverProcessedIndepVar( const T processed_indep_var )
{
  return exp( processed_indep_var );
}

// Recover the processed dependent value
template<bool use_nudge>
template<typename T>
inline T LogCosLogHelper<use_nudge>::recoverProcessedDepVar( const T processed_dep_var )
{
  return CosNudgeHelper<use_nudge>::convertToCosineVar( exp( processed_dep_var ) );
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<bool use_nudge>
template<typename T>
inline bool LogCosLogHelper<use_nudge>::isIndepVarInValidRange( const T indep_var )
{
  return indep_var > QuantityTraits<T>::zero();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogCosLogHelper<true>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         dep_var <= QuantityTraits<T>::one();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogCosLogHelper<false>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         dep_var < QuantityTraits<T>::one();
}

// Get the interpolation type
inline InterpolationType LogCosLog::getInterpolationType()
{
  return LOGCOSLOG_INTERPOLATION;
}

// The name of the policy
inline const std::string LogCosLog::name()
{
  return "LogCosLog";
}

// Get the interpolation type
inline InterpolationType NudgedLogCosLog::getInterpolationType()
{
  return NUDGEDLOGCOSLOG_INTERPOLATION;
}

// The name of the policy
inline const std::string NudgedLogCosLog::name()
{
  return "NudgedLogCosLog";
}

// Calculate the unit base grid length (L)
/*! \details For LinLogCos and LogLogCos interpolation types the grid length
 * that is calculated is not a traditional length. It is the negative of the
 * distance between the processed upper independent value and the processed
 * lower independent value. This is why any units associated with the
 * independent grid limits are stripped away. Due to conversion of the
 * independent values from a cosine (mu) to a delta cosine ( 1 - mu ) + nudge
 * for LogLogCos and LinLogCos, the negative of the grid length is used to
 * ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LogLogCosHelper<false> >::calculateUnitBaseGridLength(
                                       const IndepType grid_lower_indep_value,
                                       const IndepType grid_upper_indep_value )
{
  // Make sure the grid is valid
  testPrecondition( grid_lower_indep_value <= grid_upper_indep_value );
  testPrecondition( LinLogCosHelper<false>::isIndepVarInValidRange( grid_lower_indep_value ) );

  return ThisType::calculateUnitBaseGridLengthProcessed(
          LinLogCosHelper<false>::processIndepVar( grid_upper_indep_value ),
          LinLogCosHelper<false>::processIndepVar( grid_lower_indep_value ) );
}

// Calculate the unit base grid length (L)
/*! \details For LinLogCos and LogLogCos interpolation types the grid length
 * that is calculated is not a traditional length. It is the negative of the
 * distance between the processed upper independent value and the processed
 * lower independent value. This is why any units associated with the
 * independent grid limits are stripped away. Due to conversion of the
 * independent values from a cosine (mu) to a delta cosine ( 1 - mu ) + nudge
 * for LogLogCos and LinLogCos, the negative of the grid length is used to
 * ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LogLogCosHelper<true> >::calculateUnitBaseGridLength(
                                       const IndepType grid_lower_indep_value,
                                       const IndepType grid_upper_indep_value )
{
  // Make sure the grid is valid
  testPrecondition( grid_lower_indep_value <= grid_upper_indep_value );
  testPrecondition( LinLogCosHelper<true>::isIndepVarInValidRange( grid_lower_indep_value ) );

  return ThisType::calculateUnitBaseGridLengthProcessed(
          LinLogCosHelper<true>::processIndepVar( grid_upper_indep_value ),
          LinLogCosHelper<true>::processIndepVar( grid_lower_indep_value ) );
}

// Calculate the unit base independent variable (eta)
/*! \details The independent grid length is calculated using the
 * processed independent grid limits. It has been found that a tolerance of
 * 1e-3 works best for most applications. Due to conversion of the independent
 * values from a cosine (mu) to a delta cosine ( 1 - mu ) + nudge for LogLogCos
 * and LinLogCos, the negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LogLogCosHelper<false> >::calculateUnitBaseIndepVar(
          const IndepType indep_var,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the intermediate grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LogLogCosHelper<false>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the intermediate grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length > 0.0 );
  // Make sure the independent variable is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLogCosHelper<false>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var >=
                    ThisType::calculateFuzzyLowerBound( indep_var_min, tol ) );
  remember( typename QuantityTraits<IndepType>::RawType test_difference =
            LogLogCosHelper<false>::processIndepVar(indep_var) -
            LogLogCosHelper<false>::processIndepVar(indep_var_min) );

  testPrecondition( test_difference <= ThisType::calculateFuzzyUpperBound(
                                                    indep_grid_length, tol ) );

  return calculateUnitBaseIndepVarProcessed(
                       LogLogCosHelper<false>::processIndepVar(indep_var_min),
                       LogLogCosHelper<false>::processIndepVar(indep_var),
                       indep_grid_length,
                       tol );
}

// Calculate the unit base independent variable (eta)
/*! \details The independent grid length is calculated using the
 * processed independent grid limits. It has been found that a tolerance of
 * 1e-3 works best for most applications. Due to conversion of the independent
 * values from a cosine (mu) to a delta cosine ( 1 - mu ) + nudge for LogLogCos
 * and LinLogCos, the negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LogLogCosHelper<true> >::calculateUnitBaseIndepVar(
          const IndepType indep_var,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the intermediate grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LogLogCosHelper<true>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the intermediate grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length > 0.0 );
  // Make sure the independent variable is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogLogCosHelper<true>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var >=
                    ThisType::calculateFuzzyLowerBound( indep_var_min, tol ) );
  remember( typename QuantityTraits<IndepType>::RawType test_difference =
            LogLogCosHelper<true>::processIndepVar(indep_var) -
            LogLogCosHelper<true>::processIndepVar(indep_var_min) );

  testPrecondition( test_difference <= ThisType::calculateFuzzyUpperBound(
                                                    indep_grid_length, tol ) );

  return calculateUnitBaseIndepVarProcessed(
                       LogLogCosHelper<true>::processIndepVar(indep_var_min),
                       LogLogCosHelper<true>::processIndepVar(indep_var),
                       indep_grid_length,
                       tol );
}

// Calculate the independent variable (from eta)
/*! \details It has been found that a tolerance of 1e-3 works best for most
 * applications. Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge for LogLogCos and LinLogCos, the
 * negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline IndepType
InterpolationHelper<LogLogCosHelper<false> >::calculateIndepVar(
          const typename QuantityTraits<IndepType>::RawType eta,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the eta value is valid
  testPrecondition( eta >= 0.0 );
  testPrecondition( eta <= 1.0 );
  // Make sure the grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LogLogCosHelper<false>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length >= 0.0 );

  IndepType grid_indep_var( QuantityTraits<IndepType>::initializeQuantity(
    LogLogCosHelper<false>::recoverProcessedIndepVar(
                    LogLogCosHelper<false>::processIndepVar( indep_var_min ) -
                    indep_grid_length*eta ) ) );

  // Check for rounding errors
  if( grid_indep_var < indep_var_min &&
      grid_indep_var >= ThisType::calculateFuzzyLowerBound(indep_var_min, tol))
    grid_indep_var = indep_var_min;

  // Make sure the calculated independent variable is valid
  testPostcondition( grid_indep_var >= indep_var_min );

  return grid_indep_var;
}

// Calculate the independent variable (from eta)
/*! \details It has been found that a tolerance of 1e-3 works best for most
 * applications. Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge for LogLogCos and LinLogCos, the
 * negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline IndepType
InterpolationHelper<LogLogCosHelper<true> >::calculateIndepVar(
          const typename QuantityTraits<IndepType>::RawType eta,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the eta value is valid
  testPrecondition( eta >= 0.0 );
  testPrecondition( eta <= 1.0 );
  // Make sure the grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LogLogCosHelper<true>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length >= 0.0 );

  IndepType grid_indep_var( QuantityTraits<IndepType>::initializeQuantity(
    LogLogCosHelper<true>::recoverProcessedIndepVar(
                    LogLogCosHelper<true>::processIndepVar( indep_var_min ) -
                    indep_grid_length*eta ) ) );

  // Check for rounding errors
  if( grid_indep_var < indep_var_min &&
      grid_indep_var >= ThisType::calculateFuzzyLowerBound(indep_var_min, tol))
    grid_indep_var = indep_var_min;

  // Make sure the calculated independent variable is valid
  testPostcondition( grid_indep_var >= indep_var_min );

  return grid_indep_var;
}

// Interpolate between two points
template<bool use_nudge>
template<typename IndepCosineType, typename DepType>
inline DepType LogLogCosHelper<use_nudge>::interpolate( const IndepCosineType raw_indep_var_0,
                                       const IndepCosineType raw_indep_var_1,
                                       const IndepCosineType raw_indep_var,
                                       const DepType dep_var_0,
                                       const DepType dep_var_1 )
{
  // The IndepCosineType must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepCosineType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_0 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_1 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var ) );
  testPrecondition( raw_indep_var_0 < raw_indep_var_1 );
  testPrecondition( raw_indep_var >= raw_indep_var_0 );
  testPrecondition( raw_indep_var <= raw_indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_1 ) );

  IndepCosineType indep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_0);
  IndepCosineType indep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_1);
  IndepCosineType indep_var = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var);

  return dep_var_0*
         pow((dep_var_1/dep_var_0),
         log(indep_var/indep_var_0)/log(indep_var_1/indep_var_0));
}

// Interpolate between two processed points
/*! \details Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge , the absolute value of the difference
 * between the processed_indep_var and processed_indep_var_0 is used for
 * LinLogCos and LogLogCos.
 */
template<>
template<typename T>
T InterpolationHelper<LogLogCosHelper<false> >::interpolate( const T processed_indep_var_0,
                                               const T processed_indep_var,
                                               const T processed_dep_var_0,
                                               const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_indep_var_0 ) );
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_indep_var ) );
  testPrecondition( processed_indep_var <= processed_indep_var_0 );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_slope ) );

  return LogLogCosHelper<false>::recoverProcessedDepVar(
               processed_dep_var_0 +
               processed_slope*(processed_indep_var_0 - processed_indep_var) );
}

// Interpolate between two processed points
/*! \details Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge , the absolute value of the difference
 * between the processed_indep_var and processed_indep_var_0 is used for
 * LinLogCos and LogLogCos.
 */
template<>
template<typename T>
T InterpolationHelper<LogLogCosHelper<true> >::interpolate( const T processed_indep_var_0,
                                               const T processed_indep_var,
                                               const T processed_dep_var_0,
                                               const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_indep_var_0 ) );
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_indep_var ) );
  testPrecondition( processed_indep_var <= processed_indep_var_0 );
  // Make sure the processed dependent variable is valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_slope ) );

  return LogLogCosHelper<true>::recoverProcessedDepVar(
               processed_dep_var_0 +
               processed_slope*(processed_indep_var_0 - processed_indep_var) );
}

// Interpolate between two points using the indep variable ratio (beta)
template<bool use_nudge>
template<typename T, typename DepType>
inline DepType LogLogCosHelper<use_nudge>::interpolate( const T beta,
                                       const DepType dep_var_0,
                                       const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<T>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( beta >= QuantityTraits<T>::zero() );
  testPrecondition( beta <= QuantityTraits<T>::one() );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_1 ) );

  return dep_var_0*pow((dep_var_1/dep_var_0),beta);
}

// Interpolate between two points and return the processed value
template<bool use_nudge>
template<typename IndepCosineType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LogLogCosHelper<use_nudge>::interpolateAndProcess( const IndepCosineType raw_indep_var_0,
                                  const IndepCosineType raw_indep_var_1,
                                  const IndepCosineType raw_indep_var,
                                  const DepType dep_var_0,
                                  const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepCosineType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_0 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_1 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var ) );
  testPrecondition( raw_indep_var_0 < raw_indep_var_1 );
  testPrecondition( raw_indep_var >= raw_indep_var_0 );
  testPrecondition( raw_indep_var <= raw_indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_1 ) );

  IndepCosineType indep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_0);
  IndepCosineType indep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_1);
  IndepCosineType indep_var = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var);

  return log( getRawQuantity(dep_var_0) ) + log ( dep_var_1/dep_var_0 )*
         log( indep_var_0/indep_var )/log( indep_var_0/indep_var_1 );
}

// Process the independent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLogCosHelper<use_nudge>::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLogCosHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );

  return log( CosNudgeHelper<use_nudge>::convertFromCosineVar( getRawQuantity(indep_var) ) );
}

// Process the dependent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogLogCosHelper<use_nudge>::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var ) );

  return log( getRawQuantity(dep_var) );
}

// Recover the processed independent value
template<bool use_nudge>
template<typename T>
inline T LogLogCosHelper<use_nudge>::recoverProcessedIndepVar( const T processed_indep_var )
{
  return CosNudgeHelper<use_nudge>::convertToCosineVar( exp( processed_indep_var ) );
}

// Recover the processed dependent value
template<bool use_nudge>
template<typename T>
inline T LogLogCosHelper<use_nudge>::recoverProcessedDepVar( const T processed_dep_var )
{
  return exp( processed_dep_var );
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogLogCosHelper<false>::isIndepVarInValidRange( const T indep_var )
{
  return indep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         indep_var < QuantityTraits<T>::one();
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogLogCosHelper<true>::isIndepVarInValidRange( const T indep_var )
{
  return indep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         indep_var <= QuantityTraits<T>::one();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<bool use_nudge>
template<typename T>
inline bool LogLogCosHelper<use_nudge>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var > QuantityTraits<T>::zero();
}

// Get the interpolation type
inline InterpolationType LogLogCos::getInterpolationType()
{
  return LOGLOGCOS_INTERPOLATION;
}

// The name of the policy
inline const std::string LogLogCos::name()
{
  return "LogLogCos";
}

// Get the interpolation type
inline InterpolationType LogNudgedLogCos::getInterpolationType()
{
  return LOGNUDGEDLOGCOS_INTERPOLATION;
}

// The name of the policy
inline const std::string LogNudgedLogCos::name()
{
  return "LogNudgedLogCos";
}

// Interpolate between two points
template<bool use_nudge>
template<typename IndepType, typename DepCosineType>
inline DepCosineType LogCosLinHelper<use_nudge>::interpolate( const IndepType indep_var_0,
                                          const IndepType indep_var_1,
                                          const IndepType indep_var,
                                          const DepCosineType raw_dep_var_0,
                                          const DepCosineType raw_dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return CosNudgeHelper<use_nudge>::convertToCosineVar( dep_var_0*pow((dep_var_1/dep_var_0),
            (indep_var-indep_var_0)/(indep_var_1-indep_var_0)) );
}

// Interpolate between two points using the indep variable ratio (beta)
template<bool use_nudge>
template<typename T, typename DepCosineType>
inline DepCosineType LogCosLinHelper<use_nudge>::interpolate( const T beta,
                                          const DepCosineType raw_dep_var_0,
                                          const DepCosineType raw_dep_var_1 )
{
  // The IndepType must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<T>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( beta >= QuantityTraits<T>::zero() );
  testPrecondition( beta <= QuantityTraits<T>::one() );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return CosNudgeHelper<use_nudge>::convertToCosineVar( dep_var_0*pow((dep_var_1/dep_var_0),beta) );
}

// Interpolate between two points and return the processed value
template<bool use_nudge>
template<typename IndepType, typename DepCosineType>
inline typename QuantityTraits<DepCosineType>::RawType
LogCosLinHelper<use_nudge>::interpolateAndProcess( const IndepType indep_var_0,
                                  const IndepType indep_var_1,
                                  const IndepType indep_var,
                                  const DepCosineType raw_dep_var_0,
                                  const DepCosineType raw_dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepCosineType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var_0 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var_1 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var_0 < indep_var_1 );
  testPrecondition( indep_var >= indep_var_0 );
  testPrecondition( indep_var <= indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepCosineType>::isnaninf( raw_dep_var_1 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_0 ) );
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( raw_dep_var_1 ) );

  DepCosineType dep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_0);
  DepCosineType dep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_dep_var_1);

  return log( getRawQuantity(dep_var_0) ) + log( dep_var_1/dep_var_0 )*
         (indep_var-indep_var_0)/(indep_var_1-indep_var_0);
}

// Process the independent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogCosLinHelper<use_nudge>::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogCosLinHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );

  return getRawQuantity(indep_var);
}

// Process the dependent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LogCosLinHelper<use_nudge>::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LogCosLinHelper<use_nudge>::isDepVarInValidRange( dep_var ) );

  return log( CosNudgeHelper<use_nudge>::convertFromCosineVar( getRawQuantity(dep_var) ) );
}

// Recover the processed independent value
template<bool use_nudge>
template<typename T>
inline T LogCosLinHelper<use_nudge>::recoverProcessedIndepVar( const T processed_indep_var )
{
  return processed_indep_var;
}

// Recover the processed dependent value
template<bool use_nudge>
template<typename T>
inline T LogCosLinHelper<use_nudge>::recoverProcessedDepVar( const T processed_dep_var )
{
  return CosNudgeHelper<use_nudge>::convertToCosineVar( exp( processed_dep_var ) );
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<bool use_nudge>
template<typename T>
inline bool LogCosLinHelper<use_nudge>::isIndepVarInValidRange( const T indep_var )
{
  return true;
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogCosLinHelper<false>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         dep_var < QuantityTraits<T>::one();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LogCosLinHelper<true>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return dep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         dep_var <= QuantityTraits<T>::one();
}

// Get the interpolation type
inline InterpolationType LogCosLin::getInterpolationType()
{
  return LOGCOSLIN_INTERPOLATION;
}

// The name of the policy
inline const std::string LogCosLin::name()
{
  return "LogCosLin";
}

// Get the interpolation type
inline InterpolationType NudgedLogCosLin::getInterpolationType()
{
  return NUDGEDLOGCOSLIN_INTERPOLATION;
}

// The name of the policy
inline const std::string NudgedLogCosLin::name()
{
  return "NudgedLogCosLin";
}

// Calculate the unit base grid length (L)
/*! \details For LinLogCos and LogLogCos interpolation types the grid length
 * that is calculated is not a traditional length. It is the negative of the
 * distance between the processed upper independent value and the processed
 * lower independent value. This is why any units associated with the
 * independent grid limits are stripped away. Due to conversion of the
 * independent values from a cosine (mu) to a delta cosine ( 1 - mu ) +nudge for
 * LogLogCos and LinLogCos, the negative of the grid length is used to ensure a
 * positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LinLogCosHelper<false> >::calculateUnitBaseGridLength(
                                       const IndepType grid_lower_indep_value,
                                       const IndepType grid_upper_indep_value )
{
  // Make sure the grid is valid
  testPrecondition( grid_lower_indep_value <= grid_upper_indep_value );
  testPrecondition( LinLogCosHelper<false>::isIndepVarInValidRange( grid_lower_indep_value ) );

  return ThisType::calculateUnitBaseGridLengthProcessed(
          LinLogCosHelper<false>::processIndepVar( grid_upper_indep_value ),
          LinLogCosHelper<false>::processIndepVar( grid_lower_indep_value ) );
}

// Calculate the unit base grid length (L)
/*! \details For LinLogCos and LogLogCos interpolation types the grid length
 * that is calculated is not a traditional length. It is the negative of the
 * distance between the processed upper independent value and the processed
 * lower independent value. This is why any units associated with the
 * independent grid limits are stripped away. Due to conversion of the
 * independent values from a cosine (mu) to a delta cosine ( 1 - mu ) +nudge for
 * LogLogCos and LinLogCos, the negative of the grid length is used to ensure a
 * positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LinLogCosHelper<true> >::calculateUnitBaseGridLength(
                                       const IndepType grid_lower_indep_value,
                                       const IndepType grid_upper_indep_value )
{
  // Make sure the grid is valid
  testPrecondition( grid_lower_indep_value <= grid_upper_indep_value );
  testPrecondition( LinLogCosHelper<true>::isIndepVarInValidRange( grid_lower_indep_value ) );

  return ThisType::calculateUnitBaseGridLengthProcessed(
          LinLogCosHelper<true>::processIndepVar( grid_upper_indep_value ),
          LinLogCosHelper<true>::processIndepVar( grid_lower_indep_value ) );
}

// Calculate the unit base independent variable (eta)
/*! \details The independent grid length is calculated using the
 * processed independent grid limits. It has been found that a tolerance of
 * 1e-3 works best for most applications. Due to conversion of the independent
 * values from a cosine (mu) to a delta cosine ( 1 - mu ) +nudge for LogLogCos
 * and LinLogCos, the negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LinLogCosHelper<false> >::calculateUnitBaseIndepVar(
          const IndepType indep_var,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the intermediate grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LinLogCosHelper<false>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the intermediate grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length > 0.0 );
  // Make sure the independent variable is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LinLogCosHelper<false>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var >=
                    ThisType::calculateFuzzyLowerBound( indep_var_min, tol ) );
  remember( typename QuantityTraits<IndepType>::RawType test_difference =
            LinLogCosHelper<false>::processIndepVar(indep_var) -
            LinLogCosHelper<false>::processIndepVar(indep_var_min) );

  testPrecondition( test_difference <= ThisType::calculateFuzzyUpperBound(
                                                    indep_grid_length, tol ) );

  return calculateUnitBaseIndepVarProcessed(
                       LinLogCosHelper<false>::processIndepVar(indep_var_min),
                       LinLogCosHelper<false>::processIndepVar(indep_var),
                       indep_grid_length,
                       tol );
}

// Calculate the unit base independent variable (eta)
/*! \details The independent grid length is calculated using the
 * processed independent grid limits. It has been found that a tolerance of
 * 1e-3 works best for most applications. Due to conversion of the independent
 * values from a cosine (mu) to a delta cosine ( 1 - mu ) +nudge for LogLogCos
 * and LinLogCos, the negative of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline typename QuantityTraits<IndepType>::RawType
InterpolationHelper<LinLogCosHelper<true> >::calculateUnitBaseIndepVar(
          const IndepType indep_var,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the intermediate grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LinLogCosHelper<true>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the intermediate grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length > 0.0 );
  // Make sure the independent variable is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var ) );
  testPrecondition( LinLogCosHelper<true>::isIndepVarInValidRange( indep_var ) );
  testPrecondition( indep_var >=
                    ThisType::calculateFuzzyLowerBound( indep_var_min, tol ) );
  remember( typename QuantityTraits<IndepType>::RawType test_difference =
            LinLogCosHelper<true>::processIndepVar(indep_var) -
            LinLogCosHelper<true>::processIndepVar(indep_var_min) );

  testPrecondition( test_difference <= ThisType::calculateFuzzyUpperBound(
                                                    indep_grid_length, tol ) );

  return calculateUnitBaseIndepVarProcessed(
                       LinLogCosHelper<true>::processIndepVar(indep_var_min),
                       LinLogCosHelper<true>::processIndepVar(indep_var),
                       indep_grid_length,
                       tol );
}

// Calculate the independent variable (from eta)
/*! \details It has been found that a tolerance of 1e-3 works best for most
 * applications. Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) +nudge for LogLogCos and LinLogCos, the negative
 * of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline IndepType
InterpolationHelper<LinLogCosHelper<false> >::calculateIndepVar(
          const typename QuantityTraits<IndepType>::RawType eta,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the eta value is valid
  testPrecondition( eta >= 0.0 );
  testPrecondition( eta <= 1.0 );
  // Make sure the grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LinLogCosHelper<false>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length >= 0.0 );

  IndepType grid_indep_var( QuantityTraits<IndepType>::initializeQuantity(
    LinLogCosHelper<false>::recoverProcessedIndepVar(
                    LinLogCosHelper<false>::processIndepVar( indep_var_min ) -
                    indep_grid_length*eta ) ) );

  // Check for rounding errors
  if( grid_indep_var < indep_var_min &&
      grid_indep_var >= ThisType::calculateFuzzyLowerBound(indep_var_min, tol))
    grid_indep_var = indep_var_min;

  // Make sure the calculated independent variable is valid
  testPostcondition( grid_indep_var >= indep_var_min );

  return grid_indep_var;
}

// Calculate the independent variable (from eta)
/*! \details It has been found that a tolerance of 1e-3 works best for most
 * applications. Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) +nudge for LogLogCos and LinLogCos, the negative
 * of eta is used to ensure a positive value.
 */
template<>
template<typename IndepType>
inline IndepType
InterpolationHelper<LinLogCosHelper<true> >::calculateIndepVar(
          const typename QuantityTraits<IndepType>::RawType eta,
          const IndepType indep_var_min,
          const typename QuantityTraits<IndepType>::RawType indep_grid_length,
          const double tol )
{
  // Make sure the eta value is valid
  testPrecondition( eta >= 0.0 );
  testPrecondition( eta <= 1.0 );
  // Make sure the grid min indep var is valid
  testPrecondition( !QuantityTraits<IndepType>::isnaninf( indep_var_min ) );
  testPrecondition( LinLogCosHelper<true>::isIndepVarInValidRange( indep_var_min ) );
  // Make sure the grid length is valid
  testPrecondition( !QuantityTraits<typename QuantityTraits<IndepType>::RawType>::isnaninf( indep_grid_length ) );
  testPrecondition( indep_grid_length >= 0.0 );

  IndepType grid_indep_var( QuantityTraits<IndepType>::initializeQuantity(
    LinLogCosHelper<true>::recoverProcessedIndepVar(
                    LinLogCosHelper<true>::processIndepVar( indep_var_min ) -
                    indep_grid_length*eta ) ) );

  // Check for rounding errors
  if( grid_indep_var < indep_var_min &&
      grid_indep_var >= ThisType::calculateFuzzyLowerBound(indep_var_min, tol))
    grid_indep_var = indep_var_min;

  // Make sure the calculated independent variable is valid
  testPostcondition( grid_indep_var >= indep_var_min );

  return grid_indep_var;
}

// Interpolate between two points
template<bool use_nudge>
template<typename IndepCosineType, typename DepType>
inline DepType LinLogCosHelper<use_nudge>::interpolate( const IndepCosineType raw_indep_var_0,
                                       const IndepCosineType raw_indep_var_1,
                                       const IndepCosineType raw_indep_var,
                                       const DepType dep_var_0,
                                       const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<IndepCosineType>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_0 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var_1 ) );
  testPrecondition( !QuantityTraits<IndepCosineType>::isnaninf( raw_indep_var ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_0 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var_1 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isIndepVarInValidRange( raw_indep_var ) );
  testPrecondition( raw_indep_var_0 < raw_indep_var_1 );
  testPrecondition( raw_indep_var >= raw_indep_var_0 );
  testPrecondition( raw_indep_var <= raw_indep_var_1 );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_1 ) );

  // Convert cosine variables
  IndepCosineType indep_var_0 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_0);
  IndepCosineType indep_var_1 = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var_1);
  IndepCosineType indep_var = CosNudgeHelper<use_nudge>::convertFromCosineVar(raw_indep_var);

  DepType term_2( (dep_var_1 - dep_var_0)*log(indep_var/indep_var_0)/
                  log(indep_var_1/indep_var_0) );

  return dep_var_0 + term_2;
}

// Interpolate between two processed points
/*! \details Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge , the absolute value of the difference
 * between the processed_indep_var and processed_indep_var_0 is used for
 * LinLogCos and LogLogCos.
 */
template<>
template<typename T>
T InterpolationHelper<LinLogCosHelper<false> >::interpolate( const T processed_indep_var_0,
                                               const T processed_indep_var,
                                               const T processed_dep_var_0,
                                               const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_indep_var_0 ) );
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_indep_var ) );
  testPrecondition( processed_indep_var <= processed_indep_var_0 );
  // Make sure the processed dependent variable is valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !Utility::QuantityTraits<T>::isnaninf( processed_slope ) );

  return LinLogCosHelper<false>::recoverProcessedDepVar(
               processed_dep_var_0 +
               processed_slope*(processed_indep_var_0 - processed_indep_var) );
}

// Interpolate between two processed points
/*! \details Due to conversion of the independent values from a cosine (mu)
 * to a delta cosine ( 1 - mu ) + nudge , the absolute value of the difference
 * between the processed_indep_var and processed_indep_var_0 is used for
 * LinLogCos and LogLogCos.
 */
template<>
template<typename T>
T InterpolationHelper<LinLogCosHelper<true> >::interpolate( const T processed_indep_var_0,
                                               const T processed_indep_var,
                                               const T processed_dep_var_0,
                                               const T processed_slope )
{
  // T must be a floating point type
  testStaticPrecondition( (boost::is_floating_point<T>::value) );
  // Make sure the processed independent variables are valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_indep_var_0 ) );
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_indep_var ) );
  testPrecondition( processed_indep_var <= processed_indep_var_0 );
  // Make sure the processed dependent variable is valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_dep_var_0 ) );
  // Make sure that the slope is valid
  testPrecondition( !QuantityTraits<T>::isnaninf( processed_slope ) );

  return LinLogCosHelper<true>::recoverProcessedDepVar(
               processed_dep_var_0 +
               processed_slope*(processed_indep_var_0 - processed_indep_var) );
}

// Interpolate between two points using the indep variable ratio (beta)
template<bool use_nudge>
template<typename T, typename DepType>
inline DepType LinLogCosHelper<use_nudge>::interpolate( const T beta,
                                       const DepType dep_var_0,
                                       const DepType dep_var_1 )
{
  // T must be a floating point type
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<T>::RawType>::value) );
  testStaticPrecondition( (std::is_floating_point<typename QuantityTraits<DepType>::RawType>::value) );
  // Make sure the independent variables are valid
  testPrecondition( beta >= QuantityTraits<T>::zero() );
  testPrecondition( beta <= QuantityTraits<T>::one() );
  // Make sure the dependent variables are valid
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_0 ) );
  testPrecondition( !QuantityTraits<DepType>::isnaninf( dep_var_1 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_0 ) );
  testPrecondition( LinLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var_1 ) );

  return dep_var_0 + (dep_var_1 - dep_var_0)*beta;
}

// Interpolate between two points and return the processed value
template<bool use_nudge>
template<typename IndepCosineType, typename DepType>
inline typename QuantityTraits<DepType>::RawType
LinLogCosHelper<use_nudge>::interpolateAndProcess( const IndepCosineType raw_indep_var_0,
                                  const IndepCosineType raw_indep_var_1,
                                  const IndepCosineType raw_indep_var,
                                  const DepType dep_var_0,
                                  const DepType dep_var_1 )
{
  return getRawQuantity( interpolate( raw_indep_var_0,
                                      raw_indep_var_1,
                                      raw_indep_var,
                                      dep_var_0,
                                      dep_var_1 ) );
}

// Process the independent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLogCosHelper<use_nudge>::processIndepVar( const T indep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLogCosHelper<use_nudge>::isIndepVarInValidRange( indep_var ) );

  return log( CosNudgeHelper<use_nudge>::convertFromCosineVar( getRawQuantity(indep_var) ) );
}

// Process the dependent value
template<bool use_nudge>
template<typename T>
inline typename QuantityTraits<T>::RawType
LinLogCosHelper<use_nudge>::processDepVar( const T dep_var )
{
  // Make sure the indep var value is valid
  testPrecondition( LinLogCosHelper<use_nudge>::isDepVarInValidRange( dep_var ) );

  return getRawQuantity(dep_var);
}

// Recover the processed independent value
template<bool use_nudge>
template<typename T>
inline T LinLogCosHelper<use_nudge>::recoverProcessedIndepVar( const T processed_indep_var )
{
  return CosNudgeHelper<use_nudge>::convertToCosineVar( exp( processed_indep_var ) );
}

// Recover the processed dependent value
template<bool use_nudge>
template<typename T>
inline T LinLogCosHelper<use_nudge>::recoverProcessedDepVar( const T processed_dep_var )
{
  return processed_dep_var;
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LinLogCosHelper<false>::isIndepVarInValidRange( const T indep_var )
{
  return indep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         indep_var < QuantityTraits<T>::one();
}

// Test if the independent value is in a valid range (doesn't check nan/inf)
template<>
template<typename T>
inline bool LinLogCosHelper<true>::isIndepVarInValidRange( const T indep_var )
{
  return indep_var >= QuantityTraits<T>::initializeQuantity(-1.0) &&
         indep_var <= QuantityTraits<T>::one();
}

// Test if the dependent value is in a valid range (doesn't check nan/inf)
template<bool use_nudge>
template<typename T>
inline bool LinLogCosHelper<use_nudge>::isDepVarInValidRange( const T dep_var )
{
  // Make sure the indep var is not inf or nan
  testPrecondition( !QuantityTraits<T>::isnaninf( dep_var ) );

  return true;
}

// Get the interpolation type
inline InterpolationType LinLogCos::getInterpolationType()
{
  return LINLOGCOS_INTERPOLATION;
}

// The name of the policy
inline const std::string LinLogCos::name()
{
  return "LinLogCos";
}

// Get the interpolation type
inline InterpolationType LinNudgedLogCos::getInterpolationType()
{
  return LINNUDGEDLOGCOS_INTERPOLATION;
}

// The name of the policy
inline const std::string LinNudgedLogCos::name()
{
  return "LinNudgedLogCos";
}

} // end Utility namespace

#endif // end UTILITY_COSINE_INTERPOLATION_POLICY_DEF_HPP

//---------------------------------------------------------------------------//
// end Utility_CosineInterpolationPolicy_def.hpp
//---------------------------------------------------------------------------//


#ifndef PROXY_H
#define PROXY_H

#include <vector>
#include <map>
#include <random>
#include <Eigen/Dense>
#include <embree3/rtcore.h>

class C_Instancer {
    public:
        C_Instancer(std::vector<float> b_0, std::vector<float> b_1, bool cast_shadow_rays = false, std::vector<std::string> texture_paths = {}, uint32_t min_shadow_samples = 4, uint32_t n_shadow_samples = 512, uint32_t min_texture_samples = 4, uint32_t n_texture_samples = 512, float jitter_amount = 0, uint8_t instance_sample_method = false, bool use_mean_distance = false, uint32_t seed = 0);
        template <typename Container>
        void AddInstance(Container& transformation);
        void DistributeInstancesOnMesh(std::string mesh_path, float scale, std::string patch_origins_path = "");
        void AddMesh(std::string mesh_path, std::string texture_path = "");
        void CommitScene();
        uint32_t GetNumberOfInstances();
        void GetModelInput(float* rays_o, float* rays_d, float* t, float* dist, float* pts, float* color, float* density, float* density_weight, int* instance_id, bool* hit, uint32_t n_rays, uint32_t n_pts, float step_size, float* parameters = nullptr);
        void ExportTransformations(std::string file_path);

    private:

        struct Mesh {
            unsigned int geomID;
            Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> V;
            Eigen::Matrix<int, Eigen::Dynamic, 3, Eigen::RowMajor> F;
            Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> N;
            Eigen::Matrix<float, Eigen::Dynamic, 2, Eigen::RowMajor> UV;
            std::vector<Eigen::MatrixXf> textures;
        };

        std::map<unsigned int, Mesh> meshes;

        RTCDevice device;
        RTCScene proxy;
        RTCScene instances;
        RTCScene instancer;
        unsigned int instance_geomID;
        unsigned int instancer_geomID;
        unsigned int max_geomID;
        Mesh instancer_mesh;
        std::vector<Eigen::Vector3f> instance_origins;
        std::vector<Eigen::Matrix4f> transformations;
        std::vector<Eigen::Matrix3f> dir_transformations;
        float patch_scale;
        float patch_max_extent;
        float jitter_amount;

        bool use_mean_distance;

        bool cast_shadow_rays;
        uint32_t min_shadow_samples;
        uint32_t n_shadow_samples;

        uint32_t n_parameters;
        int32_t light_dir_parameter_idx = -1;
        int32_t light_strength_parameter_idx = -1;
        std::vector<uint32_t> texture_parameter_idxs;
        std::vector<Eigen::MatrixXf> textures;
        uint32_t min_texture_samples;
        uint32_t n_texture_samples;

        void createAABB(RTCDevice& device, RTCScene& scene, std::vector<float> b_0, std::vector<float> b_1);

        Eigen::Vector3f getPt(const Eigen::Vector3f& pt, unsigned int instID);
        Eigen::Vector3f getDir(const Eigen::Vector3f& dir, unsigned int instID);
        Eigen::Vector3f getPtOnRay(const Eigen::Vector3f& org, const Eigen::Vector3f& dir, float t);
        Eigen::Vector3f getShadowedLightDir(bool isShadowed, const Eigen::Vector3f& dir, const Eigen::Vector3f& pt, unsigned int instID);
        float getLightStrength(float lightStrength, const Eigen::Vector3f& lightPos, const Eigen::Vector3f& pt);
        bool isShadowed(const Eigen::Vector3f& pt, const Eigen::VectorXf& dir);
        Eigen::VectorXf getParameters(const Eigen::Vector3f& pt, const Eigen::VectorXf& parameters);
        float interpolate2d(Eigen::Vector2f x, Eigen::MatrixXf& y_ref);
        Eigen::VectorXf interpolate2d(Eigen::Vector2f x, std::vector<Eigen::MatrixXf>& y_ref);

        std::mt19937 gen;
        uint8_t instance_sample_method;

        template <typename Iterator>
        std::tuple<unsigned int, float> sampleRandom(Iterator begin, Iterator end);
        template <typename Iterator>
        std::tuple<unsigned int, float> sampleNearest(Iterator begin, Iterator end, const Eigen::Vector3f& pt);
        template <typename Iterator>
        std::tuple<unsigned int, float> sampleNearestBlend(Iterator begin, Iterator end, const Eigen::Vector3f& pt, float transition_range = 0.2f);

        float getMeanDistance(float mu, float hw);

        Eigen::Vector3f shadeMesh(const Eigen::Vector3f& pt, unsigned int geomID, unsigned int primID, const Eigen::Vector3f& uvw, const Eigen::Vector3f& dir, float diffuse = 1.0, float ambient = 0.2);
};

#endif


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

using namespace tensorflow;
using namespace std;

typedef Eigen::ThreadPoolDevice CPUDevice;
typedef Eigen::GpuDevice GPUDevice;
/*
 * tag_k: [num_keypoints,N,tag_C] tag值
 * loc_k: [num_keypoints,N,2], x,y
 * val_k: [num_keypoints,N]
 *
 * output:
 * output: [N,num_keypoints,3+tag_C] (x,y,val,tag..)
 */
static uint32_t s_dict_key = 0;
inline uint32_t get_dict_key() {
    return s_dict_key++;
}
REGISTER_OP("MatchByTag")
    .Attr("T: {float,double,int32,int64}")
    .Attr("detection_threshold: float=0.1")
    .Attr("tag_threshold:float=1.0")
    .Attr("use_detection_val: bool=true")
    .Input("tag_k: T")
    .Input("loc_k: T")
    .Input("val_k: T")
	.Output("output:T")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
            const auto input_shape0 = c->input(0);
            const auto num_keypoints = c->Value(c->Dim(input_shape0,0));
            const auto N = c->Dim(input_shape0,1);
            const auto tag_C = c->Value(c->Dim(input_shape0,2));
            auto shape0 = c->MakeShape({N,num_keypoints,3+tag_C});

			c->set_output(0, shape0);

			return Status::OK();
			});

template <typename Device,typename T>
class MatchByTagOp: public OpKernel {
    private:
        using Tensor1d_t = Eigen::Tensor<T,1,Eigen::RowMajor>;
        using Tensor2d_t = Eigen::Tensor<T,2,Eigen::RowMajor>;
        using Tensor3d_t = Eigen::Tensor<T,3,Eigen::RowMajor>;
    public:
        explicit MatchByTagOp(OpKernelConstruction* context) : OpKernel(context) {
            OP_REQUIRES_OK(context, context->GetAttr("detection_threshold", &detection_threshold_));
            OP_REQUIRES_OK(context, context->GetAttr("use_detection_val", &use_detection_val_));
            OP_REQUIRES_OK(context, context->GetAttr("tag_threshold", &tag_threshold_));
        }

        void Compute(OpKernelContext* context) override
        {
            TIME_THISV1("MatchByTag");
            const Tensor &_tag_k = context->input(0);
            const Tensor &_loc_k = context->input(1);
            const Tensor &_val_k = context->input(2);

            OP_REQUIRES(context, _tag_k.dims() == 3, errors::InvalidArgument("tag_k data must be 3-dimension"));
            OP_REQUIRES(context, _loc_k.dims() == 3, errors::InvalidArgument("loc_k data must be 3-dimension"));
            OP_REQUIRES(context, _val_k.dims() == 2, errors::InvalidArgument("val_k data must be 2-dimension"));

            auto          tag_k = _tag_k.template tensor<T,3>();
            auto          loc_k = _loc_k.template tensor<T,3>();
            auto          val_k = _val_k.template tensor<T,2>();

            const auto   num_keypoints = _tag_k.dim_size(0);
            const auto   N             = _tag_k.dim_size(1);
            const auto   tag_C         = _tag_k.dim_size(2);
            const auto   out_C         = 3+tag_C;
            int          dims_3d0[3]   = {N,num_keypoints,out_C};
            TensorShape  outshape0;
            Tensor      *output        = NULL;

            TensorShapeUtils::MakeShape(dims_3d0, 3, &outshape0);

            OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output));

            auto output_t = output->template tensor<T,3>();
            map<uint32_t,Eigen::Tensor<T,2,Eigen::RowMajor>> joints_dict;
            map<uint32_t,vector<Tensor1d_t>> tag_dict;
            Eigen::Tensor<T,2,Eigen::RowMajor> g_default_joints(num_keypoints,out_C);
            g_default_joints.setZero();

            output_t.setZero();

            for(auto i=0; i<num_keypoints; ++i) {
                auto valid_nr = 0;
                for(auto j=0; j<N; ++j) {
                    if(val_k(i,j)>detection_threshold_)
                        ++valid_nr;
                }
                if(0 == valid_nr)
                    continue;

                Eigen::Tensor<T,2,Eigen::RowMajor> joints(valid_nr,3+tag_C);
                vector<Tensor1d_t> tags;

                joints.setZero();
                for(auto j=0,k=0; j<N; ++j) {
                    if(val_k(i,j)<=detection_threshold_)
                        continue;
                    joints(k,0) = loc_k(i,j,0);
                    joints(k,1) = loc_k(i,j,1);
                    joints(k,2) = val_k(i,j);
                    for(auto l=0; l<tag_C; ++l)
                        joints(k,3+l) = tag_k(i,j,l);
                    ++k;
                    tags.push_back(Tensor1d_t(tag_k.chip(i,0).chip(j,0)));
                }
                if(joints_dict.empty()) {
                    for(auto j=0; j<valid_nr; ++j) {
                        auto key = get_dict_key();
                        Eigen::Tensor<T,2,Eigen::RowMajor> default_joints = g_default_joints;
                        vector<Eigen::Tensor<T,1,Eigen::RowMajor>> l_tags;

                        default_joints.chip(i,0) = joints.chip(j,0);
                        joints_dict[key] = default_joints;

                        l_tags.push_back(Eigen::Tensor<T,1,Eigen::RowMajor>(tags.at(j)));
                        tag_dict[key] = l_tags;
                    }
                } else {
                    vector<uint32_t> grouped_keys;
                    vector<Tensor1d_t> grouped_tags;
                    for(auto it=joints_dict.begin(); it!=joints_dict.end(); ++it) {
                        auto key = it->first;
                        auto tag = tags_mean(tag_dict[key],tag_C);

                        grouped_keys.push_back(key);
                        grouped_tags.emplace_back(std::move(tag));
                    }
                    auto diff_normed = get_normalized_dis_matrix(tags,grouped_tags);
                    Tensor2d_t diff_normed_saved = diff_normed;
                    if(use_detection_val_)
                        diff_normed = apply_detection_value(diff_normed,joints);
                    const auto row = diff_normed.dimension(0);
                    const auto col = diff_normed.dimension(1);
                    const auto nr = max<int>(row,col);
                    Tensor2d_t big_diff_normed(nr,nr);
                    big_diff_normed.setConstant(1e10);
                    Eigen::array<int, 2> offsets = {0, 0};
                    Eigen::array<int, 2> extents = {row, col};

                    big_diff_normed.slice(offsets,extents) = diff_normed;

                    vector<pair<int,int>> matches;
                    int i_row,i_col;

                    MOT::linear_assignment(big_diff_normed.data(),nr,nullptr,&matches);

                    for(auto& d:matches) {

                        tie(i_row,i_col) = d;

                        if((i_row<row) && (i_col<col) && (diff_normed_saved(i_row,i_col)<tag_threshold_)) {
                            auto key = grouped_keys[i_col];

                            joints_dict[key].chip(i,0) = joints.chip(i_row,0);
                            tag_dict[key].push_back(tags.at(i_row));
                        } else if(i_row<row) {
                            auto key = get_dict_key();
                            Eigen::Tensor<T,2,Eigen::RowMajor> default_joints = g_default_joints;
                            vector<Tensor1d_t> l_tags;

                            default_joints.chip(i,0) = joints.chip(i_row,0);
                            joints_dict[key] = default_joints;

                            l_tags.push_back(Eigen::Tensor<T,1,Eigen::RowMajor>(tags.at(i_row)));
                            tag_dict[key] = l_tags;
                        }
                    }
                }
            }
            vector<pair<float,uint32_t>> scores_keys;
            for(auto it=joints_dict.begin(); it!=joints_dict.end(); ++it) {
                const auto key = it->first;
                Tensor1d_t scores = it->second.chip(2,1);
                scores_keys.emplace_back(Eigen::Tensor<float, 0,Eigen::RowMajor>(scores.mean()).data()[0],key);
            }
            sort(scores_keys.begin(),scores_keys.end(),[](const auto& lhv, const auto& rhv) {
                return lhv.first>rhv.first;
            });
            for(auto i=0; i<min<int>(scores_keys.size(),N); ++i ) {
                auto data = joints_dict[scores_keys[i].second];
                output_t.chip(i,0) = data;
            }
        }
        Eigen::Tensor<T,1,Eigen::RowMajor> tags_mean(const vector<Eigen::Tensor<T,1,Eigen::RowMajor>>& tensors,int tag_dim) {
            if(tensors.size()==1)
                return tensors.at(0);

            Eigen::Tensor<T,2,Eigen::RowMajor> all_data(tensors.size(),tag_dim);

            for(auto i=0; i<tensors.size(); ++i) {
                all_data.chip(i,0) = tensors.at(i);
            }
            return all_data.mean(Eigen::array<int,1>({0}));
        }
        Tensor2d_t get_normalized_dis_matrix(const vector<Tensor1d_t>& data0,const vector<Tensor1d_t>& data1) {
            Tensor2d_t res(data0.size(),data1.size());
            for(auto i=0; i<data0.size(); ++i) {
                for(auto j=0; j<data1.size(); ++j) {
                    res(i,j) = normalized_dis(data0.at(i),data1.at(j));
                }
            }
            return res;
        }
        T normalized_dis(const Tensor1d_t& data0,const Tensor1d_t& data1) {
            if(data0.dimension(0) == 1) {
                return fabs(data0(0)-data1(0));
            } else {
                T sum = 0;
                for(auto i=0; i<data0.dimension(0); ++i) {
                    const auto diff = data0(i)-data1(i);
                    sum += (diff*diff);
                }
                return sqrt(sum);
            }
        }
        Tensor2d_t apply_detection_value(const Tensor2d_t& diff,const Tensor2d_t& joints) {
            Tensor2d_t res(diff.dimension(0),diff.dimension(1));
            res.setZero();

            for(auto i=0; i<diff.dimension(0); ++i) {
                const auto val = joints(i,2);
                for(auto j=0; j<diff.dimension(1); ++j) {
                    //res(i,j) = int(diff(i,j)+0.5)*100-val;
                    res(i,j) = int(diff(i,j)*2+0.5)*50-val;
                }
            }
            return res;
        }
    private:
        float detection_threshold_ = 0.1f;
        float tag_threshold_       = 1.0f;
        bool  use_detection_val_   = true;
};
REGISTER_KERNEL_BUILDER(Name("MatchByTag").Device(DEVICE_CPU).TypeConstraint<float>("T"), MatchByTagOp<CPUDevice, float>);


#include <boost/test/unit_test.hpp>

#include <string>
#include <vector>

#include "key.h"
#include "base58.h"
#include "uint256.h"
#include "util.h"

using namespace std;

// Mixed style keys
static const string strDefaultSecret1     ("7QRR3XwvHW963ERsAQQi2JScqv2PgNDcrwMXwAcmJ3g9byEbRqN");
static const string strDefaultSecret2     ("7RexmM8ERicLWvtNVZE3aQnViZLr2gqq1dShPDcSdmgf3MjNx64");
static const string strDefaultSecret1C    ("VGNm8BbQBqchErGkQWKe4jz5Cu396kehHQudY5SrWEonyL9d9dhs");
static const string strDefaultSecret2C    ("VMpZ8jpvG6ey96qjs3EaNTyokRkbQSrWumrRF4fS9JE2uPQ8a1xR");

// NuShares keys
static const string strSSecret1     ("5zn9do6aVdjaNn2F64LjxqG6P8AENnpqp6P1gsj7YXPrstEgQxL");
static const string strSSecret2     ("621hMcGtdrCprUUkRDA5WwbyFmUgj7T3xnUB8vintFQNKHqSpda");
static const string strSSecret1C    ("P42H7QTcqP8vK7iVBKk8dS1ckebk3BGASrkA2ztst99u9Rn9aEhP");
static const string strSSecret2C    ("P9U57xh8ueBCDNHUdrf4wA1MJBKCLsTz5Dgwjz7TXCa95V2dt5c1");

// NuBits keys
static const string strBSecret1     ("62itp2YcM4QoWbbwzdxjv9rsJEmx2VfibjT7w2R1YBQFNWuRPef");
static const string strBSecret2     ("63xSXqivVGt3zJ4TKnn5UGCkAt6QNpHvkRYHP5QgsuQkov3sKB2");
static const string strBSecret1C    ("PCbugZpX1Y44HdjFjQ92Rc7715oPsbXtcJNHbTEyNcucRkyvVT4h");
static const string strBSecret2C    ("PJ3hh8435o6LBtJFBw3xjL6qYcWrBHjiEfK5JSTZ1gKrMpGGxHs2");

// The resulting NSR addresses for the above keys
static const CBitcoinAddress addrS1 ("SkYqsCFMoSkgZh5gqzses71yRWZsvBTYTA");
static const CBitcoinAddress addrS2 ("SbNy74rQ5yGkWw6LiagR8Nnch3TNyMGvd4");
static const CBitcoinAddress addrS1C("Sj6Jtef2gkNXBW5MqxS8qUmsWJeewfKuan");
static const CBitcoinAddress addrS2C("SYij48kVjZiiWawysqsnpF8FL9gaKpPNf9");

static const string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF");

#ifdef KEY_TESTS_DUMPINFO
void dumpKeyInfo(uint256 privkey)
{
    CSecret secret;
    secret.resize(32);
    memcpy(&secret[0], &privkey, 32);
    vector<unsigned char> sec;
    sec.resize(32);
    memcpy(&sec[0], &secret[0], 32);
    printf("  * secret (hex): %s\n", HexStr(sec).c_str());

    for (int nCompressed=0; nCompressed<2; nCompressed++)
    {
        bool fCompressed = nCompressed == 1;
        printf("  * %s:\n", fCompressed ? "compressed" : "uncompressed");
        CBitcoinSecret bsecret;
        bsecret.SetSecret(secret, fCompressed, '?');
        printf("    * secret (base58): %s\n", bsecret.ToString().c_str());
        CKey key;
        key.SetSecret(secret, fCompressed);
        vector<unsigned char> vchPubKey = key.GetPubKey();
        printf("    * pubkey (hex): %s\n", HexStr(vchPubKey).c_str());
        printf("    * address (base58): %s\n", CBitcoinAddress(vchPubKey).ToString().c_str());
    }
}
#endif

BOOST_AUTO_TEST_SUITE(key_tests)

BOOST_AUTO_TEST_CASE(key_test1)
{
    CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1;
    BOOST_CHECK( bsecret1.SetString (strDefaultSecret1,  '?'));
    BOOST_CHECK( bsecret2.SetString (strDefaultSecret2,  '?'));
    BOOST_CHECK( bsecret1C.SetString(strDefaultSecret1C, '?'));
    BOOST_CHECK( bsecret2C.SetString(strDefaultSecret2C, '?'));
    BOOST_CHECK(!baddress1.SetString(strAddressBad,      '?'));

    bool fCompressed;
    CSecret secret1  = bsecret1.GetSecret (fCompressed);
    BOOST_CHECK(fCompressed == false);
    CSecret secret2  = bsecret2.GetSecret (fCompressed);
    BOOST_CHECK(fCompressed == false);
    CSecret secret1C = bsecret1C.GetSecret(fCompressed);
    BOOST_CHECK(fCompressed == true);
    CSecret secret2C = bsecret2C.GetSecret(fCompressed);
    BOOST_CHECK(fCompressed == true);

    BOOST_CHECK(secret1 == secret1C);
    BOOST_CHECK(secret2 == secret2C);

    CKey key1, key2, key1C, key2C;
    key1.SetSecret(secret1, false);
    key2.SetSecret(secret2, false);
    key1C.SetSecret(secret1, true);
    key2C.SetSecret(secret2, true);

    BOOST_CHECK(addrS1.Get()  == CTxDestination(key1.GetPubKey().GetID()));
    BOOST_CHECK(addrS2.Get()  == CTxDestination(key2.GetPubKey().GetID()));
    BOOST_CHECK(addrS1C.Get() == CTxDestination(key1C.GetPubKey().GetID()));
    BOOST_CHECK(addrS2C.Get() == CTxDestination(key2C.GetPubKey().GetID()));

    for (int n=0; n<16; n++)
    {
        string strMsg = strprintf("Very secret message %i: 11", n);
        uint256 hashMsg = Hash(strMsg.begin(), strMsg.end());

        // normal signatures

        vector<unsigned char> sign1, sign2, sign1C, sign2C;

        BOOST_CHECK(key1.Sign (hashMsg, sign1));
        BOOST_CHECK(key2.Sign (hashMsg, sign2));
        BOOST_CHECK(key1C.Sign(hashMsg, sign1C));
        BOOST_CHECK(key2C.Sign(hashMsg, sign2C));

        BOOST_CHECK( key1.Verify(hashMsg, sign1));
        BOOST_CHECK(!key1.Verify(hashMsg, sign2));
        BOOST_CHECK( key1.Verify(hashMsg, sign1C));
        BOOST_CHECK(!key1.Verify(hashMsg, sign2C));

        BOOST_CHECK(!key2.Verify(hashMsg, sign1));
        BOOST_CHECK( key2.Verify(hashMsg, sign2));
        BOOST_CHECK(!key2.Verify(hashMsg, sign1C));
        BOOST_CHECK( key2.Verify(hashMsg, sign2C));

        BOOST_CHECK( key1C.Verify(hashMsg, sign1));
        BOOST_CHECK(!key1C.Verify(hashMsg, sign2));
        BOOST_CHECK( key1C.Verify(hashMsg, sign1C));
        BOOST_CHECK(!key1C.Verify(hashMsg, sign2C));

        BOOST_CHECK(!key2C.Verify(hashMsg, sign1));
        BOOST_CHECK( key2C.Verify(hashMsg, sign2));
        BOOST_CHECK(!key2C.Verify(hashMsg, sign1C));
        BOOST_CHECK( key2C.Verify(hashMsg, sign2C));

        // compact signatures (with key recovery)

        vector<unsigned char> csign1, csign2, csign1C, csign2C;

        BOOST_CHECK(key1.SignCompact (hashMsg, csign1));
        BOOST_CHECK(key2.SignCompact (hashMsg, csign2));
        BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C));
        BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C));

        CKey rkey1, rkey2, rkey1C, rkey2C;

        BOOST_CHECK(rkey1.SetCompactSignature (hashMsg, csign1));
        BOOST_CHECK(rkey2.SetCompactSignature (hashMsg, csign2));
        BOOST_CHECK(rkey1C.SetCompactSignature(hashMsg, csign1C));
        BOOST_CHECK(rkey2C.SetCompactSignature(hashMsg, csign2C));

        BOOST_CHECK(rkey1.GetPubKey()  == key1.GetPubKey());
        BOOST_CHECK(rkey2.GetPubKey()  == key2.GetPubKey());
        BOOST_CHECK(rkey1C.GetPubKey() == key1C.GetPubKey());
        BOOST_CHECK(rkey2C.GetPubKey() == key2C.GetPubKey());
    }
}

BOOST_AUTO_TEST_CASE(key_test2)
{
    CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C;
    // New encoding for NuShares
    BOOST_CHECK( bsecret1.SetString (strSSecret1,  'S'));
    BOOST_CHECK( bsecret2.SetString (strSSecret2,  'S'));
    BOOST_CHECK( bsecret1C.SetString(strSSecret1C, 'S'));
    BOOST_CHECK( bsecret2C.SetString(strSSecret2C, 'S'));
    // Set NuShares private key with NuBits unit, should fail
    BOOST_CHECK(!bsecret1.SetString (strSSecret1,  'B'));
    BOOST_CHECK(!bsecret2.SetString (strSSecret2,  'B'));
    BOOST_CHECK(!bsecret1C.SetString(strSSecret1C, 'B'));
    BOOST_CHECK(!bsecret2C.SetString(strSSecret2C, 'B'));

    // New encoding for NuBits
    BOOST_CHECK( bsecret1.SetString (strBSecret1,  'B'));
    BOOST_CHECK( bsecret2.SetString (strBSecret2,  'B'));
    BOOST_CHECK( bsecret1C.SetString(strBSecret1C, 'B'));
    BOOST_CHECK( bsecret2C.SetString(strBSecret2C, 'B'));
    // Set NuBits private key with NuShares unit, should fail
    BOOST_CHECK(!bsecret1.SetString (strBSecret1,  'S'));
    BOOST_CHECK(!bsecret2.SetString (strBSecret2,  'S'));
    BOOST_CHECK(!bsecret1C.SetString(strBSecret1C, 'S'));
    BOOST_CHECK(!bsecret2C.SetString(strBSecret2C, 'S'));

    // Old keys encoding for NuShares
    BOOST_CHECK( bsecret1.SetString (strDefaultSecret1,  'S'));
    BOOST_CHECK( bsecret2.SetString (strDefaultSecret2,  'S'));
    BOOST_CHECK( bsecret1C.SetString(strDefaultSecret1C, 'S'));
    BOOST_CHECK( bsecret2C.SetString(strDefaultSecret2C, 'S'));

    // Old keys encoding for NuBits
    BOOST_CHECK( bsecret1.SetString (strDefaultSecret1,  'B'));
    BOOST_CHECK( bsecret2.SetString (strDefaultSecret2,  'B'));
    BOOST_CHECK( bsecret1C.SetString(strDefaultSecret1C, 'B'));
    BOOST_CHECK( bsecret2C.SetString(strDefaultSecret2C, 'B'));

    // Check the CBitcoinSecret
    bool fCompressed;
    CSecret secret = bsecret1.GetSecret(fCompressed);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'S').ToString() == strSSecret1);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'B').ToString() == strBSecret1);
    secret = bsecret2.GetSecret(fCompressed);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'S').ToString() == strSSecret2);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'B').ToString() == strBSecret2);
    secret = bsecret1C.GetSecret(fCompressed);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'S').ToString() == strSSecret1C);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'B').ToString() == strBSecret1C);
    secret = bsecret2C.GetSecret(fCompressed);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'S').ToString() == strSSecret2C);
    BOOST_CHECK(CBitcoinSecret(secret, fCompressed, 'B').ToString() == strBSecret2C);
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * renderer.cpp
 *
 *  Created on: Oct 16, 2014
 *      Author: kenaco
 */

#define GLM_FORCE_RADIANS

#include <SDL.h>
#include <SDL_video.h>
#include <boost/range/adaptor/map.hpp>
#include <glbinding/gl/gl.h>
#include <glbinding/Binding.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "log.hpp"
#include "renderer.hpp"
#include "shader.hpp"
#include "camera.hpp"
#include "texture.hpp"

using namespace gl;

/**
 * Inicializacia standardneho rendereru
 */
Renderer::Renderer(SDL_Window* window, const char* vertexShader, const char* fragShader, unsigned int width=800, unsigned int height=600):
		scene(nullptr), activeCamera(""), width(width), height(height) {
	LOG(info) << "Renderer()";
	//Context and gl initialization
	gl = SDL_GL_CreateContext(window);
	glbinding::Binding::initialize();

	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glEnable(GL_CULL_FACE);
	// Enable blending
//	glEnable(GL_BLEND);
//	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	shader = new Shader("../assets/shader.vert", "../assets/shader.frag");
	mvpUniformId_ = shader->getUniform("MVP");
	modelUniformId_ = shader->getUniform("M");
	viewUniformId_ = shader->getUniform("V");
	depthBiasmvpUniformId_ = shader->getUniform("DepthBiasMVP");
	shadowMapUniformId_ = shader->getUniform("shadowMap");
	lightInvDirectionUniformId_ = shader->getUniform("LightInvDirection_worldspace");

	shadowMap_ = new ShadowMap(512, 512);
	shadowMapShader_ = new Shader("../assets/shadow_map.vert", "../assets/shadow_map.frag");
	depthmvpUniformId_ = shadowMapShader_->getUniform("depthMVP");

	static const float quad[] = {
		-1.0f, -1.0f, 0.0f,
		 1.0f, -1.0f, 0.0f,
		-1.0f,  1.0f, 0.0f,
		-1.0f,  1.0f, 0.0f,
		 1.0f, -1.0f, 0.0f,
		 1.0f,  1.0f, 0.0f,
	};

	glGenBuffers(1, &qvbo);
	glBindBuffer(GL_ARRAY_BUFFER, qvbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
	qshader = new Shader("../assets/passthrough.vert", "../assets/base_texture.frag");
	qTextureUniformId = qshader->getUniform("texture");

	LOG(info) << "Renderer() done";
}

Renderer::~Renderer() {
	LOG(info) << "Renderer::~Renderer()";

	// posobilo problemy, bolo zakomentovane TODO este otestovat
//	scene = nullptr; //zrusime ukazatel, constroler nasledne uvolni scenu
	delete shader;
	SDL_GL_DeleteContext(gl);

	LOG(info) << "Renderer::~Renderer() done";
}

//TODO sucast konstruktoru?
void Renderer::setScene(Scene* scene) {
	this->scene = scene;
	activeCamera = scene->cameras.begin()->first;
	scene->cameras[activeCamera]->projectionMatrix(width, height);
}

/**
 * Metoda vracia objekt aktivnej kamery TODO implementovat prepinanie kamier?
 */
Camera* Renderer::getActiveCamera() {
	return scene->cameras[activeCamera];
}

void Renderer::render() {
	LOG(info) << "Renderer_t.render()";

//	glm::mat4 depthMVP = glm::mat4(1.f);

	glm::mat4 depthMVP;

	for (auto&& light: scene->lights | boost::adaptors::map_values) {
		glBindFramebuffer(GL_FRAMEBUFFER, shadowMap_->frameBufferId);
		glViewport(0,0,shadowMap_->width, shadowMap_->height);
		glEnable(GL_CULL_FACE);
		glCullFace(GL_BACK);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		lightCamera = Camera(dynamic_cast<DirectLight*>(light));
//		depthMVP = lightCamera.mvp();

		shadowMapShader_->bind();

//		glUniformMatrix4fv(depthmvpUniformId_, 1, GL_FALSE, &depthMVP[0][0]);

		for (auto mesh: scene->meshes) {
			depthMVP = lightCamera.mvp(mesh.second->modelMatrix());
			glUniformMatrix4fv(depthmvpUniformId_, 1, GL_FALSE, &depthMVP[0][0]);

			mesh.second->render();
		}
		shadowMapShader_->unbind();

	}

	LOG(debug) << "Prvy render done!";

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glViewport(0,0, width, height);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	shader->bind();

//	glm::mat4 M = getActiveCamera()->modelMatrix();
//	glUniformMatrix4fv(modelUniformId_, 1, GL_FALSE, &M[0][0]);
	glm::mat4 V = getActiveCamera()->viewMatrix();
	glUniformMatrix4fv(viewUniformId_, 1, GL_FALSE, &V[0][0]);
//	glm::mat4 MVP = getActiveCamera()->mvp(width, height);
//	glUniformMatrix4fv(mvpUniformId_, 1, GL_FALSE, &MVP[0][0]);

	glm::mat4 biasMatrix(
		0.5, 0.0, 0.0, 0.0,
		0.0, 0.5, 0.0, 0.0,
		0.0, 0.0, 0.5, 0.0,
		0.5, 0.5, 0.5, 1.0
	);

	glm::mat4 depthBiasMVP = biasMatrix * depthMVP; // TODO naivny predpoklad jedneho svetla
	glUniformMatrix4fv(depthBiasmvpUniformId_, 1, GL_FALSE, &depthBiasMVP[0][0]);

	glm::vec3 lightPos = scene->lights["light_0"]->world_position();
	glUniform3f(lightInvDirectionUniformId_, lightPos.x, lightPos.y, lightPos.z);

	glActiveTexture(GL_TEXTURE0);
	shadowMap_->bind(); // texture
	glUniform1i(shadowMapUniformId_, 0);

	for (auto item: scene->meshes) {
		Mesh* mesh = item.second;
		glUniformMatrix4fv(modelUniformId_, 1, GL_FALSE, &(mesh->modelMatrix())[0][0]);

		glm::mat4 MVP = getActiveCamera()->mvp(mesh->modelMatrix());
		glUniformMatrix4fv(mvpUniformId_, 1, GL_FALSE, &MVP[0][0]);

		mesh->render();
	}

	shader->unbind();

	// Render shadowmapy
	glViewport(0,0, width/4, height/4);

	qshader->bind();

	glActiveTexture(GL_TEXTURE0);
	shadowMap_->bind();
	glUniform1i(qTextureUniformId, 0);

	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, qvbo);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

	glDrawArrays(GL_TRIANGLES, 0, 6);
	glDisableVertexAttribArray(0);

	qshader->unbind();

	LOG(info) << "Renderer_t.render() done";
}