//---------------------------------------------------------------------------//
//!
//! \file   Utility_Measurement_def.hpp
//! \author Alex Robinson
//! \brief  The measurement class declaration.
//!
//---------------------------------------------------------------------------//

#ifndef UTILITY_MEASUREMENT_DEF_HPP
#define UTILITY_MEASUREMENT_DEF_HPP

// Std Lib Includes
#include <cmath>

// Boost Includes
#include <boost/io/ios_state.hpp>

namespace Utility{

// Constructor
template<typename T>
Measurement<T>::Measurement( const Measurement<T>::ValueType& value,
			     const Measurement<T>::ValueType& uncertainty )
  : d_value( value ),
    d_uncertainty( uncertainty )
{
  // Make sure the value is valid
  testPrecondition( !ST::isnaninf( value ) );
  // Make sure the uncertainty is valid
  testPrecondition( !ST::isnaninf( uncertainty ) );
  testPrecondition( uncertainty >= 0.0 );
}

// Copy constructor
template<typename T>
Measurement<T>::Measurement( const Measurement<T>& other_measurement )
  : d_value( other_measurement.d_value ),
    d_uncertainty( other_measurement.d_uncertainty )
{
  // Make sure the other measurement is valid
  testPrecondition( !ST::isnaninf( other_measurement.d_value ) );
  testPrecondition( !ST::isnaninf( other_measurement.d_uncertainty ) );
  testPrecondition( other_measurement.d_uncertainty >= 0.0 );
}

// Print method
template<typename T>
inline void Measurement<T>::print( std::ostream& os ) const
{
  boost::io::ios_precision_saver preision_saver(os);
  boost::io::ios_flags_saver flags_saver(os);

  os << d_value << "(+/-" << d_uncertainty << ")";
}

// Return the value of the measurement
template<typename T>
inline const typename Measurement<T>::ValueType&
Measurement<T>::getValue() const
{
  return d_value;
}

// Return the uncertainty of the measurement
template<typename T>
inline const typename Measurement<T>::ValueType&
Measurement<T>::getUncertainty() const
{
  return d_uncertainty;
}

// Return the relative uncertainty of the measurement
template<typename T>
inline const typename Measurement<T>::ValueType
Measurement<T>::getRelativeUncertainty() const
{
  if( d_value != 0.0 )
    return d_uncertainty/fabs(d_value);
  else
    return 0.0;
}

// Return the lower bound of the measurement
template<typename T>
inline const typename Measurement<T>::ValueType
Measurement<T>::getLowerBound() const
{
  return d_value - d_uncertainty;
}

// Return the upper bound of the measurement
template<typename T>
inline const typename Measurement<T>::ValueType
Measurement<T>::getUpperBound() const
{
  return d_value + d_uncertainty;
}

// Value access operator
template<typename T>
inline Measurement<T>::operator Measurement<T>::ValueType() const
{
  return d_value;
}

// In-place addition operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator+=(
				       const Measurement<T>::ValueType& value )
{
  // Make sure the value is valid
  testPrecondition( !ST::isnaninf( value ) );

  d_value += value;

  return *this;
}

// In-place addition operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator+=(
				      const Measurement<T>& other_measurement )
{
  // Make sure the other measurement is valid
  testPrecondition( !ST::isnaninf( other_measurement.d_value ) );
  testPrecondition( !ST::isnaninf( other_measurement.d_uncertainty ) );
  testPrecondition( other_measurement.d_uncertainty >= 0.0 );

  d_value += other_measurement.d_value;

  // Propagate the uncertainty of the measurements
  d_uncertainty = std::sqrt( d_uncertainty*d_uncertainty +
			     other_measurement.d_uncertainty*
			     other_measurement.d_uncertainty );

  return *this;
}

// In-place subtraction operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator-=(
				       const Measurement<T>::ValueType& value )
{
  // Make sure the value is valid
  testPrecondition( !ST::isnaninf( value ) );

  d_value -= value;

  return *this;
}

// In-place subtraction operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator-=(
				      const Measurement<T>& other_measurement )
{
  // Make sure the other measurement is valid
  testPrecondition( !ST::isnaninf( other_measurement.d_value ) );
  testPrecondition( !ST::isnaninf( other_measurement.d_uncertainty ) );
  testPrecondition( other_measurement.d_uncertainty >= 0.0 );

  d_value -= other_measurement.d_value;

  // Propagate the uncertainty of the measurements
  d_uncertainty = std::sqrt( d_uncertainty*d_uncertainty +
			     other_measurement.d_uncertainty*
			     other_measurement.d_uncertainty );

  return *this;
}

// In-place multiplication operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator*=(
				       const Measurement<T>::ValueType& value )
{
  // Make sure the value is valid
  testPrecondition( !ST::isnaninf( value ) );

  d_value *= value;

  d_uncertainty *= fabs( value );

  return *this;
}

// In-place multiplication operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator*=(
				      const Measurement<T>& other_measurement )
{
  // Make sure the other measurement is valid
  testPrecondition( !ST::isnaninf( other_measurement.d_value ) );
  testPrecondition( !ST::isnaninf( other_measurement.d_uncertainty ) );

  // Propagate the uncertainty of the measurements
  d_uncertainty = std::sqrt( d_uncertainty*d_uncertainty*
	       other_measurement.d_value*other_measurement.d_value +
	       other_measurement.d_uncertainty*other_measurement.d_uncertainty*
	       d_value*d_value );

  d_value *= other_measurement.d_value;

  return *this;
}

// In-place division operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator/=(
				       const Measurement<T>::ValueType& value )
{
  // Make sure the value is valid
  testPrecondition( !ST::isnaninf( value ) );
  testPrecondition( value != 0.0 );

  d_value /= value;

  d_uncertainty /= fabs( value );

  return *this;
}

// In-place division operator
template<typename T>
inline Measurement<T>& Measurement<T>::operator/=(
				      const Measurement<T>& other_measurement )
{
  // Make sure the other measurement is valid
  testPrecondition( !ST::isnaninf( other_measurement.d_value ) );
  testPrecondition( !ST::isnaninf( other_measurement.d_uncertainty ) );
  testPrecondition( other_measurement.d_value != 0.0 );
  testPrecondition( other_measurement.d_uncertainty >= 0.0 );

  double other_value_sqr = other_measurement.d_value*other_measurement.d_value;

  double term_1 = d_uncertainty*d_uncertainty/other_value_sqr;

  double term_2 =
    other_measurement.d_uncertainty*other_measurement.d_uncertainty*
    d_value*d_value/(other_value_sqr*other_value_sqr);

  // Propagate the uncertainty of the measurements
  d_uncertainty = std::sqrt( term_1 + term_2 );

  d_value /= other_measurement.d_value;

  return *this;
}

} // end Utility namespace

#endif // end UTILITY_MEASUREMENT_DEF_HPP

//---------------------------------------------------------------------------//
// end Utility_Measurement_def.hpp
//---------------------------------------------------------------------------//


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_MULT_INCLUDE
#define MTL_MULT_INCLUDE

#include <boost/numeric/mtl/config.hpp>
#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/utility/flatcat.hpp>
#include <boost/numeric/mtl/utility/ashape.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/mtl/utility/static_assert.hpp>
#include <boost/numeric/mtl/operation/dmat_dmat_mult.hpp>
#include <boost/numeric/mtl/operation/smat_smat_mult.hpp>
#include <boost/numeric/mtl/operation/smat_dmat_mult.hpp>
#include <boost/numeric/mtl/operation/mat_vec_mult.hpp>
#include <boost/numeric/mtl/operation/rvec_mat_mult.hpp> // Row vector times matrix
#include <boost/numeric/mtl/operation/mult_specialize.hpp>
#include <boost/numeric/mtl/operation/assign_mode.hpp>
#include <boost/numeric/mtl/operation/mult_assign_mode.hpp>
#include <boost/numeric/mtl/utility/enable_if.hpp>

#include <boost/mpl/if.hpp>
#include <boost/numeric/mtl/interface/vpt.hpp>

namespace mtl { namespace mat {

/// Multiplication: mult(a, b, c) computes c= a * b;
/** The 3 types must be compatible, e.g. all three matrices or b and c are column vectors and a is a matrix.
    The dimensions are checked at compile time. **/
template <typename A, typename B, typename C>
typename mtl::traits::enable_if_matrix<A>::type
inline mult(const A& a, const B& b, C& c)
{
    vampir_trace<4010> tracer;
    MTL_DEBUG_THROW_IF(static_cast<const void*>(&a) == static_cast<const void*>(&c)
		       || static_cast<const void*>(&b) == static_cast<const void*>(&c),
		       argument_result_conflict());

    // dispatch between matrices, vectors, and scalars
    using mtl::traits::shape_flatcat;
    gen_mult(a, b, c, assign::assign_sum(), shape_flatcat<A>(), shape_flatcat<B>(), shape_flatcat<C>());
                                            // typename category<A>::type(), typename category<B>::type(), typename category<C>::type());
}

/// Multiplication: mult_add(a, b, c) computes c+= a * b;
/** The 3 types must be compatible, e.g. all three matrices or b and c are column vectors and a is a matrix.
    The dimensions are checked at compile time. **/
template <typename A, typename B, typename C>
typename mtl::traits::enable_if_matrix<A>::type
inline mult_add(const A& a, const B& b, C& c)
{
    vampir_trace<4010> tracer;
    // dispatch between matrices, vectors, and scalars
    using mtl::traits::shape_flatcat;
    gen_mult(a, b, c, assign::plus_sum(), shape_flatcat<A>(), shape_flatcat<B>(), shape_flatcat<C>());
                                          // typename category<A>::type(), typename category<B>::type(), typename category<C>::type());
}

/// Four term multiplication: mult(a, x, y, z) computes z= a * x + y;
/** The 4 types must be compatible, i.e. a*x must be assignable to z and z must be incrementable by y.
    Right now, it is not more efficient than z= a * x; z+= y. For compatibility with MTL2. **/
template <typename A, typename X, typename Y, typename Z>
inline void mult(const A& a, const X& x, const Y& y, Z& z)
{
    vampir_trace<4010> tracer;
    mult(a, x, z);
    z+= y;
}

// Matrix multiplication
template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void gen_mult(const MatrixA& a, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::matrix>, tag::flat<tag::matrix>, tag::flat<tag::matrix>)
{
    vampir_trace<4011> tracer;
#if 1
    MTL_DEBUG_THROW_IF((const void*)&a == (const void*)&c || (const void*)&b == (const void*)&c, argument_result_conflict());
#else
    if ((const void*)&a == (const void*)&c || (const void*)&b == (const void*)&c) {
	C tmp(num_rows(c), num_cols(c));
	mult(a, b, tmp);
	swap(C, tmp);
	return;
    }
#endif

    MTL_DEBUG_THROW_IF(num_rows(a) != num_rows(c) || num_cols(a) != num_rows(b) || num_cols(b) != num_cols(c), incompatible_size());
    // dispatch between dense and sparse
    using mtl::traits::sparsity_flatcat;
    mat_mat_mult(a, b, c, Assign(), sparsity_flatcat<MatrixA>(), sparsity_flatcat<MatrixB>(), sparsity_flatcat<MatrixC>());
		 // typename category<MatrixA>::type(), typename category<MatrixB>::type(), typename category<MatrixC>::type());
}

/// Dense matrix multiplication
/**  The function for dense matrix multiplication defines a default multiplication functor.
     Alternatively the user can define his own functors for specific triplets of matrix types,
     see detail::dmat_dmat_mult_specialize.
     The default functor for dense matrix multiplication is:
     -# Use BLAS   if available, otherwise
     -# Recursive multiplication with:
        -# Platform optimized mult on blocks   if available, otherwise
        -# Tiled multiplication on blocks      if available, otherwise
        -# Naive multiplication on blocks
     -# Naive multiplication on entire matrices if recursion is not available
**/
template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::dense>, tag::flat<tag::dense>, tag::flat<tag::dense>)
{
    vampir_trace<4012> tracer;
    using assign::plus_sum; using assign::assign_sum;

    static const unsigned long tiling1= detail::dmat_dmat_mult_tiling1<MatrixA, MatrixB, MatrixC>::value;
    static const unsigned long tiling2= detail::dmat_dmat_mult_tiling2<MatrixA, MatrixB, MatrixC>::value;
    typedef gen_tiling_dmat_dmat_mult_t<tiling1, tiling2, plus_sum>    tiling_mult_t;

    typedef gen_platform_dmat_dmat_mult_t<plus_sum, tiling_mult_t>     platform_mult_t;
    typedef gen_recursive_dmat_dmat_mult_t<platform_mult_t>            recursive_mult_t;
    typedef gen_blas_dmat_dmat_mult_t<assign_sum, recursive_mult_t>    blas_mult_t;
    typedef size_switch_dmat_dmat_mult_t<straight_dmat_dmat_mult_limit, tiling_mult_t, blas_mult_t>   variable_size_t;

    typedef fully_unroll_fixed_size_dmat_dmat_mult_t<Assign>           fully_unroll_t;
    typedef size_switch_dmat_dmat_mult_t<fully_unroll_dmat_dmat_mult_limit, fully_unroll_t, tiling_mult_t> fixed_size_t;

    static const bool all_static= mtl::traits::is_static<MatrixA>::value && mtl::traits::is_static<MatrixB>::value
	                          && mtl::traits::is_static<MatrixC>::value;
    typedef static_switch_dmat_dmat_mult_t<all_static, fixed_size_t, variable_size_t>  default_functor_t;

    /// Use user-defined functor if provided (assign mode can be arbitrary)
    typedef typename boost::mpl::if_<
	detail::dmat_dmat_mult_specialize<MatrixA, MatrixB, MatrixC>
      , typename detail::dmat_dmat_mult_specialize<MatrixA, MatrixB, MatrixC>::type
      , default_functor_t
    >::type raw_functor_type;

    /// Finally substitute assign mode (consistently)
    typename assign::mult_assign_mode<raw_functor_type, Assign>::type functor;

    functor(A, b, c);
}

template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::dense>, tag::flat<tag::dense>, tag::flat<tag::sparse>)
{
    vampir_trace<4012> tracer;
    // This is a useless and extremely inefficient operation!!!!
    // We compute this with a dense matrix and copy the result back
    dense2D<typename Collection<MatrixC>::value_type, mat::parameters<> > c_copy(num_rows(c), num_cols(c));
    c_copy= c;
    mat_mat_mult(A, b, c_copy, Assign(), tag::flat<tag::dense>(), tag::flat<tag::dense>(), tag::flat<tag::dense>());
    c= c_copy;
}

/// Sparse matrix multiplication
template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::sparse>, tag::flat<tag::sparse>, tag::flat<tag::sparse>)
{
    vampir_trace<4012> tracer;
    smat_smat_mult(A, b, c, Assign(), typename OrientedCollection<MatrixA>::orientation(),
		   typename OrientedCollection<MatrixB>::orientation());
}

template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::sparse>, tag::flat<tag::sparse>, tag::flat<tag::dense>)
{
    vampir_trace<4012> tracer;
    // This is a useless and extremely inefficient operation!!!!
    // We compute this with a sparse matrix and copy the result back
    compressed2D<typename Collection<MatrixC>::value_type, mat::parameters<> > c_copy(num_rows(c), num_cols(c));
    c_copy= c;
    smat_smat_mult(A, b, c_copy, Assign(), typename OrientedCollection<MatrixA>::orientation(),
		   typename OrientedCollection<MatrixB>::orientation());
    c= c_copy;
}

/// Product of sparse times dense matrix
/**  This function (specialization of mult) is intended to multiply sparse matrices with multiple matrices
     gathered into a dense matrix.  Likewise, the resulting dense matrix corresponds to multiple vectors.
     The default functor for this operation is:
     -# Use tiled multiplication      if available, otherwise
     -# Naive multiplication
**/
template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::sparse>, tag::flat<tag::dense>, tag::flat<tag::dense>)
{
    vampir_trace<4012> tracer;
    using assign::plus_sum; using assign::assign_sum;
    using namespace functor;

    // static const unsigned long tiling1= detail::dmat_dmat_mult_tiling1<MatrixA, MatrixB, MatrixC>::value;

    //typedef gen_smat_dmat_mult<Assign>                         default_functor_t;
    typedef gen_tiling_smat_dmat_mult<8, Assign>                         default_functor_t;

    // Finally substitute assign mode (consistently)
    // typename assign::mult_assign_mode<raw_functor_type, Assign>::type functor;

    default_functor_t functor;
    functor(A, b, c);
}

template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::sparse>, tag::flat<tag::dense>, tag::flat<tag::sparse>)
{
    vampir_trace<4012> tracer;
    // This is a useless and extremely inefficient operation!!!!
    // We compute this with a sparse matrix and copy the result back
    dense2D<typename Collection<MatrixC>::value_type, mat::parameters<> > c_copy(num_rows(c), num_cols(c));
    c_copy= c;
    mat_mat_mult(A, b, c_copy, Assign(), tag::flat<tag::sparse>(), tag::flat<tag::dense>(), tag::flat<tag::dense>());
    c= c_copy;
}

template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::dense>, tag::flat<tag::sparse>, tag::flat<tag::dense>)
{
    vampir_trace<4012> tracer;
    // This is could be a usefull operation, i.e. multiplying multiple row vectors with a sparse matrix
    // Might be supported in future
    // Now we compute this with a sparse matrix as first argument
    compressed2D<typename Collection<MatrixA>::value_type, mat::parameters<> > A_copy(num_rows(A), num_cols(A));
    A_copy= A;
    compressed2D<typename Collection<MatrixC>::value_type, mat::parameters<> > c_copy(num_rows(c), num_cols(c));
    c_copy= c;
    mat_mat_mult(A_copy, b, c_copy, Assign(), tag::flat<tag::sparse>(), tag::flat<tag::sparse>(), tag::flat<tag::sparse>());
    c= c_copy;
}

template <typename MatrixA, typename MatrixB, typename MatrixC, typename Assign>
inline void mat_mat_mult(const MatrixA& A, const MatrixB& b, MatrixC& c, Assign, tag::flat<tag::dense>, tag::flat<tag::sparse>, tag::flat<tag::sparse>)
{
    vampir_trace<4012> tracer;
    // This is not a usefull operation, because the result is dense
    // Now we compute this with a sparse matrix as first argument
    compressed2D<typename Collection<MatrixA>::value_type, mat::parameters<> > A_copy(num_rows(A), num_cols(A));
    A_copy= A;
    mat_mat_mult(A_copy, b, c, Assign(), tag::flat<tag::sparse>(), tag::flat<tag::sparse>(), tag::flat<tag::sparse>());
}

// Matrix vector multiplication
template <typename Matrix, typename VectorIn, typename VectorOut, typename Assign>
inline void gen_mult(const Matrix& A, const VectorIn& v, VectorOut& w, Assign, tag::flat<tag::matrix>, tag::flat<tag::col_vector>, tag::flat<tag::col_vector>)
{
    vampir_trace<4011> tracer;
    // Vector must be column vector
    // If vector is row vector then matrix must have one column and the operation is a outer product
    //   -> result should be a matrix too

    // Check if element types are compatible (in contrast to tag dispatching, nesting is considered here)
    MTL_STATIC_ASSERT((boost::is_same< typename ashape::mult_op<typename ashape::ashape<Matrix>::type,
			                                          typename ashape::ashape<VectorIn>::type >::type,
			                 ::mtl::ashape::mat_cvec_mult
				     >::value),
		      "The type nesting of the arguments does not allow for a consistent matrix vector product.");

#if 1
    MTL_DEBUG_THROW_IF((void*)&v == (void*)&w, argument_result_conflict());
#else
    if ((void*)&v == (void*)&w) {
	VectorOut tmp(size(w));
	mult(A, b, tmp);
	swap(w, tmp);
	return;
    }
#endif
    // w.checked_change_dim(num_rows(A)); // destroys distribution in parallel -> dimension changed in assignment
    MTL_DEBUG_THROW_IF(num_rows(A) != mtl::size(w), incompatible_size());
    MTL_DEBUG_THROW_IF(num_cols(A) != mtl::size(v), incompatible_size());

    mat_cvec_mult(A, v, w, Assign(), mtl::traits::mat_cvec_flatcat<Matrix>());
}

// Vector matrix multiplication
template <typename VectorIn, typename Matrix, typename VectorOut, typename Assign>
inline void gen_mult(const VectorIn& v, const Matrix& A, VectorOut& w, Assign, tag::flat<tag::row_vector>, tag::flat<tag::matrix>, tag::flat<tag::row_vector>)
{
    vampir_trace<4011> tracer;
    // Vector must be column vector
    // If vector is row vector then matrix must have one column and the operation is a outer product
    //   -> result should be a matrix too

    // Check if element types are compatible (in contrast to tag dispatching, nesting is considered here)
    MTL_STATIC_ASSERT((boost::is_same< typename ashape::mult_op<typename ashape::ashape<VectorIn>::type,
			                                          typename ashape::ashape<Matrix>::type >::type,
			                 ::mtl::ashape::rvec_mat_mult
			             >::value),
		      "The type nesting of the arguments does not allow for a consistent matrix vector product.");

#if 1
    MTL_DEBUG_THROW_IF((void*)&v == (void*)&w, argument_result_conflict());
#else
    if ((void*)&v == (void*)&w) {
	VectorOut tmp(size(w));
	mult(A, b, tmp);
	swap(w, tmp);
	return;
    }
#endif
    // w.checked_change_dim(num_cols(A));
    w.checked_change_resource(v);
    MTL_DEBUG_THROW_IF(num_cols(v) != num_rows(A), incompatible_size());

    // same dispatching criterion as mat_cvec_mult (until now)
    rvec_mat_mult(v, A, w, Assign(), typename mtl::traits::mat_cvec_flatcat<Matrix>::type());
}

}} // namespace mtl::matrix

#endif // MTL_MULT_INCLUDE


/*
 * Copyright (c) 2012, Frederic Dubouchet
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the CERN nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Frederic Dubouchet ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Frederic DUBOUCHET BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <vector>
#define __CL_ENABLE_EXCEPTIONS
#include <cl.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>

#include "cl_util.h"
#include "cl_floyd_warshall.h"
#include "ewd_file.h"

using namespace cl;
using namespace boost::program_options;
using namespace boost::posix_time;

int main(int ac, char** av) {
	bool enable_gpu = true;
	unsigned int nb_loops = 1;
	std::string cl_file = "floyd_warshall.cl";
	std::string graph_in = "rome99.txt";
	std::string graph_out = "";
	try {
		// parse command line
		options_description desc("Allowed options");
		desc.add_options()
		("help,h", "produce help message")
		("opencl-cpu,c", "compute using openCL on CPU")
		("opencl-gpu,g", "compute using openCL on GPU (default)")
		("opencl-file,f", value<std::string>(), "input cl file")
		("file-in,i", value<std::string>(),
		 "Input graph file in EWD format")
		("file-out,o", value<std::string>(),
		 "Output graph file in EWD format")
		("loop,l", value<unsigned int>(),
		 "number of loops (only in no-window mode)")
		;
		variables_map vm;
		store(command_line_parser(ac, av).options(desc).run(), vm);
		if (vm.count("help")) {
			std::cout << desc << std::endl;
			return 1;
		}
		if (vm.count("opencl-cpu")) {
			enable_gpu = false;
		}
		if (vm.count("opencl-gpu")) {
			enable_gpu = true;
		}
		std::cout << "OpenCL (" << ((enable_gpu) ? "GPU" : "CPU");
		std::cout << ")    : enable" << std::endl;
		if (vm.count("file-in")) {
			graph_in = vm["file-in"].as<std::string>();
		}
		std::cout << "Graph file (in) : " << graph_in << std::endl;
		if (vm.count("file-out")) {
			graph_out = vm["file-out"].as<std::string>();
			std::cout << "Graph file (out): " << graph_out << std::endl;
		}
		if (vm.count("opencl-file")) {
			cl_file = vm["opencl-file"].as<std::string>();
			std::cout << "OpenCL file     : " << cl_file << std::endl;
		}
		if (vm.count("loop")) {
			nb_loops = vm["loop"].as<unsigned int>();
		}
		std::cout << "Loop            : " << nb_loops << std::endl;
		{
			// read the file with the data
			ewd_file ef;
			ef.import_file(graph_in);
			std::vector<float> initial_state(ef.size() * ef.size());
			ef.export_matrix(&initial_state[0], initial_state.size());
			if (initial_state.size() <= 256) ef.print_matrix(std::cout);
			// do the actual job
			cl_floyd_warshall cfw(enable_gpu);
			cfw.init(cl_file);
			std::cout
			<< "Setup           : (" << ef.size() << ", "
			<< ef.size() << ")" << std::endl;
			cfw.setup(std::make_pair<unsigned int, unsigned int>(
				ef.size(),
				ef.size()));
			time_duration best_time = minutes(60);
			for (int i = 0; i < nb_loops; ++i) {
				time_duration actual_time;
				std::cout
				<< "Prepare (buffer): " << initial_state.size() << std::endl;
				cfw.prepare(initial_state);
				std::cout
				<< "Run (buffer)    : " << initial_state.size() << std::endl;
				actual_time = cfw.run(initial_state);
				if (actual_time < best_time) best_time = actual_time;
				std::cout
				<< "\riteration [" << i + 1 << "/" << nb_loops << "] : "
				<< "actual_time : " << actual_time;
				std::cout.flush();
			}
			std::cout << std::endl
			<< "Finished : [" << nb_loops << "] Best time : " << best_time
			<< std::endl;
			if (graph_out.size() || initial_state.size() <= 256)
				ef.import_matrix(&initial_state[0], initial_state.size());
			// export to file out (in case exist)
			if (graph_out.size()) {
				ef.export_file(graph_out);
			}
			if (initial_state.size() <= 256) ef.print_matrix(std::cout);
		}
		// error handling
	} catch (cl::Error er) {
		std::cerr
		<< "exception (CL)  : " << er.what()
		<< "(" << er << ")" << std::endl;
		return -2;
	} catch (std::exception& ex) {
		std::cerr << "exception (std) : " << ex.what() << std::endl;
		return -1;
	}
	return 0;
}


//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright Contributors to the OpenEXR Project.
//

// clang-format off

#include "PyImathQuat.h"
#include "PyImathExport.h"
#include "PyImathDecorators.h"
#include <Python.h>
#include <boost/python.hpp>
#include <boost/python/make_constructor.hpp>
#include <boost/format.hpp>
#include "PyImath.h"
#include "PyImathMathExc.h"
#include <ImathVec.h>
#include <ImathMatrixAlgo.h>
#include <ImathEuler.h>
#include "PyImathOperators.h"

// XXX incomplete array wrapping, docstrings missing

namespace PyImath {
template <> const char *PyImath::QuatfArray::name() { return "QuatfArray"; }
template <> const char *PyImath::QuatdArray::name() { return "QuatdArray"; }
}

namespace PyImath {
using namespace boost::python;
using namespace IMATH_NAMESPACE;

template <class T> struct QuatName { static const char *value; };
template<> const char *QuatName<float>::value  = "Quatf";
template<> const char *QuatName<double>::value = "Quatd";

template <class T>
static std::string Quat_str(const Quat<T> &v)
{
    std::stringstream stream;
    stream << QuatName<T>::value << "(" << v[0] << ", " << v[1] << ", "
           << v[2] << ", " << v[3] << ")";
    return stream.str();
}

// Non-specialized repr is same as str
template <class T>
static std::string Quat_repr(const Quat<T> &v)
{
    return Quat_str(v);
}

// Specialization for float to full precision
template <>
std::string Quat_repr(const Quat<float> &v)
{
    return (boost::format("%s(%.9g, %.9g, %.9g, %.9g)")
                        % QuatName<float>::value
                        % v[0] % v[1] % v[2] % v[3]).str();
}

// Specialization for double to full precision
template <>
std::string Quat_repr(const Quat<double> &v)
{
    return (boost::format("%s(%.17g, %.17g, %.17g, %.17g)")
                        % QuatName<double>::value
                        % v[0] % v[1] % v[2] % v[3]).str();
}

template <class T>
static Quat<T> &
invert(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.invert();
}

template <class T>
static Quat<T>
inverse(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.inverse();
}

template <class T>
static Quat<T> &
normalize(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.normalize();
}

template <class T>
static Quat<T>
normalized(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.normalized();
}

template <class T>
static T
length (Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.length();
}

template <class T>
static Quat<T> &
setAxisAngle(Quat<T> &quat, const Vec3<T> &axis, T radians)
{
    MATH_EXC_ON;
    return quat.setAxisAngle(axis, radians);
}

template <class T>
static Quat<T> &
setRotation(Quat<T> &quat, const Vec3<T> &from, const Vec3<T> &to)
{
    MATH_EXC_ON;
    return quat.setRotation(from, to);
}

template <class T>
static T
angle (Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.angle();
}

template <class T>
static Vec3<T>
axis (Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.axis();
}

template <class T>
static Matrix33<T>
toMatrix33 (Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.toMatrix33();
}

template <class T>
static Matrix44<T>
toMatrix44 (Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.toMatrix44();
}

template <class T>
static Quat<T>
log(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.log();
}

template <class T>
static Quat<T>
exp(Quat<T> &quat)
{
    MATH_EXC_ON;
    return quat.exp();
}

template <class T>
static void
setR(Quat<T> &quat, const double &r)
{
    quat.r = r;
}

template <class T>
static void
setV(Quat<T> &quat, const Vec3<T> &v)
{
    quat.v = v;
}

template <class T>
static void
extract(Quat<T> &quat, const Matrix44<T> &mat)
{
    MATH_EXC_ON;
    Quat<T> q = IMATH_NAMESPACE::extractQuat(mat);
    quat.r = q.r;
    quat.v = q.v;
}

template <class T>
static T scalar(Quat<T> &quat)
{
    return quat.r;
}

template <class T>
static Vec3<T> vector(Quat<T> &quat)
{
    return quat.v;
}

template <class T>
static Quat<T>
slerp(const Quat<T> &quat, const Quat<T> &other, T t)
{
    MATH_EXC_ON;
    return IMATH_NAMESPACE::slerp (quat, other, t);
}

template <class T>
static const Quat<T> &
imul (Quat<T> &quat, const Quat<T> &other)
{
    MATH_EXC_ON;
    return quat *= other;
}

template <class T>
static const Quat<T> &
imulT (Quat<T> &quat, T t)
{
    MATH_EXC_ON;
    return quat *= t;
}

template <class T>
static const Quat<T> &
idiv (Quat<T> &quat, const Quat<T> &other)
{
    MATH_EXC_ON;
    return quat /= other;
}

template <class T>
static const Quat<T> &
idivT (Quat<T> &quat, T t)
{
    MATH_EXC_ON;
    return quat /= t;
}

template <class T>
static const Quat<T> &
iadd (Quat<T> &quat, const Quat<T> &other)
{
    MATH_EXC_ON;
    return quat += other;
}

template <class T>
static const Quat<T> &
isub (Quat<T> &quat, const Quat<T> &other)
{
    MATH_EXC_ON;
    return quat -= other;
}

template <class T>
static Matrix33<T>
rmulM33(Quat<T> &quat, Matrix33<T> &m)
{
    MATH_EXC_ON;
    return m * quat;
}

template <class T>
static Matrix33<T>
mulM33(Quat<T> &quat, Matrix33<T> &m)
{
    MATH_EXC_ON;
    return quat * m;
}

template <class T>
static Quat<T>
mul(Quat<T> &quat, Quat<T> &other)
{
    MATH_EXC_ON;
    return quat * other;
}

template <class T>
static Quat<T>
div(Quat<T> &quat, Quat<T> &other)
{
    MATH_EXC_ON;
    return quat / other;
}

template <class T>
static Quat<T>
divT(Quat<T> &quat, T t)
{
    MATH_EXC_ON;
    return quat / t;
}

template <class T>
static Quat<T>
mulT(Quat<T> &quat, T t)
{
    MATH_EXC_ON;
    return quat * t;
}

template <class T>
static Quat<T>
add(Quat<T> &quat, Quat<T> &other)
{
    MATH_EXC_ON;
    return quat + other;
}

template <class T>
static Quat<T>
sub(Quat<T> &quat, Quat<T> &other)
{
    MATH_EXC_ON;
    return quat - other;
}

template <class T>
static Quat<T>
neg(Quat<T> &quat)
{
    MATH_EXC_ON;
    return -quat;
}

template <class T>
static Quat<T>
conj(Quat<T> &quat)
{
    MATH_EXC_ON;
    return ~quat;
}

template <class T>
static T
dot(Quat<T> &quat, Quat<T> &other)
{
    MATH_EXC_ON;
    return quat ^ other;
}

template <class T>
static Vec3<T>
rmulVec3(Quat<T> &quat, const Vec3<T> &v)
{
    MATH_EXC_ON;
    return v * quat.toMatrix44();
}

template <class T>
static FixedArray< Vec3<T> >
rmulVec3Array(Quat<T> &quat, const FixedArray< Vec3<T> > &a)
{
    MATH_EXC_ON;
    Matrix44<T> m = quat.toMatrix44();
    size_t len = a.len();
    FixedArray< Vec3<T> > r(len);
    for (size_t i = 0; i < len; i++)
        r[i] = a[i] * m;
    return r;
}

template <class T>
static Quat<T> *
quatConstructor1(const Euler<T> &euler)
{
    MATH_EXC_ON;
    return new Quat<T>(euler.toQuat());
}

template <class T>
static Quat<T> *
quatConstructor2(const Matrix33<T> &mat)
{
    MATH_EXC_ON;
    return new Quat<T>(Euler<T>(mat).toQuat());
}

template <class T>
static Quat<T> *
quatConstructor3(const Matrix44<T> &mat)
{
    MATH_EXC_ON;
    return new Quat<T>(Euler<T>(mat).toQuat());
}

template <class T>
class_<Quat<T> >
register_Quat()
{
    class_<Quat<T> > quat_class(QuatName<T>::value, QuatName<T>::value,init<Quat<T> >("copy construction"));
    quat_class
        .def(init<>("imath Quat initialization") )
        .def(init<Quat<float> >("imath Quat copy initialization") )
        .def(init<Quat<double> >("imath Quat copy initialization") )
        .def(init<T,T,T,T>("make Quat from components") )
        .def(init<T, Vec3<T> >("make Quat from components") )
        .def("__init__", make_constructor(quatConstructor1<T>))
        .def("__init__", make_constructor(quatConstructor2<T>))
        .def("__init__", make_constructor(quatConstructor3<T>))
        .def("identity",&Quat<T>::identity)
        .def("invert",&invert<T>,return_internal_reference<>(),
        	 "q.invert() -- inverts quaternion q\n"
			 "(modifying q); returns q")

        .def("inverse",&inverse<T>,
        	 "q.inverse() -- returns the inverse of\n"
			 "quaternion q; q is not modified\n")

        .def("normalize",&normalize<T>,return_internal_reference<>(),
        	 "q.normalize() -- normalizes quaternion q\n"
			 "(modifying q); returns q")

        .def("normalized",&normalized<T>,
        	 "q.normalized() -- returns a normalized version\n"
			 "of quaternion q; q is not modified\n")

        .def("length",&length<T>)
        .def("setAxisAngle",&setAxisAngle<T>,return_internal_reference<>(),
			"q.setAxisAngle(x,r) -- sets the value of\n"
			"quaternion q so that q represents a rotation\n"
			"of r radians around axis x")

        .def("setRotation",&setRotation<T>,return_internal_reference<>(),
        	 "q.setRotation(v,w) -- sets the value of\n"
			 "quaternion q so that rotating vector v by\n"
			 "q produces vector w")

        .def("angle",&angle<T>,
        	 "q.angle() -- returns the rotation angle\n"
			 "(in radians) represented by quaternion q")

        .def("axis",&axis<T>,
        	 "q.axis() -- returns the rotation axis\n"
			 "represented by quaternion q")

        .def("toMatrix33",&toMatrix33<T>,
             "q.toMatrix33() -- returns a 3x3 matrix that\n"
			 "represents the same rotation as quaternion q")

        .def("toMatrix44",&toMatrix44<T>,
        	 "q.toMatrix44() -- returns a 4x4 matrix that\n"
			 "represents the same rotation as quaternion q")

        .def("log",&log<T>)
        .def("exp",&exp<T>)
        .def_readwrite("v",&Quat<T>::v)
        .def_readwrite("r",&Quat<T>::r)
        .def("v", &vector<T>,
			  "q.v() -- returns the v (vector) component\n"
			  "of quaternion q")

        .def("r", &scalar<T>,
        	 "q.r() -- returns the r (scalar) component\n"
			 "of quaternion q")

        .def("setR", &setR<T>,
        	 "q.setR(s) -- sets the r (scalar) component\n"
			 "of quaternion q to s")

        .def("setV", &setV<T>,
        	 "q.setV(w) -- sets the v (vector) component\n"
			 "of quaternion q to w")

        .def("extract", &extract<T>,
        	 "q.extract(m) -- extracts the rotation component\n"
			 "from 4x4 matrix m and stores the result in q")

        .def("slerp", &slerp<T>,
        	 "q.slerp(p,t) -- performs sperical linear\n"
			 "interpolation between quaternions q and p:\n"
			 "q.slerp(p,0) returns q; q.slerp(p,1) returns p.\n"
			 "q and p must be normalized\n")

        .def("__str__",Quat_str<T>)
        .def("__repr__",Quat_repr<T>)
        .def ("__imul__", &imul<T>, return_internal_reference<>())
        .def ("__imul__", &imulT<T>, return_internal_reference<>())
        .def ("__idiv__", idiv<T>, return_internal_reference<>())
        .def ("__idiv__", &idivT<T>, return_internal_reference<>())
        .def ("__itruediv__", idiv<T>, return_internal_reference<>())
        .def ("__itruediv__", &idivT<T>, return_internal_reference<>())
        .def ("__iadd__", &iadd<T>, return_internal_reference<>())
        .def ("__isub__", &isub<T>, return_internal_reference<>())
        .def(self == self) // NOSONAR - suppress SonarCloud bug report.
        .def(self != self) // NOSONAR - suppress SonarCloud bug report.
        .def ("__rmul__", &rmulM33<T>)
        .def ("__mul__", &mulM33<T>)
        .def ("__mul__", &mul<T>)
        .def ("__div__", &div<T>)
        .def ("__div__", &divT<T>)
        .def ("__truediv__", &div<T>)
        .def ("__truediv__", &divT<T>)
        .def ("__mul__", &mulT<T>)
        .def ("__rmul__", &mulT<T>)
        .def ("__add__", &add<T>)
        .def ("__sub__", &sub<T>)
        .def ("__neg__", &neg<T>)
        .def ("__invert__", &conj<T>)
        .def ("__xor__", &dot<T>)
        .def ("__rmul__", &rmulVec3<T>)
        .def ("__rmul__", &rmulVec3Array<T>)
        ;

    decoratecopy(quat_class);

    return quat_class;
}

// XXX fixme - template this
// really this should get generated automatically...

template <class T,int index>
static FixedArray<T>
QuatArray_get(FixedArray<IMATH_NAMESPACE::Quat<T> > &qa)
{
    return FixedArray<T>( &(qa[0].r)+index, qa.len(), 4*qa.stride(), qa.handle());
}

template <class T>
struct QuatArray_SetRotationTask : public Task
{
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &from;
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &to;
    FixedArray<IMATH_NAMESPACE::Quat<T> >       &result;

    QuatArray_SetRotationTask (const FixedArray<IMATH_NAMESPACE::Vec3<T> > &fromIn,
                               const FixedArray<IMATH_NAMESPACE::Vec3<T> > &toIn,
                               FixedArray<IMATH_NAMESPACE::Quat<T> >       &resultIn)
        : from (fromIn), to (toIn), result (resultIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
            result[i].setRotation (from[i], to[i]);
    }
};

template <class T> static void
QuatArray_setRotation (FixedArray<IMATH_NAMESPACE::Quat<T> > &va,
                       const FixedArray<IMATH_NAMESPACE::Vec3<T> > &from,
                       const FixedArray<IMATH_NAMESPACE::Vec3<T> > &to)
{
    MATH_EXC_ON;
    size_t len = va.match_dimension(from);
    va.match_dimension(to);

    QuatArray_SetRotationTask<T> task (from, to, va);
    dispatchTask (task, len);
}

template <class T>
struct QuatArray_OrientToVectors : public Task
{
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &forward;
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &up;
    FixedArray<IMATH_NAMESPACE::Quat<T> >       &result;
    bool alignForward;

    QuatArray_OrientToVectors (const FixedArray<IMATH_NAMESPACE::Vec3<T> > &forwardIn,
                               const FixedArray<IMATH_NAMESPACE::Vec3<T> > &upIn,
                               FixedArray<IMATH_NAMESPACE::Quat<T> >       &resultIn,
                               bool alignForwardIn)
        : forward (forwardIn), up (upIn), result (resultIn),
          alignForward (alignForwardIn) {}

    void execute (size_t start, size_t end)
    {
        Vec3<T> f(0), u(0);
        Euler<T> eu(0,0,0);
        const Vec3<T> fRef(1,0,0);

        for (size_t i = start; i < end; ++i)
        {
            if (alignForward)
            {
                f = forward[i].normalized();
                u = up[i] - f.dot(up[i])*f;
                u.normalize();
            }
            else
            {
                u = up[i].normalized();
                f = forward[i] - u.dot(forward[i])*u;
                f.normalize();
            }

            extractEulerXYZ (rotationMatrixWithUpDir (fRef, f, u), eu);
            result[i] = eu.toQuat();
        }
    }
};

template <class T> static void
QuatArray_orientToVectors (FixedArray<IMATH_NAMESPACE::Quat<T> >       &va,
                           const FixedArray<IMATH_NAMESPACE::Vec3<T> > &forward,
                           const FixedArray<IMATH_NAMESPACE::Vec3<T> > &up,
                           bool alignForward)
{
    MATH_EXC_ON;
    size_t len = va.match_dimension(forward);
    va.match_dimension(up);

    QuatArray_OrientToVectors<T> task (forward, up, va, alignForward);
    dispatchTask (task, len);
}

template <class T>
struct QuatArray_Axis : public Task
{
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &va;
    FixedArray<IMATH_NAMESPACE::Vec3<T> >       &result;

    QuatArray_Axis (const FixedArray<IMATH_NAMESPACE::Quat<T> > &vaIn,
                    FixedArray<IMATH_NAMESPACE::Vec3<T> >       &resultIn)
        : va (vaIn), result (resultIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
            result[i] = va[i].axis();
    }
};

template <class T> static FixedArray<IMATH_NAMESPACE::Vec3<T> >
QuatArray_axis(const FixedArray<IMATH_NAMESPACE::Quat<T> > &va)
{
    MATH_EXC_ON;
    size_t len = va.len();
    FixedArray<IMATH_NAMESPACE::Vec3<T> > retval (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_Axis<T> task (va, retval);
    dispatchTask (task, len);
    return retval;
}

template <class T>
struct QuatArray_Angle : public Task
{
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &va;
    FixedArray<T>                               &result;

    QuatArray_Angle (const FixedArray<IMATH_NAMESPACE::Quat<T> > &vaIn,
                     FixedArray<T>                               &resultIn)
        : va (vaIn), result (resultIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
            result[i] = va[i].angle();
    }
};

template <class T> static FixedArray<T>
QuatArray_angle(const FixedArray<IMATH_NAMESPACE::Quat<T> > &va)
{
    MATH_EXC_ON;
    size_t len = va.len();
    FixedArray<T> retval (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_Angle<T> task (va, retval);
    dispatchTask (task, len);
    return retval;
}

template <class T>
struct QuatArray_RmulVec3 : public Task
{
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &a;
    const Vec3<T>                               &v;
    FixedArray<Vec3<T> >                        &r;

    QuatArray_RmulVec3 (const FixedArray<IMATH_NAMESPACE::Quat<T> > &aIn,
                        const Vec3<T> &vIn, FixedArray<Vec3<T> > &rIn)
        : a (aIn), v (vIn), r (rIn) {}

    void execute(size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            Matrix44<T> m = a[i].toMatrix44();
            r[i] = v * m;
        }
    }
};

template <class T>
static FixedArray< Vec3<T> >
QuatArray_rmulVec3 (const FixedArray< IMATH_NAMESPACE::Quat<T> > &a, const Vec3<T> &v)
{
    MATH_EXC_ON;
    size_t len = a.len();
    FixedArray< Vec3<T> > r (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_RmulVec3<T> task (a, v, r);
    dispatchTask (task, len);
    return r;
}

template <class T>
struct QuatArray_RmulVec3Array : public Task
{
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &a;
    const FixedArray<Vec3<T> >                  &b;
    FixedArray<Vec3<T> >                        &r;

    QuatArray_RmulVec3Array (const FixedArray<IMATH_NAMESPACE::Quat<T> > &aIn,
                             const FixedArray<Vec3<T> > &bIn,
                             FixedArray<Vec3<T> > &rIn)
        : a (aIn), b (bIn), r (rIn) {}

    void execute(size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            Matrix44<T> m = a[i].toMatrix44();
            r[i] = b[i] * m;
        }
    }
};

template <class T>
static FixedArray< Vec3<T> >
QuatArray_rmulVec3Array (const FixedArray< IMATH_NAMESPACE::Quat<T> > &a,
                         const FixedArray< Vec3<T> > &b)
{
    MATH_EXC_ON;
    size_t len = a.match_dimension(b);
    FixedArray< Vec3<T> > r (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_RmulVec3Array<T> task (a, b, r);
    dispatchTask (task, len);
    return r;
}

template <class T>
struct QuatArray_SetAxisAngle : public Task
{
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &axis;
    const FixedArray<T>                         &angles;
    FixedArray<IMATH_NAMESPACE::Quat<T> >       &quats;

    QuatArray_SetAxisAngle (const FixedArray<IMATH_NAMESPACE::Vec3<T> > &axisIn,
                            const FixedArray<T>                         &anglesIn,
                            FixedArray<IMATH_NAMESPACE::Quat<T> >       &quatsIn)
        : axis (axisIn), angles (anglesIn), quats (quatsIn) {}

    void execute(size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            quats[i].setAxisAngle (axis[i], angles[i]);
        }
    }
};

template <class T>
static void
QuatArray_setAxisAngle (FixedArray< IMATH_NAMESPACE::Quat<T> > &quats,
                        const FixedArray< IMATH_NAMESPACE::Vec3<T> > &axis,
                        const FixedArray<T> &angles)
{
    MATH_EXC_ON;
    size_t len = quats.match_dimension(axis);
    quats.match_dimension(angles);

    QuatArray_SetAxisAngle<T> task (axis, angles, quats);
    dispatchTask (task, len);
}

template <class T>
struct QuatArray_SetEulerXYZ : public Task
{
    const FixedArray<IMATH_NAMESPACE::Vec3<T> > &rot;
    FixedArray<IMATH_NAMESPACE::Quat<T> >       &quats;

    QuatArray_SetEulerXYZ (const FixedArray<IMATH_NAMESPACE::Vec3<T> > &rotIn,
                           FixedArray<IMATH_NAMESPACE::Quat<T> >       &quatsIn)
        : rot (rotIn), quats (quatsIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            Eulerf e(rot[i]);
            quats[i] = e.toQuat();
        }
    }
};

template <class T>
static void
QuatArray_setEulerXYZ (FixedArray< IMATH_NAMESPACE::Quat<T> > &quats,
                       const FixedArray< IMATH_NAMESPACE::Vec3<T> > &rot)
{
    MATH_EXC_ON;
    size_t len = quats.match_dimension(rot);

    QuatArray_SetEulerXYZ<T> task (rot, quats);
    dispatchTask (task, len);
}

template <class T>
struct QuatArray_Mul : public Task
{
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &q1;
    const FixedArray<IMATH_NAMESPACE::Quat<T> > &q2;
    FixedArray<IMATH_NAMESPACE::Quat<T> >       &result;

    QuatArray_Mul (const FixedArray<IMATH_NAMESPACE::Quat<T> > &q1In,
                   const FixedArray<IMATH_NAMESPACE::Quat<T> > &q2In,
                   FixedArray<IMATH_NAMESPACE::Quat<T> >       &resultIn)
        : q1 (q1In), q2 (q2In), result (resultIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            result[i] = q1[i] * q2[i];
        }
    }
};

template <class T>
static FixedArray< IMATH_NAMESPACE::Quat<T> >
QuatArray_mul(const FixedArray< IMATH_NAMESPACE::Quat<T> > &q1,
              const FixedArray< IMATH_NAMESPACE::Quat<T> > &q2)
{
    MATH_EXC_ON;
    size_t len = q1.match_dimension(q2);
    FixedArray< IMATH_NAMESPACE::Quat<T> > result (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_Mul<T> task (q1, q2, result);
    dispatchTask (task, len);
    return result;
}

template <class T>
struct QuatArray_QuatConstructor1 : public Task
{
    const FixedArray<IMATH_NAMESPACE::Euler<T> > &euler;
    FixedArray<IMATH_NAMESPACE::Quat<T> >        &result;

    QuatArray_QuatConstructor1 (const FixedArray<IMATH_NAMESPACE::Euler<T> > &eulerIn,
                                FixedArray<IMATH_NAMESPACE::Quat<T> >        &resultIn)
        : euler (eulerIn), result (resultIn) {}

    void execute (size_t start, size_t end)
    {
        for (size_t i = start; i < end; ++i)
        {
            result[i] = euler[i].toQuat();
        }
    }
};

template <class T>
static FixedArray<IMATH_NAMESPACE::Quat<T> > *
QuatArray_quatConstructor1(const FixedArray<IMATH_NAMESPACE::Euler<T> > &e)
{
    MATH_EXC_ON;
    size_t len = e.len();
    FixedArray<IMATH_NAMESPACE::Quat<T> >* result =
        new FixedArray<IMATH_NAMESPACE::Quat<T> > (Py_ssize_t(len), UNINITIALIZED);

    QuatArray_QuatConstructor1<T> task (e, *result);
    dispatchTask (task, len);
    return result;
}

template <class T>
class_<FixedArray<IMATH_NAMESPACE::Quat<T> > >
register_QuatArray()
{
    class_<FixedArray<IMATH_NAMESPACE::Quat<T> > > quatArray_class = FixedArray<IMATH_NAMESPACE::Quat<T> >::register_("Fixed length array of IMATH_NAMESPACE::Quat");
    quatArray_class
        .add_property("r",&QuatArray_get<T,0>)
        .add_property("x",&QuatArray_get<T,1>)
        .add_property("y",&QuatArray_get<T,2>)
        .add_property("z",&QuatArray_get<T,3>)
        .def("setRotation", &QuatArray_setRotation<T>,
             "set rotation angles for each quat",
             (args("from", "to")))
        .def("orientToVectors", &QuatArray_orientToVectors<T>,
             "Sets the orientations to match the given forward and up vectors, "
             "matching the forward vector exactly if 'alignForward' is True, matching "
             "the up vector exactly if 'alignForward' is False.  If the vectors are "
             "already orthogonal, both vectors will be matched exactly.",
             (args("forward", "up", "alignForward")))
        .def("axis", &QuatArray_axis<T>,
             "get rotation axis for each quat")
        .def("angle", &QuatArray_angle<T>,
             "get rotation angle about the axis returned by axis() for each quat")
        .def("setAxisAngle", &QuatArray_setAxisAngle<T>,
             "set the quaternion arrays from a given axis and angle",
             (args("axis", "angle")))
        .def("setEulerXYZ", &QuatArray_setEulerXYZ<T>,
             "set the quaternion arrays from a given euler XYZ angle vector",
             (args("euler")))
        .def("__mul__", &QuatArray_mul<T>)
        .def("__rmul__", &QuatArray_rmulVec3<T>)
        .def("__rmul__", &QuatArray_rmulVec3Array<T>)
        .def("__init__", make_constructor(QuatArray_quatConstructor1<T>))
        ;

    add_comparison_functions(quatArray_class);
    decoratecopy(quatArray_class);

    return quatArray_class;
}

template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Quat<float> > register_Quat<float>();
template PYIMATH_EXPORT class_<IMATH_NAMESPACE::Quat<double> > register_Quat<double>();

template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Quat<float> > > register_QuatArray<float>();
template PYIMATH_EXPORT class_<FixedArray<IMATH_NAMESPACE::Quat<double> > > register_QuatArray<double>();
template<> PYIMATH_EXPORT IMATH_NAMESPACE::Quat<float> FixedArrayDefaultValue<IMATH_NAMESPACE::Quat<float> >::value() { return IMATH_NAMESPACE::Quat<float>(); }
template<> PYIMATH_EXPORT IMATH_NAMESPACE::Quat<double> FixedArrayDefaultValue<IMATH_NAMESPACE::Quat<double> >::value() { return IMATH_NAMESPACE::Quat<double>(); }
}


#include <boost/algorithm/string.hpp>
#include <iostream>
#include <limits>
#include <stdint.h>
#include <vector>

const int MAX_STATIONS  = 100000;
const uint32_t INF = std::numeric_limits<uint32_t>::max();

struct Connection {
    uint32_t departure_station, arrival_station;
    uint32_t departure_timestamp, arrival_timestamp;

    // Connection constructor
    Connection(std::string line) {
        boost::trim(line);
        std::vector<std::string> tokens;
        boost::split(tokens, line, boost::is_any_of(" "));

        departure_station = std::stoi(tokens[0]);
        arrival_station = std::stoi(tokens[1]);
        departure_timestamp = std::stoi(tokens[2]);
        arrival_timestamp = std::stoi(tokens[3]);
    }
};

struct Timetable {
    std::vector<Connection> connections;

    // Timetable constructor: reads all the connections from stdin
    Timetable() {
        std::string line;
        getline(std::cin, line);

        while (!line.empty()) {
            connections.push_back( Connection(line) );
            getline(std::cin, line);
        }
    }
};

struct CSA {
    Timetable timetable;
    std::vector<uint32_t> in_connection;
    std::vector<uint32_t> earliest_arrival;

    void main_loop(uint32_t arrival_station) {
        uint32_t earliest = INF;
        for (size_t i = 0; i < timetable.connections.size(); ++i) {
            Connection connection = timetable.connections[i];

            if (connection.departure_timestamp >= earliest_arrival[connection.departure_station] &&
                    connection.arrival_timestamp < earliest_arrival[connection.arrival_station]) {
                earliest_arrival[connection.arrival_station] = connection.arrival_timestamp;
                in_connection[connection.arrival_station] = i;

                if(connection.arrival_station == arrival_station) {
                    earliest = std::min(earliest, connection.arrival_timestamp);
                }
            } else if(connection.arrival_timestamp > earliest) {
              return;
            }
        }
    }

    void print_result(uint32_t arrival_station) {
        if(in_connection[arrival_station] == INF) {
            std::cout << "NO_SOLUTION" << std::endl;
        } else {
            std::vector<Connection> route;
            // We have to rebuild the route from the arrival station
            uint32_t last_connection_index = in_connection[arrival_station];
            while (last_connection_index != INF) {
                Connection connection = timetable.connections[last_connection_index];
                route.push_back(connection);
                last_connection_index = in_connection[connection.departure_station];
            }

            // And now print it out in the right direction
            std::reverse(route.begin(), route.end());
            for (auto connection : route) {
                std::cout << connection.departure_station << " " << connection.arrival_station << " " <<
                    connection.departure_timestamp << " " << connection.arrival_timestamp << std::endl;
            }
        }
        std::cout << std::endl;
    }

    void compute(uint32_t departure_station, uint32_t arrival_station, uint32_t departure_time) {
        in_connection.assign(MAX_STATIONS, INF);
        earliest_arrival.assign(MAX_STATIONS, INF);
        earliest_arrival[departure_station] = departure_time;

        if (departure_station <= MAX_STATIONS && arrival_station <= MAX_STATIONS) {
            main_loop(arrival_station);
        }
        print_result(arrival_station);
    }
};

int main(int, char**) {
    CSA csa;

    std::string line;
    std::vector<std::string> tokens;
    getline(std::cin, line);

    while (!line.empty()) {
        boost::split(tokens, line, boost::is_any_of(" "));
        csa.compute(std::stoi(tokens[0]), std::stoi(tokens[1]), std::stoi(tokens[2]));
        getline(std::cin, line);
    }
}


//---------------------------------------------------------------------------//
//!
//! \file   Utility_ComparePolicy.hpp
//! \author Alex Robinson
//! \brief  Compare policy specializations
//!
//---------------------------------------------------------------------------//

#ifndef UTILITY_COMPARE_POLICY_HPP
#define UTILITY_COMPARE_POLICY_HPP

// Std Lib Includes
#include <string>

// Boost Includes
#include <boost/units/quantity.hpp>
#include <boost/units/io.hpp>

// Trilinos Includes
#include <Teuchos_FancyOStream.hpp>
#include <Teuchos_ScalarTraits.hpp>

// FRENSIE Includes
#include "Utility_ComparePolicyDecl.hpp"
#include "Utility_Tuple.hpp"

namespace Utility{

namespace Policy{

//---------------------------------------------------------------------------//
// ComparePolicy Helper Functions.
//---------------------------------------------------------------------------//

/*! \brief Relative error of two values
 * \ingroup compare_policy
 */
template<typename ScalarType>
ScalarType relError( const ScalarType first_value,
		     const ScalarType second_value )
{
  typedef Teuchos::ScalarTraits<ScalarType> ST;
  typename ST::magnitudeType err;
  if( first_value != ST::zero() && second_value != ST::zero() )
  {
    err = ST::magnitude( first_value - second_value )/
      std::max( ST::magnitude( first_value ),
		ST::magnitude( second_value ) );
  }
  else
  {
    err = std::max( ST::magnitude( first_value ),
		    ST::magnitude( second_value ) );
  }

  return err;
}

/*! \brief Compare the first tuple members
 * \ingroup compare_policy
 */
template<typename T>
bool compareFirstTupleMembers( const T &first_value,
			       const std::string &first_name,
			       const T &second_value,
			       const std::string &second_name,
			       Teuchos::FancyOStream &out,
			       const int index,
			       const double tol )
{
  bool success = true;

  std::stringstream first_tuple_member_name;
  std::stringstream second_tuple_member_name;
  // Array Element Compare
  if( index >= 0 )
  {
    first_tuple_member_name << first_name << "[" << index << "].first";
    second_tuple_member_name << second_name << "[" << index << "].first";
  }
  // Single Tuple Compare
  else
  {
    first_tuple_member_name << first_name << ".first";
    second_tuple_member_name << second_name << ".first";
  }

  success = ComparePolicy<T>::compare( first_value,
				       first_tuple_member_name.str(),
				       second_value,
				       second_tuple_member_name.str(),
				       out,
				       -1,
				       tol );
  return success;
}

/*! \brief Compare the second tuple members
 * \ingroup compare_policy
 */
template<typename T>
bool compareSecondTupleMembers( const T &first_value,
				const std::string &first_name,
				const T &second_value,
				const std::string &second_name,
				Teuchos::FancyOStream &out,
				const int index,
				const double tol )
{
  bool success = true;

  std::stringstream first_tuple_member_name;
  std::stringstream second_tuple_member_name;
  // Array Element Compare
  if( index >= 0 )
  {
    first_tuple_member_name << first_name << "[" << index << "].second";
    second_tuple_member_name << second_name << "[" << index << "].second";
  }
  // Single Tuple Compare
  else
  {
    first_tuple_member_name << first_name << ".second";
    second_tuple_member_name << second_name << ".second";
  }

  success = ComparePolicy<T>::compare( first_value,
				       first_tuple_member_name.str(),
				       second_value,
				       second_tuple_member_name.str(),
				       out,
				       -1,
				       tol );
  return success;
}

/*! \brief Compare the third tuple members
 * \ingroup compare_policy
 */
template<typename T>
bool compareThirdTupleMembers( const T &first_value,
			       const std::string &first_name,
			       const T &second_value,
			       const std::string &second_name,
			       Teuchos::FancyOStream &out,
			       const int index,
			       const double tol )
{
  bool success = true;

  std::stringstream first_tuple_member_name;
  std::stringstream second_tuple_member_name;
  // Array Element Compare
  if( index >= 0 )
  {
    first_tuple_member_name << first_name << "[" << index << "].third";
    second_tuple_member_name << second_name << "[" << index << "].third";
  }
  // Single Tuple Compare
  else
  {
    first_tuple_member_name << first_name << ".third";
    second_tuple_member_name << second_name << ".third";
  }

  success = ComparePolicy<T>::compare( first_value,
				       first_tuple_member_name.str(),
				       second_value,
				       second_tuple_member_name.str(),
				       out,
				       -1,
				       tol );
  return success;
}

/*! \brief Compare the fourth tuple members
 * \ingroup compare_policy
 */
template<typename T>
bool compareFourthTupleMembers( const T &first_value,
				const std::string &first_name,
				const T &second_value,
				const std::string &second_name,
				Teuchos::FancyOStream &out,
				const int index,
				const double tol )
{
  bool success = true;

  std::stringstream first_tuple_member_name;
  std::stringstream second_tuple_member_name;
  // Array Element Compare
  if( index >= 0 )
  {
    first_tuple_member_name << first_name << "[" << index << "].fourth";
    second_tuple_member_name << second_name << "[" << index << "].fourth";
  }
  // Single Tuple Compare
  else
  {
    first_tuple_member_name << first_name << ".fourth";
    second_tuple_member_name << second_name << ".fourth";
  }

  success = ComparePolicy<T>::compare( first_value,
				       first_tuple_member_name.str(),
				       second_value,
				       second_tuple_member_name.str(),
				       out,
				       -1,
				       tol );
  return success;
}

/*! \brief The specialization of the Utility::ComparePolicy for const int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const int>
{
  typedef const int scalarType;

  static inline bool compare( const int &first_value,
			      const std::string &first_name,
			      const int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    bool success = true;

    // Array Element Compare
    if( index >= 0 )
    {
      out << "\nError, " << first_name << "[" << index << "]" << " = "
	  << first_value << " == " << second_name << "[" << index << "]"
	  << " = " << second_value << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }
    // Single Compare
    else
    {
      out << first_name << " = " << first_value
	  << " == " << second_name << " = " << second_value
	  << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<int>
{
  typedef int scalarType;

  static inline bool compare( const int &first_value,
			      const std::string &first_name,
			      const int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    return ComparePolicy<const int>::compare( first_value,
					      first_name,
					      second_value,
					      second_name,
					      out,
					      index,
					      tol );
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for const
 * unsigned int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const unsigned int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned int &first_value,
			      const std::string &first_name,
			      const unsigned int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    bool success = true;

    // Array Element Compare
    if( index >= 0 )
    {
      out << "\nError, " << first_name << "[" << index << "]" << " = "
	  << first_value << " == " << second_name << "[" << index << "]"
	  << " = " << second_value << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }
    // Single Compare
    else
    {
      out << first_name << " = " << first_value
	  << " == " << second_name << " = " << second_value
	  << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for unsigned int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<unsigned int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned int &first_value,
			      const std::string &first_name,
			      const unsigned int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    return ComparePolicy<const unsigned int>::compare( first_value,
						       first_name,
						       second_value,
						       second_name,
						       out,
						       index,
						       tol );
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for const
 * unsigned long int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const unsigned long int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned long int &first_value,
			      const std::string &first_name,
			      const unsigned long int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    bool success = true;

    // Array Element Compare
    if( index >= 0 )
    {
      out << "\nError, " << first_name << "[" << index << "]" << " = "
	  << first_value << " == " << second_name << "[" << index << "]"
	  << " = " << second_value << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }
    // Single Compare
    else
    {
      out << first_name << " = " << first_value
	  << " == " << second_name << " = " << second_value
	  << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for unsigned long
 * int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<unsigned long int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned long int &first_value,
			      const std::string &first_name,
			      const unsigned long int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    return ComparePolicy<const unsigned long int>::compare( first_value,
							    first_name,
							    second_value,
							    second_name,
							    out,
							    index,
							    tol );
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for const
 * unsigned long long int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const unsigned long long int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned long long int &first_value,
			      const std::string &first_name,
			      const unsigned long long int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    bool success = true;

    // Array Element Compare
    if( index >= 0 )
    {
      out << "\nError, " << first_name << "[" << index << "]" << " = "
	  << first_value << " == " << second_name << "[" << index << "]"
	  << " = " << second_value << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }
    // Single Compare
    else
    {
      out << first_name << " = " << first_value
	  << " == " << second_name << " = " << second_value
	  << ": ";
      if( first_value != second_value )
      {
	out << "failed!\n";

	success = false;
      }
      else
	out << "passed\n";
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for unsigned long
 * long int.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<unsigned long long int>
{
  typedef unsigned int scalarType;

  static inline bool compare( const unsigned long long int &first_value,
			      const std::string &first_name,
			      const unsigned long long int &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0 )
  {
    return ComparePolicy<const unsigned long long int>::compare( first_value,
								 first_name,
								 second_value,
								 second_name,
								 out,
								 index,
								 tol );
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for const double.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const double>
{
  typedef const double scalarType;
  static inline bool compare( const double &first_value,
			      const std::string &first_name,
			      const double &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    bool success = true;

    if( tol == 0.0 )
    {
      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, " << first_name << "[" << index << "]" << " = "
	    << first_value << " == " << second_name << "[" << index << "]"
	    << " = " << second_value << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << first_name << " = " << first_value
	    << " == " << second_name << " = " << second_value
	    << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }
    else
    {
      double err = relError( first_value, second_value );

      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, relErr(" << first_name << "[" << index << "],"
	    << second_name << "[" << index << "])" << " = relErr("
	    << first_value << "," << second_value << ") = " << err
	    << " <= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << "\nCheck: relErr(" << first_name << "," << second_name << ")"
	    << "\n= relErr(" << first_value << "," << second_value << ") = "
	    << err << "\n<= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for double.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<double>
{
  typedef double scalarType;
  static inline bool compare( const double &first_value,
			      const std::string &first_name,
			      const double &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    return ComparePolicy<const double>::compare( first_value,
						 first_name,
						 second_value,
						 second_name,
						 out,
						 index,
						 tol );
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for const float.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<const float>
{
  typedef const float scalarType;
  static inline bool compare( const float &first_value,
			      const std::string &first_name,
			      const float &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0f )
  {
    bool success = true;

    if( tol == 0.0f )
    {
      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, " << first_name << "[" << index << "]" << " = "
	    << first_value << " == " << second_name << "[" << index << "]"
	    << " = " << second_value << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << first_name << " = " << first_value
	    << " == " << second_name << " = " << second_value
	    << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }
    else
    {
      float err = relError( first_value, second_value );

      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, relErr(" << first_name << "[" << index << "],"
	    << second_name << "[" << index << "])" << " = relErr("
	    << first_value << "," << second_value << ") = " << err
	    << " <= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << "\nCheck: relErr(" << first_name << "," << second_name << ")"
	    << "\n= relErr(" << first_value << "," << second_value << ") = "
	    << err << "\n<= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }

    return success;
  }
};

/*! \brief The specialization of the Utility::ComparePolicy for float.
 * \ingroup compare_policy
 */
template<>
struct ComparePolicy<float>
{
  typedef float scalarType;
  static inline bool compare( const float &first_value,
			      const std::string &first_name,
			      const float &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0f )
  {
    return ComparePolicy<const float>::compare( first_value,
						first_name,
						second_value,
						second_name,
						out,
						index,
						tol );
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * const Utility::Pair.
 * \ingroup compare_policy
 */
template<typename T, typename T2>
struct ComparePolicy<const Pair<T,T2> >
{
  typedef const double scalarType;
  static inline bool compare( const Pair<T,T2> &first_value,
			      const std::string &first_name,
			      const Pair<T,T2> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    bool success = true;

    // Compare the first tuple members
    {
      bool local_success = true;
      local_success = compareFirstTupleMembers<T>( first_value.first,
						   first_name,
						   second_value.first,
						   second_name,
						   out,
						   index,
						   tol );
      if( !local_success )
	success = false;
    }

    // Compare the second tuple members
    {
      bool local_success = true;
      local_success = compareSecondTupleMembers<T2>( first_value.second,
						     first_name,
						     second_value.second,
						     second_name,
						     out,
						     index,
						     tol );
      if( !local_success )
	success = false;
    }

    return success;
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * Utility::Pair.
 * \ingroup compare_policy
 */
template<typename T, typename T2>
struct ComparePolicy<Pair<T,T2> >
{
  typedef double scalarType;
  static inline bool compare( const Pair<T,T2> &first_value,
			      const std::string &first_name,
			      const Pair<T,T2> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    return ComparePolicy<const Pair<T,T2> >::compare( first_value,
						      first_name,
						      second_value,
						      second_name,
						      out,
						      index,
						      tol );
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * const std::pair.
 * \ingroup compare_policy
 */
template<typename T, typename T2>
struct ComparePolicy<const std::pair<T,T2> >
{
  typedef const double scalarType;
  static inline bool compare( const std::pair<T,T2> &first_value,
			      const std::string &first_name,
			      const std::pair<T,T2> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    bool success = true;

    // Compare the first tuple members
    {
      bool local_success = true;
      local_success = compareFirstTupleMembers<T>( first_value.first,
						   first_name,
						   second_value.first,
						   second_name,
						   out,
						   index,
						   tol );
      if( !local_success )
	success = false;
    }

    // Compare the second tuple members
    {
      bool local_success = true;
      local_success = compareSecondTupleMembers<T2>( first_value.second,
						     first_name,
						     second_value.second,
						     second_name,
						     out,
						     index,
						     tol );
      if( !local_success )
	success = false;
    }

    return success;
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * std::pair.
 * \ingroup compare_policy
 */
template<typename T, typename T2>
struct ComparePolicy<std::pair<T,T2> >
{
  typedef const double scalarType;
  static inline bool compare( const std::pair<T,T2> &first_value,
			      const std::string &first_name,
			      const std::pair<T,T2> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    return ComparePolicy<const std::pair<T,T2> >::compare( first_value,
							   first_name,
							   second_value,
							   second_name,
							   out,
							   index,
							   tol );
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * const Utility::Trip.
 * \ingroup compare_policy
 */
template<typename T, typename T2, typename T3>
struct ComparePolicy<const Trip<T,T2,T3> >
{
  typedef const double scalarType;

  static inline bool compare( const Trip<T,T2,T3> &first_value,
			      const std::string &first_name,
			      const Trip<T,T2,T3> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    bool success = true;

    // Compare the first tuple members
    {
      bool local_success = true;
      local_success = compareFirstTupleMembers<T>( first_value.first,
						   first_name,
						   second_value.first,
						   second_name,
						   out,
						   index,
						   tol );
      if( !local_success )
	success = false;
    }

    // Compare the second tuple members
    {
      bool local_success = true;
      local_success = compareSecondTupleMembers<T2>( first_value.second,
						     first_name,
						     second_value.second,
						     second_name,
						     out,
						     index,
						     tol );
      if( !local_success )
	success = false;
    }

    // Compare the third tuple members
    {
      bool local_success = true;
      local_success = compareThirdTupleMembers<T3>( first_value.third,
						    first_name,
						    second_value.third,
						    second_name,
						    out,
						    index,
						    tol );
      if( !local_success )
	success = false;
    }

    return success;
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * Utility::Trip.
 * \ingroup compare_policy
 */
template<typename T, typename T2, typename T3>
struct ComparePolicy<Trip<T,T2,T3> >
{
  typedef double scalarType;

  static inline bool compare( const Trip<T,T2,T3> &first_value,
			      const std::string &first_name,
			      const Trip<T,T2,T3> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    return ComparePolicy<const Trip<T,T2,T3> >::compare( first_value,
							 first_name,
							 second_value,
							 second_name,
							 out,
							 index,
							 tol );
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * const Utility::Quad.
 * \ingroup compare_policy
 */
template<typename T, typename T2, typename T3, typename T4>
struct ComparePolicy<const Quad<T,T2,T3,T4> >
{
  typedef const double scalarType;

  static inline bool compare( const Quad<T,T2,T3,T4> &first_value,
			      const std::string &first_name,
			      const Quad<T,T2,T3,T4> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    bool success = true;

    // Compare the first tuple members
    {
      bool local_success = true;
      local_success = compareFirstTupleMembers<T>( first_value.first,
						   first_name,
						   second_value.first,
						   second_name,
						   out,
						   index,
						   tol );
      if( !local_success )
	success = false;
    }

    // Compare the second tuple members
    {
      bool local_success = true;
      local_success = compareSecondTupleMembers<T2>( first_value.second,
						     first_name,
						     second_value.second,
						     second_name,
						     out,
						     index,
						     tol );
      if( !local_success )
	success = false;
    }

    // Compare the third tuple members
    {
      bool local_success = true;
      local_success = compareThirdTupleMembers<T3>( first_value.third,
						    first_name,
						    second_value.third,
						    second_name,
						    out,
						    index,
						    tol );
      if( !local_success )
	success = false;
    }

    // Compare the fourth tuple members
    {
      bool local_success = true;
      local_success = compareFourthTupleMembers<T4>( first_value.fourth,
						     first_name,
						     second_value.fourth,
						     second_name,
						     out,
						     index,
						     tol );
      if( !local_success )
	success = false;
    }

    return success;
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * Utility::Quad.
 * \ingroup compare_policy
 */
template<typename T, typename T2, typename T3, typename T4>
struct ComparePolicy<Quad<T,T2,T3,T4> >
{
  typedef double scalarType;

  static inline bool compare( const Quad<T,T2,T3,T4> &first_value,
			      const std::string &first_name,
			      const Quad<T,T2,T3,T4> &second_value,
			      const std::string &second_name,
			      Teuchos::FancyOStream &out,
			      const int index = -1,
			      const scalarType tol = 0.0 )
  {
    return ComparePolicy<const Quad<T,T2,T3,T4> >::compare( first_value,
							    first_name,
							    second_value,
							    second_name,
							    out,
							    index,
							    tol );
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * const boost::units::quantity.
 * \ingroup compare_policy
 */
template<typename Unit, typename T>
struct ComparePolicy<const boost::units::quantity<Unit,T> >
{
  typedef const T scalarType;

  static inline bool compare(const boost::units::quantity<Unit,T>& first_value,
			     const std::string& first_name,
			     const boost::units::quantity<Unit,T>& second_value,
			     const std::string& second_name,
			     Teuchos::FancyOStream &out,
			     const int index = -1,
			     const scalarType tol = 0.0 )
  {
    bool success = true;

    if( tol == 0.0 )
    {
      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, " << first_name << "[" << index << "]" << " = "
	    << first_value << " == " << second_name << "[" << index << "]"
	    << " = " << second_value << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << first_name << " = " << first_value
	    << " == " << second_name << " = " << second_value
	    << ": ";
	if( first_value != second_value )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }
    else
    {
      double err = relError( first_value.value(), second_value.value() );

      // Array Element Compare
      if( index >= 0 )
      {
	out << "\nError, relErr(" << first_name << "[" << index << "],"
	    << second_name << "[" << index << "])" << " = relErr("
	    << first_value << "," << second_value << ") = " << err
	    << " <= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
      // Single Compare
      else
      {
	out << "\nCheck: relErr(" << first_name << "," << second_name << ")"
	    << "\n= relErr(" << first_value << "," << second_value << ") = "
	    << err << "\n<= tol = " << tol << ": ";
	if( err > tol )
	{
	  out << "failed!\n";

	  success = false;
	}
	else
	  out << "passed\n";
      }
    }

    return success;
  }
};

/*! \brief The partial specialization of the Utility::ComparePolicy for
 * boost::units::quantity.
 * \ingroup compare_policy
 */
template<typename Unit, typename T>
struct ComparePolicy<boost::units::quantity<Unit,T> >
{
  typedef T scalarType;

  static inline bool compare(const boost::units::quantity<Unit,T>& first_value,
			     const std::string& first_name,
			     const boost::units::quantity<Unit,T>& second_value,
			     const std::string& second_name,
			     Teuchos::FancyOStream &out,
			     const int index = -1,
			     const scalarType tol = 0.0 )
  {
    return ComparePolicy<const boost::units::quantity<Unit,T> >::compare(
							    first_value,
							    first_name,
							    second_value,
							    second_name,
							    out,
							    index,
							    tol );
  }
};

} // end Policy namespace

} // end Utility namespace

#endif // end UTILITY_COMPARE_POLICY_HPP

//---------------------------------------------------------------------------//
// end Utility_ComparePolicy.hpp
//---------------------------------------------------------------------------//


/*  _______________________________________________________________________

    DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
    Copyright 2014 Sandia Corporation.
    This software is distributed under the GNU Lesser General Public License.
    For more information, see the README file in the top Dakota directory.
    _______________________________________________________________________ */

//- Classes     : BootstrapSamplerBase, BootstrapSampler, BootstrapSamplerWithGS
//- Description : Functors for performing bootstrap sampling on a dataset
//- Owner       : Brian Adams
//- Checked by  :
//- Version     :

#ifndef __DAKOTA_BOOTSTRAP_SAMPLER_H__
#define __DAKOTA_BOOTSTRAP_SAMPLER_H__

#include <iostream>
#include <stdexcept>
#include <cstring>
#include <vector>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include "Teuchos_SerialDenseVector.hpp"
#include "Teuchos_SerialDenseHelpers.hpp"

namespace Dakota {

/// Base class/interface for the bootstrap sampler

/** BootstrapSamplerBase defines the minimum interface for a bootstrap sampler
    and handles initialization of the random variate generation used by the
    bootstrap. Functor is templated on the data type, but does not actually
    define a data member. */
template<typename Data>
class BootstrapSamplerBase
{
public:

  //
  //- Heading: Static methods for access to random variate generation
  //

  static void set_seed(size_t seed)
  {
    bootstrapRNG.seed(seed);
  }

  //
  //- Heading: Constructors and destructor
  //

  /// Constructor for the bootstrap functor base
  BootstrapSamplerBase(size_t data_size, Data orig_data) : dataSize(data_size),
                          origData(orig_data), sampler(0, data_size - 1)
  {
  }

  /// Destructor
  virtual ~BootstrapSamplerBase()
  {
    /* empty destructor */
  }

  //
  //- Heading: Public members functions that perform for boostrap sampling
  //

  /// Generate and store a new boostrapped sample into bootstrapped_sample
  virtual void operator()(size_t num_samp, Data& bootstrapped_sample) = 0;

  /// Obatin the number of samples used in the empirical distribution
  virtual size_t getDataSize()
  {
    return dataSize;
  }

  /// Generate and store an dataSize out of dataSize boostrap sample
  virtual void operator()(Data& bootstrapped_sample)
  {
    (*this)(dataSize, bootstrapped_sample);
  }

  /// Return boostrapped sample
  virtual Data operator()()
  {
    Data sample(origData);
    (*this)(dataSize, sample);
    return sample;
  }

protected:

  // Internal static members for random variate generation

  /// Random number generator to use for sampling
  static boost::random::mt19937 bootstrapRNG;

  // Internal instance members

  /// Uniform distribution to provide samples from the empirical distribution
  boost::random::uniform_int_distribution<> sampler;

  /// Size of the dataset defining the empirical distribution
  const size_t dataSize;

  /// Original data defining the empirical distribution
  /// TODO: Consider if it should be const (breaks Teuchos)
  Data origData;
};

/// The boostrap random number generator
template<typename Data>
boost::random::mt19937 BootstrapSamplerBase<Data>::bootstrapRNG;

/// Actual boostrap sampler implementation for common data types

/** Template requires the given type to support an STL-like interface, including
    a size method and begin and end methods returning random access iterators */
template<typename Data>
class BootstrapSampler : public BootstrapSamplerBase<Data>
{
public:

  //
  //- Heading: Type definitions and aliases
  //

  using BootstrapSamplerBase<Data>::operator();

  //
  //- Heading: Constructors and destructor
  //

  /// Constructor for the sampler
  BootstrapSampler(const Data& orig_data, size_t block_size = 1) :
    BootstrapSamplerBase<Data>::BootstrapSamplerBase(
      block_size ? orig_data.size()/block_size : orig_data.size(), orig_data),
      blockSize(block_size ? block_size : 1)
  {
    if(block_size &&
      (block_size > this->dataSize || orig_data.size() % block_size != 0))
        throw "Boostrap sampler data size must be a multiple of block size.";
  }

  /// Destructor
  virtual ~BootstrapSampler()
  {
    /* empty destructor */
  }

  //
  //- Heading: Public members functions that perform for boostrap sampling
  //

  /// \copydoc
  virtual void operator()(size_t num_samp, Data& bootstrapped_sample)
  {
    if(num_samp > bootstrapped_sample.size()/blockSize)
      throw
        std::out_of_range("Number of samples exceeds the size of container");

    typename Data::iterator beg_data = this->origData.begin();
    for(typename Data::iterator sample = bootstrapped_sample.begin();
        sample != bootstrapped_sample.end(); sample += blockSize)
    {
      typename Data::iterator beg_block = beg_data +
	      this->sampler(this->bootstrapRNG) * blockSize;
      for(size_t i = 0; i < blockSize; ++i)
      {
        *(sample + i) = *(beg_block + i);
      }
    }
  }

protected:

  // Internal instance members

  /// Size of the block defining a sample
  size_t blockSize;
};

/// Bootstrap sampler that is specialized to allow for the boostrapping of
/// RealMatrix
template<typename OrdinalType, typename ScalarType>
class BootstrapSampler<Teuchos::SerialDenseMatrix<OrdinalType, ScalarType> > :
  public BootstrapSamplerBase<Teuchos::
                              SerialDenseMatrix<OrdinalType, ScalarType> >
{
public:

  //
  //- Heading: Type definitions and aliases
  //

  /// Convenience definition
  typedef Teuchos::SerialDenseMatrix<OrdinalType, ScalarType> MatType;

  using BootstrapSamplerBase<MatType>::operator();

  /// Constructor for the sampler.
  BootstrapSampler(const MatType& orig_data, size_t block_size = 1) :
    BootstrapSamplerBase<MatType>::BootstrapSamplerBase(
      block_size ? orig_data.numCols()/block_size : orig_data.numCols(),
      orig_data),
      blockSize(block_size ? block_size : 1)
  {
    if(block_size &&
      (block_size > this->dataSize || orig_data.numCols() % block_size != 0))
        throw "Boostrap sampler data size must be a multiple of block size.";
  }

  /// Destructor
  virtual ~BootstrapSampler()
  {
    /* empty destructor */
  }

  //
  //- Heading: Public members functions that perform boostrap sampling
  //

  /// \copydoc
  virtual void operator()(size_t num_samp, MatType& bootstrapped_sample)
  {
    OrdinalType stride = this->origData.stride();
    if(stride != bootstrapped_sample.stride())
      throw
          std::out_of_range("Dimension of a boostrapped sample differs from "
                            "the dimension of the original dataset");

    if(num_samp > bootstrapped_sample.numCols()/blockSize)
      throw
        std::out_of_range("Number of samples exceeds the size of container");

    for(int i = 0; i < num_samp * blockSize; i += blockSize)
    {
      std::memcpy(bootstrapped_sample[i],
        this->origData[this->sampler(this->bootstrapRNG) * blockSize],
        blockSize * stride * sizeof(ScalarType));
    }
  }

protected:

  // Internal instance members

  /// Size of the block defining a sample
  size_t blockSize;
};

/// A derived sampler to allow for user specification of the accessor methods
template<typename Data, typename Getter, typename Setter>
class BootstrapSamplerWithGS : public BootstrapSampler<Data>
{
public:

  //
  //- Heading: Type definitions and aliases
  //

  using BootstrapSampler<Data>::operator();

  //
  //- Heading: Constructors and destructor
  //

  /// Constructor with extra arguments for the accessor methods
  BootstrapSamplerWithGS(const Data& orig_data,
                         Getter getter_method,
                         Setter setter_method) :
                         BootstrapSampler<Data>::BootstrapSampler(orig_data),
                         getterMethod(getter_method),
                         setterMethod(setter_method)
  {
  }

  /// Destructor
 virtual  ~BootstrapSamplerWithGS()
  {
    /* empty destructor */
  }

  //
  //- Heading: Public members functions that perform for boostrap sampling
  //

  /// Generate and store a new boostrapped sample into bootstrapped_sample
  /// TODO: bounds checking
  virtual void operator()(size_t num_samp, Data& bootstrapped_sample)
  {
    for(size_t i = 0; i < num_samp; ++i)
    {
      setterMethod(i, getterMethod(this->sampler(this->bootstrapRNG),
                                   this->origData), bootstrapped_sample);
    }
  }

protected:

  // Internal instance members

  /// Function to obtain a single sample from a Data object. Function should
  /// take a Data object and an unsigned integer corresponding to a sample
  /// index and return the sample
  Getter getterMethod;

  /// Function to place a single sample into a Data object. Function should
  /// take a Data object and an unsigned integer corresponding to the sample
  /// index to set.
  Setter setterMethod;
};

}

#endif // __DAKOTA_BOOTSTRAP_SAMPLER_H__