/*

	This file is part of PEST++.

	PEST++ is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	PEST++ is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with PEST++.  If not, see<http://www.gnu.org/licenses/>.
*/
#include "network_wrapper.h"
#include <ostream>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <fstream>
#include <iomanip>
#include <utility>
#include <cassert>
#include <memory>
#include <Eigen/Dense>
#include <utility>
#include <cmath>
#include <cfloat>
#include "Transformable.h"
#include "pest_error.h"
#include "utilities.h"

using std::string;
using std::map;
using std::ostream;
using std::endl;
using std::isnormal;
using namespace pest_utils;
using namespace Eigen;

const double Transformable::no_data = -9.99E99;

Transformable::Transformable(const Transformable &copyin) : items(copyin.items)
{
}

Transformable::Transformable(const Transformable &&copyin)
{
	items = std::move(copyin.items);
}

Transformable::Transformable(const Transformable &copyin, const vector<string> &copy_names)
{
	for (vector<string>::const_iterator b=copy_names.begin(), e=copy_names.end(); b!=e; ++b) {
		items[*b] = copyin.get_rec(*b);
	}
}

Transformable::Transformable(const vector<string> &names, const Eigen::VectorXd &values)
{
	assert(names.size() == values.size());
	if(names.size() != values.size())
	{
		throw PestIndexError("Transformable::Transformable(const vector<string> &names, Eigen::VectorXd &values)",
			"size of names vector does not match the size of the values vector");
	}
	size_t len = min(size_t(names.size()), size_t(values.size()));
	for (size_t i=0; i<len; ++i)
	{
		items[names[i]] = values(i);
	}
}

const Transformable& Transformable::operator=(const Transformable &rhs)
{
	items = rhs.items;
	return *this;
}

bool Transformable::operator==(const Transformable &rhs) const
{
	return items.size() == rhs.items.size()
		&& std::equal(items.begin(), items.end(),
					  rhs.items.begin());
}

bool Transformable::operator!=(const Transformable &rhs) const
{
	return !(*this == rhs);
}

Transformable& Transformable::operator+=(const Transformable &rhs)
{
	for(auto &i : items)
	{
		auto iter = rhs.items.find(i.first);
		assert(iter != rhs.items.end());
		i.second += iter->second;
	}
	return *this;
 }

Transformable& Transformable::operator-=(const Transformable &rhs)
{
	for(auto &i : items)
	{
		auto iter = rhs.items.find(i.first);
		assert(iter != rhs.items.end());
		i.second -= iter->second;
	}
	return *this;
 }

Transformable& Transformable::operator*=(double scale)
{
	for(auto &i : items)
	{
		i.second *= scale;
	}
	 return *this;
}

Transformable Transformable::operator-(const Transformable &rhs) const
{
	Transformable ret_val(*this);
	ret_val -= rhs;
	return ret_val;
}

Transformable Transformable::operator+(const Transformable &rhs) const
{
	Transformable ret_val(*this);
	ret_val += rhs;
	return ret_val;

}

Transformable Transformable::operator*(double scale) const
{
	Transformable ret_val(*this);
	ret_val *= scale;
	return ret_val;
}

double &Transformable::operator[](const string &name)
{
	return items[name];
}

vector<string> Transformable::get_notnormal_keys()
{
	vector<string> not_normal;
	for (auto &i : items)
	{
		if ((isnormal(i.second)) || (i.second ==0.0))
			continue;
		not_normal.push_back(i.first);
	}
	return not_normal;
}

pair<Transformable::iterator,bool> Transformable::insert(const string &name, double value)
{
	pair<string, double> rec(name, value);
	return items.insert(rec);
}

pair<Transformable::iterator,bool>  Transformable::insert(const pair<string, double> &x)
{
	return items.insert(x);
}

void Transformable::insert(const vector<string> &name_vec, const vector<double> &value_vec)
{
	assert(name_vec.size() == value_vec.size());
	int vec_size = name_vec.size();
	items.reserve(vec_size);
	for(int i=0; i<vec_size; ++i)
	{
		insert(pair<string, double>(name_vec[i], value_vec[i]));
	}
}

void Transformable::insert(iterator first, iterator last )
{
	items.insert(first, last);
}

void Transformable::insert(const Transformable &insert_items)
{
	for(auto &ipar : insert_items)
	{
		items[ipar.first] = ipar.second;
	}
}

size_t Transformable::erase(const string &name)
{
	return items.erase(name);
}

void Transformable::erase(iterator position)
{
	items.erase(position);
}

void Transformable::erase(const Parameters &erase_pars)
{
	auto end_erase_pars = erase_pars.end();
	auto end_items = items.end();
	auto item_iter = items.end();;
	for(auto iter = erase_pars.begin(); iter != end_erase_pars;) {
		item_iter = items.find((*iter).first);
		if (item_iter != end_items)
		{
		   items.erase(item_iter++);
		}
		else
		{
			++iter;
	   }
	}
}

void Transformable::erase(const vector<string> &erase_par_names)
{
	auto end_erase_pars = erase_par_names.end();
	auto end_items = items.end();
	auto item_iter = items.end();;
	for(auto iter = erase_par_names.begin(); iter != end_erase_pars;) {
		item_iter = items.find((*iter));
		if (item_iter != end_items)
		{
		   items.erase(item_iter++);
		}
		else
		{
			++iter;
	   }
	}
}

Transformable::iterator Transformable::find(const string &name)
{
	return items.find(name);
}

Transformable::const_iterator Transformable::find(const string &name) const
{
	return items.find(name);
}

const double* Transformable::get_rec_ptr(const string &name) const
{
	const double *ret_val = 0;
	Transformable::const_iterator iter = find(name);
	if (iter != end()) {
		ret_val = &((*iter).second);
	}
	return ret_val;
}

double Transformable::get_rec(const string &name) const
{
	double ret_val = no_data;
	Transformable::const_iterator iter = find(name);
	if (iter != end()) {
		ret_val = (*iter).second;
	}
	else {
		throw(Transformable_value_error(name));
	}
	return ret_val;
}

void Transformable::update_rec(const string &name, double value)
{
	Transformable::iterator iter = find(name);
	assert(iter != end());
	if (iter != end()) {
		(*iter).second = value;
	}
	else {
		throw(Transformable_value_error(name));
	}
}

void Transformable::update(const vector<string> &names, const vector<double> &values)
{
	items.clear();
	assert(names.size() == values.size());
	size_t n_rec = names.size();
	items.reserve(n_rec);
	for (size_t i=0; i<n_rec; ++i)
	{
		items.insert(make_pair(names[i], values[i]));
	}
}
void Transformable::update_without_clear(const vector<string> &names, const vector<double> &values)
{
	assert(names.size() == values.size());
	size_t n_rec = names.size();
	items.reserve(n_rec);
	for (size_t i=0; i<n_rec; ++i)
	{
		items[names[i]] = values[i];
	}
}

void Transformable::update_without_clear(const vector<string> &names, const Eigen::VectorXd &values)
{
	assert(names.size() == values.size());
	size_t n_rec = names.size();
	items.reserve(n_rec);
	for (size_t i = 0; i<n_rec; ++i)
	{
		items[names[i]] = values[i];
	}
}

vector<double> Transformable::get_data_vec(const vector<string> &keys) const
{
	vector<double> v;
	v.resize(keys.size(), 0.0);

	double value;
	int i = 0;

	for(vector<string>::const_iterator b=keys.begin(), e=keys.end();
		b!=e; ++b, ++i)
	{
		value = get_rec((*b));
		v[i] = value;
	}
	return v;
}

Eigen::VectorXd Transformable::get_data_eigen_vec(const vector<string> &keys) const
{
	VectorXd vec;
	vec.resize(keys.size());
	int i = 0;
	for (auto &k : keys)
	{
		vec(i++) = get_rec(k);
	}
	return vec;
}

Eigen::VectorXd Transformable::get_partial_data_eigen_vec(const vector<string> &keys) const
{
	VectorXd vec;
	vec.resize(keys.size());
	int i = 0;
	auto end = items.end();
	for (auto &k : keys)
	{
		auto iter = items.find(k);
		if (iter != end)
		{
			vec(i) = get_rec(k);
		}
		else
		{
			vec(i) = 0;
		}
		++i;
	}
	return vec;
}

vector<string> Transformable::get_keys() const
{
	vector<string> ret_val;
	ret_val.reserve(size());
	for(Transformable::const_iterator b=begin(), e=end();
		b!=e; ++b)
	{
		ret_val.push_back((*b).first);
	}
	return ret_val;
}

double Transformable::l2_norm() const
{
   double norm=0;
   for (auto &i : items)
   {
	norm += pow(i.second, 2.0);
   }
   norm = sqrt(norm);
   return norm;
}

double  Transformable::l2_norm(const Transformable &d1, const Transformable &d2)
{
	double norm = 0.0;
	const auto it_end = d1.end();
	assert(d1.size() == d2.size());
	for (const auto &i_d2 : d2.items)
	{
		auto it_d1 = d1.find(i_d2.first);
		if (it_d1 != it_end)
		{
			norm += pow(it_d1->second - i_d2.second, 2.0);
		}
		else
		{
			assert(true);
		}
	}
	norm = sqrt(norm);
	return norm;
}

ostream& operator<<(ostream& out, const Transformable &rhs)
{
	for(Transformable::const_iterator b=rhs.begin(), e=rhs.end();
		b!=e; ++b)
	{
		out << b->first << " = " << b->second << endl;
	}
	return out;
}

void Parameters::read_par_file(ifstream &fin,  map<string, double> &offset, map<string, double> &scale)
{
	clear();
	offset.clear();
	scale.clear();

	vector<string> tokens;
	string line;
	getline(fin, line);
	while (getline(fin, line))
	{
		strip_ip(line);
		if (line.length() > 0)
		{
			tokens.clear();
			tokenize(line, tokens);
			string name = upper_cp(tokens[0]);
			double par_val = convert_cp<double>(tokens[1]);
			double offset_val = convert_cp<double>(tokens[2]);
			double scale_val = convert_cp<double>(tokens[3]);
			insert(name, par_val);
			offset[name] = offset_val;
			scale[name] = scale_val;
		}
	}
}


/*
// Copyright (c) 2000-2009, Texas Engineering Experiment Station (TEES), a
// component of the Texas A&M University System.

// All rights reserved.

// The information and source code contained herein is the exclusive
// property of TEES and may not be disclosed, examined or reproduced
// in whole or in part without explicit written authorization from TEES.
*/

#ifndef STAPL_SKELETONS_FUNCTIONAL_SCAN_HPP
#define STAPL_SKELETONS_FUNCTIONAL_SCAN_HPP

#include <type_traits>
#include <stapl/skeletons/utility/utility.hpp>
#include <stapl/skeletons/utility/tags.hpp>
#include <stapl/skeletons/utility/skeleton.hpp>
#include <stapl/skeletons/flows/repeat_flows.hpp>
#include <stapl/skeletons/flows/scan_flows.hpp>
#include <stapl/skeletons/param_deps/scan_broadcast_pd.hpp>
#include <stapl/skeletons/param_deps/scan_blelloch_broadcast_pd.hpp>
#include <stapl/skeletons/param_deps/scan_expand_from_pow_two_pd.hpp>
#include <stapl/skeletons/param_deps/binomial_tree_pd.hpp>
#include <stapl/skeletons/spans/balanced.hpp>
#include <stapl/skeletons/spans/binomial_tree.hpp>
#include <stapl/skeletons/spans/nearest_pow_two.hpp>
#include <stapl/skeletons/operators/compose.hpp>
#include "pointer_jumping.hpp"
#include "reduce_to_pow_two.hpp"
#include "binary_tree.hpp"
#include "tree.hpp"

#include <boost/mpl/has_xxx.hpp>

namespace stapl {
namespace skeletons {

BOOST_MPL_HAS_XXX_TRAIT_DEF(first_argument_type)

template <typename Op,
          bool with_arg_type = has_first_argument_type<Op>::value>
struct scan_operand_type
{
  using type = typename std::decay<Op>::type::first_argument_type;
};

template <typename Op>
struct scan_operand_type<Op, false>
{
  using type = typename std::decay<Op>::type::result_type;
};

namespace skeletons_impl {

//////////////////////////////////////////////////////////////////////
/// @brief Determines the scan algorithm used.
//////////////////////////////////////////////////////////////////////
template <typename Tag>
struct scan_algorithm_tag_type;

template <typename Tag, typename IE>
struct scan_algorithm_tag_type<tags::scan<Tag, IE>>
{
  using type = Tag;
};

template <typename Op, typename Tag>
struct inclusive_scan;

template <typename Op, typename T, typename Tag>
struct exclusive_scan;

//////////////////////////////////////////////////////////////////////
/// @brief Hillis Steele scan skeleton is a pointer-jumping based
/// inclusive scan algorithm which has half the height of the scan
/// algorithm in Joseph Jaja's book, but it performs \f$O(n logn)\f$
/// operations and hence is not work optimal.
///
/// @tparam Op the operation to be used to compute the scan results
///
/// @see flows::repeat_flows::piped
/// @see flows::repeat_flows::input_wrapper
/// @see pointer_jumping
/// @see scan
///
/// @ingroup skeletonsFunctionalInternal
//////////////////////////////////////////////////////////////////////
template <typename Op>
struct inclusive_scan<Op, tags::hillis_steele>
  : public decltype(
             skeletons::pointer_jumping<
               flows::repeat_flows::input_wrapper<flows::repeat_flows::piped>
             >(std::declval<Op>()))
{
  using skeleton_tag_type = tags::scan<tags::hillis_steele, tags::inclusive>;
  using base_type = decltype(
                      skeletons::pointer_jumping<
                        flows::repeat_flows::input_wrapper<
                          flows::repeat_flows::piped>
                      >(std::declval<Op>()));

  explicit inclusive_scan(Op const& op)
    : base_type(op)
  { }

  Op get_op() const
  {
    return base_type::get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

//////////////////////////////////////////////////////////////////////
/// @brief Binomial scan is one of the common implementations of
/// MPI_Scan.
///
/// A binomial scan consists of an up-phase and a down-phase. In the
/// up-phase, one or more reduction trees (depending on whether the
/// input size is a power-of-two or not) reduce the values and prepare
/// partial results for the down-phase. In the down-phase, partial
/// results are combined to produce the final result.
///
/// @code
/// O O O O O O
/// |\| |\| |\|
/// | O_| O | O
/// | | |\| | |
/// | | | O | |
/// | | | | | |
/// | | | O | |
/// | | | |\|_|
/// | O | O | O
/// | |\| |\| |
/// O O O O O O
/// @endcode
///
/// @tparam Op the operation to be used to compute the scan results
///
/// @see flows::repeat_flows::output_from_all
/// @see flows::repeat_flows::
/// @see flows::scan_f::scan_broadcast
/// @see spans::binomial_tree
/// @see binomial_tree_pd
///
/// @ingroup skeletonsFunctionalInternal
//////////////////////////////////////////////////////////////////////
template <typename Op>
struct inclusive_scan<Op, tags::binomial>
  : public decltype(
             skeletons::compose<
               flows::repeat_flows::input_wrapper<
                 flows::compose_flows::input_to_all
               >
             >(skeletons::repeat<flows::repeat_flows::output_from_all>(
                 skeletons::elem<
                   spans::binomial_tree<spans::balanced<>, tags::up_phase>
                 >(skeletons::binomial_tree_pd<tags::up_phase>(
                     std::declval<Op>())),
                 log_lazysize<2>()),
               skeletons::repeat<flows::repeat_flows::scan_broadcast>(
                 skeletons::elem<
                   spans::binomial_tree<spans::balanced<>, tags::down_phase>
                 >(skeletons::binomial_tree_pd<tags::down_phase>(
                     std::declval<Op>())),
                 log_ceil_lazysize<2>()))
           )
{
  using skeleton_tag_type = tags::scan<tags::binomial, tags::inclusive>;
  using base_type = decltype(
                      skeletons::compose<
                        flows::repeat_flows::input_wrapper<
                          flows::compose_flows::input_to_all
                        >
                      >(skeletons::repeat<flows::repeat_flows::output_from_all>(
                          skeletons::elem<
                            spans::binomial_tree<
                              spans::balanced<>, tags::up_phase>
                          >(skeletons::binomial_tree_pd<tags::up_phase>(
                              std::declval<Op>())),
                          log_lazysize<2>()),
                        skeletons::repeat<flows::repeat_flows::scan_broadcast>(
                          skeletons::elem<
                            spans::binomial_tree<
                              spans::balanced<>, tags::down_phase>
                          >(skeletons::binomial_tree_pd<tags::down_phase>(
                              std::declval<Op>())),
                          log_ceil_lazysize<2>())));

  explicit inclusive_scan(Op const& op)
    : base_type(
        skeletons::compose<
          flows::repeat_flows::input_wrapper<flows::compose_flows::input_to_all>
        >(skeletons::repeat<flows::repeat_flows::output_from_all>(
            skeletons::elem<
              spans::binomial_tree<spans::balanced<>, tags::up_phase>
            >(skeletons::binomial_tree_pd<tags::up_phase>(op)),
            log_lazysize<2>()),
          skeletons::repeat<flows::repeat_flows::scan_broadcast>(
            skeletons::elem<
              spans::binomial_tree<spans::balanced<>, tags::down_phase>
            >(skeletons::binomial_tree_pd<tags::down_phase>(op)),
            log_ceil_lazysize<2>()))
      )
  { }

  Op get_op() const
  {
    return base_type::template get_skeleton<0>().nested_skeleton().
             nested_skeleton().get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

//////////////////////////////////////////////////////////////////////
/// @brief This scan skeleton is based on the algorithm given in
/// <b>An Introduction to Parallel Algorithms</b> by Joseph Jaja, page
/// 48. This algorithm's time complexity is \f$O(log n)\f$. It
/// consists of a reduction tree followed by a modified broadcast
/// skeleton. This scan skeleton is an inclusive scan.
///
/// @tparam Op the operation to be used to compute the scan results
///
/// @see flows::compose_flows::input_to_all
/// @see flows::repeat_flows::output_from_all
/// @see flows::repeat_flows::scan_broadcast
/// @see scan_broadcast_pd
///
/// @ingroup skeletonsFunctionalInternal
//////////////////////////////////////////////////////////////////////
template <typename Op>
struct inclusive_scan<Op, tags::jaja>
  : public decltype(
             skeletons::compose<flows::compose_flows::input_to_all>(
               skeletons::binary_tree<
                 tags::left_skewed,
                 flows::repeat_flows::output_from_all,
                 spans::nearest_pow_two<spans::balanced<>>, true
               >(std::declval<Op>()),
               skeletons::reverse_tree<
                 2, flows::repeat_flows::scan_broadcast,
                 spans::reverse_tree<
                   spans::nearest_pow_two<spans::balanced<>>, tags::left_skewed
                 >
               >(skeletons::scan_broadcast_pd(std::declval<Op>()))))
{
  using skeleton_tag_type = tags::scan<tags::jaja, tags::inclusive>;

  using base_type = decltype(
                      skeletons::compose<flows::compose_flows::input_to_all>(
                        skeletons::binary_tree<
                          tags::left_skewed,
                          flows::repeat_flows::output_from_all,
                          spans::nearest_pow_two<spans::balanced<>>, true
                        >(std::declval<Op>()),
                        skeletons::reverse_tree<
                          2, flows::repeat_flows::scan_broadcast,
                          spans::reverse_tree<
                            spans::nearest_pow_two<spans::balanced<>>,
                            tags::left_skewed
                          >
                        >(skeletons::scan_broadcast_pd(std::declval<Op>()))));

  explicit inclusive_scan(Op const& op)
    : base_type(
        skeletons::compose<flows::compose_flows::input_to_all>(
          skeletons::binary_tree<
            tags::left_skewed,
            flows::repeat_flows::output_from_all,
            spans::nearest_pow_two<spans::balanced<>>, true
          >(op),
          skeletons::reverse_tree<
            2, flows::repeat_flows::scan_broadcast,
            spans::reverse_tree<
              spans::nearest_pow_two<spans::balanced<>>, tags::left_skewed
            >
          >(skeletons::scan_broadcast_pd(op)))
      )
  { }

  Op get_op() const
  {
    return base_type::template get_skeleton<0>().get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

//////////////////////////////////////////////////////////////////////
/// @brief Blelloch scan is an exclusive scan algorithm which is
/// similar to the inclusive scan mentioned in Joseph Jaja's book
/// (@see inclusive_scan(op,tags::jaja)).
///
/// @tparam Op the operation to be used to compute the scan results
/// @tparam T  the type of the initial value for the exclusive scan.
///
/// @see flows::compose_flows::input_to_all
/// @see flows::repeat_flows::output_from_all
/// @see flows::repeat_flows::scan_broadcast
/// @see scan_blelloch_broadcast_pd
/// @see
///
/// @ingroup skeletonsFunctionalInternal
//////////////////////////////////////////////////////////////////////
template <typename Op, typename T>
struct exclusive_scan<Op, T, tags::blelloch>
  : public decltype(
             skeletons::compose<
               flows::repeat_flows::input_wrapper<
                 flows::compose_flows::input_to_all>
             >(skeletons::binary_tree<
                 tags::right_aligned,
                 flows::repeat_flows::output_from_all,
                 spans::nearest_pow_two<spans::balanced<>>, true
               >(std::declval<Op>()),
               skeletons::reverse_tree<
                 2, flows::repeat_flows::scan_broadcast,
                 spans::reverse_tree<
                   spans::nearest_pow_two<spans::balanced<>>,
                   tags::right_aligned
                 >
               >(skeletons::scan_blelloch_broadcast_pd(
                  std::declval<Op>(), T())))
           )
{
  using skeleton_tag_type = tags::scan<tags::blelloch, tags::exclusive>;

  using base_type = decltype(
                      skeletons::compose<
                         flows::repeat_flows::input_wrapper<
                           flows::compose_flows::input_to_all
                         >
                      >(skeletons::binary_tree<
                          tags::right_aligned,
                          flows::repeat_flows::output_from_all,
                          spans::nearest_pow_two<spans::balanced<>>, true
                        >(std::declval<Op>()),
                        skeletons::reverse_tree<
                          2, flows::repeat_flows::scan_broadcast,
                          spans::reverse_tree<
                            spans::nearest_pow_two<spans::balanced<>>,
                            tags::right_aligned
                          >
                        >(skeletons::scan_blelloch_broadcast_pd(
                            std::declval<Op>(), T()))));

  exclusive_scan(Op const& op, T const& initial_value)
    : base_type(
        skeletons::compose<
          flows::repeat_flows::input_wrapper<flows::compose_flows::input_to_all>
        >(skeletons::binary_tree<
            tags::right_aligned,
            flows::repeat_flows::output_from_all,
            spans::nearest_pow_two<spans::balanced<>>, true
          >(op),
          skeletons::reverse_tree<
            2, flows::repeat_flows::scan_broadcast,
            spans::reverse_tree<
              spans::nearest_pow_two<spans::balanced<>>, tags::right_aligned
            >
          >(skeletons::scan_blelloch_broadcast_pd(op, initial_value))
        )
      )
  { }

  Op get_op() const
  {
    return base_type::template get_skeleton<0>().get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

} // namespace skeletons_impl

namespace result_of {

template <typename Tag, typename Op>
using inclusive_scan = skeletons_impl::inclusive_scan<Op, Tag>;

template <typename Tag, typename Op, typename T>
using exclusive_scan = skeletons_impl::exclusive_scan<Op, T, Tag>;

}

//////////////////////////////////////////////////////////////////////
/// @brief An inclusive scan is a scan in which each element in the
/// output is the result of cumulative application of the operation
/// on the elements before it including itself.
///
/// For example,  if + is used as the operation, each element in the
/// output is the sum of all elements before it, and the first element
/// is the neutral value. An exclusive scan of [1, 2, 3, 4, ...] would
/// be [1, 3, 6, 10, ...].
///
/// @param Tag determines the type of inclusive scan to be used.
/// @param op  the operation to be used to compute the scan results
///
/// @see scan
///
/// @ingroup skeletonsFunctional
//////////////////////////////////////////////////////////////////////
template <typename Tag, typename Op>
result_of::inclusive_scan<Tag, Op>
inclusive_scan(Op const& op)
{
  return result_of::inclusive_scan<Tag, Op>(op);
}

//////////////////////////////////////////////////////////////////////
/// @brief An exclusive scan is a scan in which each element in the
/// output is the result of cumulative application of the operation
/// on the elements before it.
///
/// @param T   value type of the operator
/// @param op  the operation to be used to compute the scan results
/// @param Tag determines the type of inclusive scan to be used.
///
/// @see scan
///
/// @ingroup skeletonsFunctional
//////////////////////////////////////////////////////////////////////
template <typename Tag, typename Op, typename T>
result_of::exclusive_scan<Tag, Op, T>
exclusive_scan(Op const& op, T const& initial_value)
{
  return result_of::exclusive_scan<Tag, Op, T>(op, initial_value);
}

namespace skeletons_impl {

template<typename Op, typename Tag>
struct scan;

//////////////////////////////////////////////////////////////////////
/// @brief Most of the inclusive scan skeletons cannot work directly on
/// inputs with non-power-of-two sizes. As a result, we have to first
/// convert the input to the closest power of two and then perform the
/// scan operation.
///
/// @ingroup skeletonsFunctional
//////////////////////////////////////////////////////////////////////
template<typename Op, typename Tag>
struct scan<Op, tags::scan<Tag, tags::inclusive>>
  : public decltype(
             skeletons::compose<flows::compose_flows::scan>(
               skeletons::reduce_to_pow_two(std::declval<Op>()),
               skeletons::inclusive_scan<Tag>(std::declval<Op>()),
               skeletons::elem(skeletons::scan_expand_from_pow_two_pd(
                                 std::declval<Op>(),
                                 tags::scan<Tag, tags::inclusive>())))
           )
{
  using skeleton_tag_type = tags::scan<Tag, tags::inclusive>;
  using base_type = decltype(
                      skeletons::compose<flows::compose_flows::scan>(
                        skeletons::reduce_to_pow_two(std::declval<Op>()),
                        skeletons::inclusive_scan<Tag>(std::declval<Op>()),
                        skeletons::elem(skeletons::scan_expand_from_pow_two_pd(
                                          std::declval<Op>(),
                                          tags::scan<Tag, tags::inclusive>())))
                    );

  explicit scan(Op const& op)
    : base_type(
        skeletons::compose<flows::compose_flows::scan>(
          skeletons::reduce_to_pow_two(op),
          skeletons::inclusive_scan<Tag>(op),
          skeletons::elem(skeletons::scan_expand_from_pow_two_pd(
                           op, tags::scan<Tag, tags::inclusive>())))
      )
  { }

  Op get_op() const
  {
    return base_type::template get_skeleton<0>().get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

//////////////////////////////////////////////////////////////////////
/// @brief Most of the exclusive scan skeletons cannot work directly on
/// inputs with non-power-of-two sizes. As a result, we have to first
/// convert the input to the closest power of two and then perform the
/// scan operation.
///
/// @ingroup skeletonsFunctional
//////////////////////////////////////////////////////////////////////
template<typename Op, typename Tag>
struct scan<Op, tags::scan<Tag, tags::exclusive>>
  : public decltype(
             skeletons::compose<flows::compose_flows::scan>(
               skeletons::reduce_to_pow_two(std::declval<Op>()),
               skeletons::exclusive_scan<Tag>(
                 std::declval<Op>(),
                 typename scan_operand_type<Op>::type()),
               skeletons::elem(
                 skeletons::scan_expand_from_pow_two_pd(
                   std::declval<Op>(),
                   tags::scan<Tag, tags::exclusive>()))))
{
  using skeleton_tag_type = tags::scan<Tag, tags::exclusive>;
  using value_t = typename scan_operand_type<Op>::type;
  using base_type = decltype(
                      skeletons::compose<flows::compose_flows::scan>(
                        skeletons::reduce_to_pow_two(std::declval<Op>()),
                        skeletons::exclusive_scan<Tag>(
                          std::declval<Op>(), value_t()),
                        skeletons::elem(
                          skeletons::scan_expand_from_pow_two_pd(
                            std::declval<Op>(),
                            tags::scan<Tag, tags::exclusive>()))));

  scan(Op const& op, value_t const& initial_value)
    : base_type(
        skeletons::compose<flows::compose_flows::scan>(
          skeletons::reduce_to_pow_two(op),
          skeletons::exclusive_scan<Tag>(op, initial_value),
          skeletons::elem(
            skeletons::scan_expand_from_pow_two_pd(
              op, tags::scan<Tag, tags::exclusive>()))))
  { }

  Op get_op() const
  {
    return base_type::template get_skeleton<0>().get_op();
  }

  void define_type(typer& t)
  {
    t.base<base_type>(*this);
  }
};

} // namespace skeletons_impl

namespace result_of {

//////////////////////////////////////////////////////////////////////
/// @brief Hillis-Steele scan can work on inputs with non-power-of-two
/// sizes, therefore, we do not need to wrap it with extra steps.
///
/// @ingroup skeletonsFunctional
//////////////////////////////////////////////////////////////////////
template <typename Tag, typename Op>
using scan = typename std::conditional<
               std::is_same<
                 tags::binomial,
                 typename skeletons_impl::scan_algorithm_tag_type<Tag>::type
               >::value ||
               std::is_same<
                 tags::hillis_steele,
                 typename skeletons_impl::scan_algorithm_tag_type<Tag>::type
               >::value,
               skeletons_impl::inclusive_scan<
                 typename std::decay<Op>::type,
                 typename skeletons_impl::scan_algorithm_tag_type<Tag>::type>,
               skeletons_impl::scan<typename std::decay<Op>::type, Tag>>::type;

} // namespace result_of

//////////////////////////////////////////////////////////////////////
/// @brief A scan skeleton which is specialized based on the tag
/// given.
///
/// @ingroup skeletonsFunctionalAlgorithm
//////////////////////////////////////////////////////////////////////
template <typename Tag = tags::binomial,
          typename Op>
result_of::scan<tags::scan<Tag, tags::inclusive>, Op>
scan(Op&& op)
{
  using std::is_same;
  static_assert(
    is_same<Tag, tags::binomial>::value      ||
    is_same<Tag, tags::hillis_steele>::value ||
    is_same<Tag, tags::jaja>::value,
    "The requested inclusive scan algorithm is not supported");
  return result_of::scan<tags::scan<Tag, tags::inclusive>, Op>(
           std::forward<Op>(op));
}

//////////////////////////////////////////////////////////////////////
/// @brief In an exclusive scan each element in the result is the
/// result of the cumulative operation on all elements before it.
///
/// For example,  if + is used as the operation, each element in the
/// output is the sum of all elements before it, and the first element
/// is the neutral value. An exclusive scan of [1, 2, 3, 4, ...] would
/// be [0, 1, 3, 6, 10, ...].
///
/// @param  op            the operation to be used to compute the scan
///                       results
/// @param  initial_value the value for the first element of the output
/// @tparam Tag           the type of exclusive scan to be used
///
/// @return an exclusive scan specified by the @c Tag
///
/// @see exclusive_scan
///
/// @ingroup skeletonsFunctionalAlgorithm
//////////////////////////////////////////////////////////////////////
template <typename Tag = tags::blelloch,
          typename Op, typename T>
result_of::scan<tags::scan<Tag, tags::exclusive>, Op>
scan(Op&& op, T const& initial_value)
{
  using std::is_same;
  static_assert(is_same<Tag, tags::blelloch>::value,
                "The requested exclusive scan algorithm is not supported");

  return result_of::scan<tags::scan<Tag, tags::exclusive>, Op>(
           std::forward<Op>(op), initial_value);
}

} // namespace skeletons
} // namespace stapl

#endif // STAPL_SKELETONS_FUNCTIONAL_SCAN_HPP


/*

Copyright (c) 2005-2021, University of Oxford.
All rights reserved.

University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.

This file is part of Chaste.

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 University of Oxford 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.

*/

#ifndef ELECTRODESSTIMULSUFACTORY_HPP_
#define ELECTRODESSTIMULSUFACTORY_HPP_

#include <boost/shared_ptr.hpp>
#include "ChasteSerialization.hpp"
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/shared_ptr.hpp>

#include "AbstractTetrahedralMesh.hpp"
#include "DistributedVector.hpp"
#include "AbstractChasteRegion.hpp"
#include "AbstractStimulusFactory.hpp"
#include "LinearBasisFunction.hpp"

/**
 * This class implements the specification of tow electrodes with a RegularStimulus applied to them.
 * It makes sure the compatibility conditions is verified by scaling the magnitude of the second electrode
 * according to the flux flowing thorugh each of them.
 *
 * User needs to pass in a vector of pairs of electrodes and, for each pair, the stimulation parameters.
 * This class will then implement the CreateStimulusForNode accordingly by creating a RegularStimulus object
 * for each node. Each node belonging to any "first electrode" will have an extracellular volume stimulus equals to the value that is passed in,
 * while each node belonging to any "second elecrode" will have an extracellular volume stimulus corrected by the flux (proportional to electrode volume)
 * in such a way that compatibility conditions of the extended bidomain equations are followed.
 *
 * It is possible to ground all second electrodes by calling GroundSecondElectrode. In this case, the member variable
 * mGroundSecondElectrode will have size > 0 and other classes can behave accordingly (e.g., problem class will set Dirichlet
 * boundary conditions accordingly).
 *
 */
template<unsigned DIM>
class ElectrodesStimulusFactory : public AbstractStimulusFactory<DIM>
{
protected:

    friend class TestStimulusFactory;//for testing

    /**
     * Vector of pairs, each pair representing a pair of electrodes.
     */
    std::vector<std::pair<AbstractChasteRegion<DIM>*, AbstractChasteRegion<DIM>*> >& mrElectrodePairs;

    /**
     * Vector of stimuli magnitudes (microA / cm^3). Must be the same size as mrElectrodePairs.
     */
    std::vector<double>& mrMagnitudes;
    /**
     * Vector of stimuli durations (ms). Must be the same size as mrElectrodePairs.
     */
    std::vector<double>& mrDurations;
    /**
     * Vector of stimuli periods (ms). Must be the same size as mrElectrodePairs.
     */
    std::vector<double>& mrPeriods;
    /**
     * Vector of stimuli start times (ms). Must be the same size as mrElectrodePairs.
     */
    std::vector<double>& mrStarts;
    /**
     * Vector of stimuli end times (ms). Must be the same size as mrElectrodePairs.
     */
    std::vector<double>& mrEnds;

    /**
     * Used to store temporarily magnitudes of electrode 1 for correction
     */
    std::vector<double> mMagnitudesElectrode1;

    /**
     * Used to store temporarily magnitudes of electrode 2 for correction
     */
    std::vector<double> mMagnitudesElectrode2;

    /** Whether the second electrode is grounded */
    bool mGroundSecondElectrode;

    /**
     * Helper method to check if any of the electrodes intersects with the other.
     * it needs to check whether any element that conatins any node of electrode 1
     * does not conatin also nodes of eledtrode 2
     */
    void CheckForElectrodesIntersection();

    /** typedef for basis function*/
    typedef LinearBasisFunction<DIM> BasisFunction;

    /**
     * Helper method to compute electrode flux. This method is the main functionality of this class.
     *
     * It essentially computes the contribution of each electrode to the RHS vector of the linear system.
     * The loops over elements and gauss points mimicks the ones in AssembleOnElement.
     * Interpolation is done for the value of the magnitude of the electrode.
     *
     * NOTE that this method assumes the use of a 2nd order Gauss quadrature rule (the default value of Chaste FE assemblers).
     *
     * @param pRegion  the electrode
     * @param stimulusMagnitude the stimulus magnitude
     * @return electrode flux
     */
    double ComputeElectrodeTotalFlux(AbstractChasteRegion<DIM>* pRegion, double stimulusMagnitude);

public:

    /**
     * Constructor. Electrodes and stimulation parameters need to be passed in.
     *
     * @param rElectrodePairs the pairs of electrodes
     * @param rStimulusMagnitudes the magnitudes of the stimuli (microA / cm^3). First electrode will have magnitude value and second electrode will have -magnitude (before being corrected to ensure equal flux).
     * @param rDurations the duration of each stimulus (ms)
     * @param rPeriods the period of each stimulus (ms)
     * @param rStarts the start time of each stimulus (ms).
     * @param rEnds the end of each stimulation (ms)
     */
    ElectrodesStimulusFactory(std::vector<std::pair<AbstractChasteRegion<DIM>*, AbstractChasteRegion<DIM>*> >& rElectrodePairs,
                              std::vector<double>& rStimulusMagnitudes,
                              std::vector<double>& rDurations,
                              std::vector<double>& rPeriods,
                              std::vector<double>& rStarts,
                              std::vector<double>& rEnds);

    /**
     * @return Creates an appropriate stimulus for each node as to abide compatibility conditions.
     *
     * @param pNode the node for which to create the stimulus
     */
    boost::shared_ptr<AbstractStimulusFunction> CreateStimulusForNode(Node<DIM>* pNode);

    /**
     * Destructor
     */
    ~ElectrodesStimulusFactory();

    /**
     * This method checks whether the users wanted a grounded electrode.
     * If so, it fills in mGroundedRegions accordingly.
     *
     * If not, it calls ComputeElectrodeTotalFlux and scales the magnitudes of the electrodes stimuli accordingly
     */
    void SetCompatibleExtracellularStimulus();

    /**
     * Allow the user to ground the second electrode
     *
     * @param grounded : true if second electrode is grounded
     */
    void GroundSecondElectrode(bool grounded);
};

#endif /*ELECTRODESSTIMULSUFACTORY_HPP_*/


// Boost.Geometry
// This file is manually converted from PROJ4

// This file was modified by Oracle on 2018.
// Modifications copyright (c) 2018, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// This file was converted to Geometry Library by Adam Wulkiewicz

// Original copyright notice:
// Author:   Frank Warmerdam, warmerdam@pobox.com

// Copyright (c) 2000, Frank Warmerdam

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP
#define BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP

#include <boost/algorithm/string.hpp>

#include <boost/geometry/core/assert.hpp>

#include <boost/cstdint.hpp>

#include <algorithm>
#include <string>
#include <vector>

namespace boost { namespace geometry { namespace projections
{

namespace detail
{

/************************************************************************/
/*                             swap_words()                             */
/*                                                                      */
/*      Convert the byte order of the given word(s) in place.           */
/************************************************************************/

inline bool is_lsb()
{
    static const int byte_order_test = 1;
    static bool result = (1 == ((const unsigned char *) (&byte_order_test))[0]);
    return result;
}

inline void swap_words( char *data, int word_size, int word_count )
{
    for (int word = 0; word < word_count; word++)
    {
        for (int i = 0; i < word_size/2; i++)
        {
            std::swap(data[i], data[word_size-i-1]);
        }

        data += word_size;
    }
}

inline bool cstr_equal(const char * s1, const char * s2, std::size_t n)
{
    return std::equal(s1, s1 + n, s2);
}

struct is_trimmable_char
{
    inline bool operator()(char ch)
    {
        return ch == '\n' || ch == ' ';
    }
};

// structs originally defined in projects.h

struct pj_ctable
{
    struct lp_t  { double lam, phi; };
    struct flp_t { float lam, phi; };
    struct ilp_t { boost::int32_t lam, phi; };

    std::string id;         // ascii info
    lp_t ll;                // lower left corner coordinates
    lp_t del;               // size of cells
    ilp_t lim;              // limits of conversion matrix
    std::vector<flp_t> cvs; // conversion matrix

    inline void swap(pj_ctable & r)
    {
        id.swap(r.id);
        std::swap(ll, r.ll);
        std::swap(del, r.del);
        std::swap(lim, r.lim);
        cvs.swap(r.cvs);
    }
};

struct pj_gi_load
{
    enum format_t { missing = 0, ntv1, ntv2, gtx, ctable, ctable2 };
    typedef boost::long_long_type offset_t;

    explicit pj_gi_load(std::string const& gname = "",
                        format_t f = missing,
                        offset_t off = 0,
                        bool swap = false)
        : gridname(gname)
        , format(f)
        , grid_offset(off)
        , must_swap(swap)
    {}

    std::string gridname; // identifying name of grid, eg "conus" or ntv2_0.gsb

    format_t format;      // format of this grid, ie "ctable", "ntv1", "ntv2" or "missing".

    offset_t grid_offset; // offset in file, for delayed loading
    bool must_swap;       // only for NTv2

    pj_ctable ct;

    inline void swap(pj_gi_load & r)
    {
        gridname.swap(r.gridname);
        std::swap(format, r.format);
        std::swap(grid_offset, r.grid_offset);
        std::swap(must_swap, r.must_swap);
        ct.swap(r.ct);
    }

};

struct pj_gi
    : pj_gi_load
{
    explicit pj_gi(std::string const& gname = "",
                   pj_gi_load::format_t f = missing,
                   pj_gi_load::offset_t off = 0,
                   bool swap = false)
        : pj_gi_load(gname, f, off, swap)
    {}

    std::vector<pj_gi> children;

    inline void swap(pj_gi & r)
    {
        pj_gi_load::swap(r);
        children.swap(r.children);
    }
};

typedef std::vector<pj_gi> pj_gridinfo;

/************************************************************************/
/*                   pj_gridinfo_load_ctable()                          */
/*                                                                      */
/*      Load the data portion of a ctable formatted grid.               */
/************************************************************************/

// Originally nad_ctable_load() defined in nad_init.c
template <typename IStream>
bool pj_gridinfo_load_ctable(IStream & is, pj_gi_load & gi)
{
    pj_ctable & ct = gi.ct;

    // Move the input stream by the size of the proj4 original CTABLE
    std::size_t header_size = 80
                            + 2 * sizeof(pj_ctable::lp_t)
                            + sizeof(pj_ctable::ilp_t)
                            + sizeof(pj_ctable::flp_t*);
    is.seekg(header_size);

    // read all the actual shift values
    std::size_t a_size = ct.lim.lam * ct.lim.phi;
    ct.cvs.resize(a_size);

    std::size_t ch_size = sizeof(pj_ctable::flp_t) * a_size;
    is.read(reinterpret_cast<char*>(&ct.cvs[0]), ch_size);

    if (is.fail() || is.gcount() != ch_size)
    {
        ct.cvs.clear();
        //ctable loading failed on fread() - binary incompatible?
        return false;
    }

    return true;
}

/************************************************************************/
/*                  pj_gridinfo_load_ctable2()                          */
/*                                                                      */
/*      Load the data portion of a ctable2 formatted grid.              */
/************************************************************************/

// Originally nad_ctable2_load() defined in nad_init.c
template <typename IStream>
bool pj_gridinfo_load_ctable2(IStream & is, pj_gi_load & gi)
{
    pj_ctable & ct = gi.ct;

    is.seekg(160);

    // read all the actual shift values
    std::size_t a_size = ct.lim.lam * ct.lim.phi;
    ct.cvs.resize(a_size);

    std::size_t ch_size = sizeof(pj_ctable::flp_t) * a_size;
    is.read(reinterpret_cast<char*>(&ct.cvs[0]), ch_size);

    if (is.fail() || is.gcount() != ch_size)
    {
        //ctable2 loading failed on fread() - binary incompatible?
        ct.cvs.clear();
        return false;
    }

    if (! is_lsb())
    {
        swap_words(reinterpret_cast<char*>(&ct.cvs[0]), 4, (int)a_size * 2);
    }

    return true;
}

/************************************************************************/
/*                      pj_gridinfo_load_ntv1()                         */
/*                                                                      */
/*      NTv1 format.                                                    */
/*      We process one line at a time.  Note that the array storage     */
/*      direction (e-w) is different in the NTv1 file and what          */
/*      the CTABLE is supposed to have.  The phi/lam are also           */
/*      reversed, and we have to be aware of byte swapping.             */
/************************************************************************/

// originally in pj_gridinfo_load() function
template <typename IStream>
inline bool pj_gridinfo_load_ntv1(IStream & is, pj_gi_load & gi)
{
    static const double s2r = math::d2r<double>() / 3600.0;

    std::size_t const r_size = gi.ct.lim.lam * 2;
    std::size_t const ch_size = sizeof(double) * r_size;

    is.seekg(gi.grid_offset);

    std::vector<double> row_buf(r_size);
    gi.ct.cvs.resize(gi.ct.lim.lam * gi.ct.lim.phi);

    for (boost::int32_t row = 0; row < gi.ct.lim.phi; row++ )
    {
        is.read(reinterpret_cast<char*>(&row_buf[0]), ch_size);

        if (is.fail() || is.gcount() != ch_size)
        {
            gi.ct.cvs.clear();
            return false;
        }

        if (is_lsb())
            swap_words(reinterpret_cast<char*>(&row_buf[0]), 8, (int)r_size);

        // convert seconds to radians
        for (boost::int32_t i = 0; i < gi.ct.lim.lam; i++ )
        {
            pj_ctable::flp_t & cvs = gi.ct.cvs[row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1)];

            cvs.phi = (float) (row_buf[i*2] * s2r);
            cvs.lam = (float) (row_buf[i*2+1] * s2r);
        }
    }

    return true;
}

/* -------------------------------------------------------------------- */
/*                         pj_gridinfo_load_ntv2()                      */
/*                                                                      */
/*      NTv2 format.                                                    */
/*      We process one line at a time.  Note that the array storage     */
/*      direction (e-w) is different in the NTv2 file and what          */
/*      the CTABLE is supposed to have.  The phi/lam are also           */
/*      reversed, and we have to be aware of byte swapping.             */
/* -------------------------------------------------------------------- */

// originally in pj_gridinfo_load() function
template <typename IStream>
inline bool pj_gridinfo_load_ntv2(IStream & is, pj_gi_load & gi)
{
    static const double s2r = math::d2r<double>() / 3600.0;

    std::size_t const r_size = gi.ct.lim.lam * 4;
    std::size_t const ch_size = sizeof(float) * r_size;

    is.seekg(gi.grid_offset);

    std::vector<float> row_buf(r_size);
    gi.ct.cvs.resize(gi.ct.lim.lam * gi.ct.lim.phi);

    for (boost::int32_t row = 0; row < gi.ct.lim.phi; row++ )
    {
        is.read(reinterpret_cast<char*>(&row_buf[0]), ch_size);

        if (is.fail() || is.gcount() != ch_size)
        {
            gi.ct.cvs.clear();
            return false;
        }

        if (gi.must_swap)
        {
            swap_words(reinterpret_cast<char*>(&row_buf[0]), 4, (int)r_size);
        }

        // convert seconds to radians
        for (boost::int32_t i = 0; i < gi.ct.lim.lam; i++ )
        {
            pj_ctable::flp_t & cvs = gi.ct.cvs[row * gi.ct.lim.lam + (gi.ct.lim.lam - i - 1)];

            // skip accuracy values
            cvs.phi = (float) (row_buf[i*4] * s2r);
            cvs.lam = (float) (row_buf[i*4+1] * s2r);
        }
    }

    return true;
}

/************************************************************************/
/*                   pj_gridinfo_load_gtx()                             */
/*                                                                      */
/*      GTX format.                                                     */
/************************************************************************/

// originally in pj_gridinfo_load() function
template <typename IStream>
inline bool pj_gridinfo_load_gtx(IStream & is, pj_gi_load & gi)
{
    boost::int32_t words = gi.ct.lim.lam * gi.ct.lim.phi;
    std::size_t const ch_size = sizeof(float) * words;

    is.seekg(gi.grid_offset);

    // TODO: Consider changing this unintuitive code
    // NOTE: Vertical shift data (one float per point) is stored in a container
    // holding horizontal shift data (two floats per point).
    gi.ct.cvs.resize((words + 1) / 2);

    is.read(reinterpret_cast<char*>(&gi.ct.cvs[0]), ch_size);

    if (is.fail() || is.gcount() != ch_size)
    {
        gi.ct.cvs.clear();
        return false;
    }

    if (is_lsb())
    {
        swap_words(reinterpret_cast<char*>(&gi.ct.cvs[0]), 4, words);
    }

    return true;
}

/************************************************************************/
/*                          pj_gridinfo_load()                          */
/*                                                                      */
/*      This function is intended to implement delayed loading of       */
/*      the data contents of a grid file.  The header and related       */
/*      stuff are loaded by pj_gridinfo_init().                         */
/************************************************************************/

template <typename IStream>
inline bool pj_gridinfo_load(IStream & is, pj_gi_load & gi)
{
    if (! gi.ct.cvs.empty())
    {
        return true;
    }

    if (! is.is_open())
    {
        return false;
    }

    // Original platform specific CTable format.
    if (gi.format == pj_gi::ctable)
    {
        return pj_gridinfo_load_ctable(is, gi);
    }
    // CTable2 format.
    else if (gi.format == pj_gi::ctable2)
    {
        return pj_gridinfo_load_ctable2(is, gi);
    }
    // NTv1 format.
    else if (gi.format == pj_gi::ntv1)
    {
        return pj_gridinfo_load_ntv1(is, gi);
    }
    // NTv2 format.
    else if (gi.format == pj_gi::ntv2)
    {
        return pj_gridinfo_load_ntv2(is, gi);
    }
    // GTX format.
    else if (gi.format == pj_gi::gtx)
    {
        return pj_gridinfo_load_gtx(is, gi);
    }
    else
    {
        return false;
    }
}

/************************************************************************/
/*                        pj_gridinfo_parent()                          */
/*                                                                      */
/*      Seek a parent grid file by name from a grid list                */
/************************************************************************/

template <typename It>
inline It pj_gridinfo_parent(It first, It last, std::string const& name)
{
    for ( ; first != last ; ++first)
    {
        if (first->ct.id == name)
            return first;

        It parent = pj_gridinfo_parent(first->children.begin(), first->children.end(), name);
        if( parent != first->children.end() )
            return parent;
    }

    return last;
}

/************************************************************************/
/*                       pj_gridinfo_init_ntv2()                        */
/*                                                                      */
/*      Load a ntv2 (.gsb) file.                                        */
/************************************************************************/

template <typename IStream>
inline bool pj_gridinfo_init_ntv2(std::string const& gridname,
                                  IStream & is,
                                  pj_gridinfo & gridinfo)
{
    BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
    BOOST_STATIC_ASSERT( sizeof(double) == 8 );

    static const double s2r = math::d2r<double>() / 3600.0;

    std::size_t gridinfo_orig_size = gridinfo.size();

    // Read the overview header.
    char header[11*16];

    is.read(header, sizeof(header));
    if( is.fail() )
    {
        return false;
    }

    bool must_swap = (header[8] == 11)
                   ? !is_lsb()
                   : is_lsb();

    // NOTE: This check is not implemented in proj4
    if (! cstr_equal(header + 56, "SECONDS", 7))
    {
        return false;
    }

    // Byte swap interesting fields if needed.
    if( must_swap )
    {
        swap_words( header+8, 4, 1 );
        swap_words( header+8+16, 4, 1 );
        swap_words( header+8+32, 4, 1 );
        swap_words( header+8+7*16, 8, 1 );
        swap_words( header+8+8*16, 8, 1 );
        swap_words( header+8+9*16, 8, 1 );
        swap_words( header+8+10*16, 8, 1 );
    }

    // Get the subfile count out ... all we really use for now.
    boost::int32_t num_subfiles;
    memcpy( &num_subfiles, header+8+32, 4 );

    // Step through the subfiles, creating a PJ_GRIDINFO for each.
    for( boost::int32_t subfile = 0; subfile < num_subfiles; subfile++ )
    {
        // Read header.
        is.read(header, sizeof(header));
        if( is.fail() )
        {
            return false;
        }

        if(! cstr_equal(header, "SUB_NAME", 8))
        {
            return false;
        }

        // Byte swap interesting fields if needed.
        if( must_swap )
        {
            swap_words( header+8+16*4, 8, 1 );
            swap_words( header+8+16*5, 8, 1 );
            swap_words( header+8+16*6, 8, 1 );
            swap_words( header+8+16*7, 8, 1 );
            swap_words( header+8+16*8, 8, 1 );
            swap_words( header+8+16*9, 8, 1 );
            swap_words( header+8+16*10, 4, 1 );
        }

        // Initialize a corresponding "ct" structure.
        pj_ctable ct;
        pj_ctable::lp_t ur;

        ct.id = std::string(header + 8, 8);

        ct.ll.lam = - *((double *) (header+7*16+8)); /* W_LONG */
        ct.ll.phi = *((double *) (header+4*16+8));   /* S_LAT */

        ur.lam = - *((double *) (header+6*16+8));     /* E_LONG */
        ur.phi = *((double *) (header+5*16+8));       /* N_LAT */

        ct.del.lam = *((double *) (header+9*16+8));
        ct.del.phi = *((double *) (header+8*16+8));

        ct.lim.lam = (boost::int32_t) (fabs(ur.lam-ct.ll.lam)/ct.del.lam + 0.5) + 1;
        ct.lim.phi = (boost::int32_t) (fabs(ur.phi-ct.ll.phi)/ct.del.phi + 0.5) + 1;

        ct.ll.lam *= s2r;
        ct.ll.phi *= s2r;
        ct.del.lam *= s2r;
        ct.del.phi *= s2r;

        boost::int32_t gs_count;
        memcpy( &gs_count, header + 8 + 16*10, 4 );
        if( gs_count != ct.lim.lam * ct.lim.phi )
        {
            return false;
        }

        //ct.cvs.clear();

        // Create a new gridinfo for this if we aren't processing the
        // 1st subfile, and initialize our grid info.

        // Attach to the correct list or sublist.

        // TODO is offset needed?
        pj_gi gi(gridname, pj_gi::ntv2, is.tellg(), must_swap);
        gi.ct = ct;

        if( subfile == 0 )
        {
            gridinfo.push_back(gi);
        }
        else if( cstr_equal(header+24, "NONE", 4) )
        {
            gridinfo.push_back(gi);
        }
        else
        {
            pj_gridinfo::iterator git = pj_gridinfo_parent(gridinfo.begin() + gridinfo_orig_size,
                                                           gridinfo.end(),
                                                           std::string((const char*)header+24, 8));

            if( git == gridinfo.end() )
            {
                gridinfo.push_back(gi);
            }
            else
            {
                git->children.push_back(gi);
            }
        }

        // Seek past the data.
        is.seekg(gs_count * 16, std::ios::cur);
    }

    return true;
}

/************************************************************************/
/*                       pj_gridinfo_init_ntv1()                        */
/*                                                                      */
/*      Load an NTv1 style Canadian grid shift file.                    */
/************************************************************************/

template <typename IStream>
inline bool pj_gridinfo_init_ntv1(std::string const& gridname,
                                  IStream & is,
                                  pj_gridinfo & gridinfo)
{
    BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
    BOOST_STATIC_ASSERT( sizeof(double) == 8 );

    static const double d2r = math::d2r<double>();

    // Read the header.
    char header[176];

    is.read(header, sizeof(header));
    if( is.fail() )
    {
        return false;
    }

    // Regularize fields of interest.
    if( is_lsb() )
    {
        swap_words( header+8, 4, 1 );
        swap_words( header+24, 8, 1 );
        swap_words( header+40, 8, 1 );
        swap_words( header+56, 8, 1 );
        swap_words( header+72, 8, 1 );
        swap_words( header+88, 8, 1 );
        swap_words( header+104, 8, 1 );
    }

    // NTv1 grid shift file has wrong record count, corrupt?
    if( *((boost::int32_t *) (header+8)) != 12 )
    {
        return false;
    }

    // NOTE: This check is not implemented in proj4
    if (! cstr_equal(header + 120, "SECONDS", 7))
    {
        return false;
    }

    // Fill in CTABLE structure.
    pj_ctable ct;
    pj_ctable::lp_t ur;

    ct.id = "NTv1 Grid Shift File";

    ct.ll.lam = - *((double *) (header+72));
    ct.ll.phi = *((double *) (header+24));
    ur.lam = - *((double *) (header+56));
    ur.phi = *((double *) (header+40));
    ct.del.lam = *((double *) (header+104));
    ct.del.phi = *((double *) (header+88));
    ct.lim.lam = (boost::int32_t) (fabs(ur.lam-ct.ll.lam)/ct.del.lam + 0.5) + 1;
    ct.lim.phi = (boost::int32_t) (fabs(ur.phi-ct.ll.phi)/ct.del.phi + 0.5) + 1;

    ct.ll.lam *= d2r;
    ct.ll.phi *= d2r;
    ct.del.lam *= d2r;
    ct.del.phi *= d2r;
    //ct.cvs.clear();

    // is offset needed?
    gridinfo.push_back(pj_gi(gridname, pj_gi::ntv1, is.tellg()));
    gridinfo.back().ct = ct;

    return true;
}

/************************************************************************/
/*                       pj_gridinfo_init_gtx()                         */
/*                                                                      */
/*      Load a NOAA .gtx vertical datum shift file.                     */
/************************************************************************/

template <typename IStream>
inline bool pj_gridinfo_init_gtx(std::string const& gridname,
                                 IStream & is,
                                 pj_gridinfo & gridinfo)
{
    BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
    BOOST_STATIC_ASSERT( sizeof(double) == 8 );

    static const double d2r = math::d2r<double>();

    // Read the header.
    char header[40];

    is.read(header, sizeof(header));
    if( is.fail() )
    {
        return false;
    }

    // Regularize fields of interest and extract.
    double         xorigin, yorigin, xstep, ystep;
    boost::int32_t rows, columns;

    if( is_lsb() )
    {
        swap_words( header+0, 8, 4 );
        swap_words( header+32, 4, 2 );
    }

    memcpy( &yorigin, header+0, 8 );
    memcpy( &xorigin, header+8, 8 );
    memcpy( &ystep, header+16, 8 );
    memcpy( &xstep, header+24, 8 );

    memcpy( &rows, header+32, 4 );
    memcpy( &columns, header+36, 4 );

    // gtx file header has invalid extents, corrupt?
    if( xorigin < -360 || xorigin > 360
        || yorigin < -90 || yorigin > 90 )
    {
        return false;
    }

    // Fill in CTABLE structure.
    pj_ctable ct;

    ct.id = "GTX Vertical Grid Shift File";

    ct.ll.lam = xorigin;
    ct.ll.phi = yorigin;
    ct.del.lam = xstep;
    ct.del.phi = ystep;
    ct.lim.lam = columns;
    ct.lim.phi = rows;

    // some GTX files come in 0-360 and we shift them back into the
    // expected -180 to 180 range if possible. This does not solve
    // problems with grids spanning the dateline.
    if( ct.ll.lam >= 180.0 )
        ct.ll.lam -= 360.0;

    if( ct.ll.lam >= 0.0 && ct.ll.lam + ct.del.lam * ct.lim.lam > 180.0 )
    {
        //"This GTX spans the dateline!  This will cause problems." );
    }

    ct.ll.lam *= d2r;
    ct.ll.phi *= d2r;
    ct.del.lam *= d2r;
    ct.del.phi *= d2r;
    //ct.cvs.clear();

    // is offset needed?
    gridinfo.push_back(pj_gi(gridname, pj_gi::gtx, 40));
    gridinfo.back().ct = ct;

    return true;
}

/************************************************************************/
/*                   pj_gridinfo_init_ctable2()                         */
/*                                                                      */
/*      Read the header portion of a "ctable2" format grid.             */
/************************************************************************/

// Originally nad_ctable2_init() defined in nad_init.c
template <typename IStream>
inline bool pj_gridinfo_init_ctable2(std::string const& gridname,
                                     IStream & is,
                                     pj_gridinfo & gridinfo)
{
    BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
    BOOST_STATIC_ASSERT( sizeof(double) == 8 );

    char header[160];

    is.read(header, sizeof(header));
    if( is.fail() )
    {
        return false;
    }

    if( !is_lsb() )
    {
        swap_words( header +  96, 8, 4 );
        swap_words( header + 128, 4, 2 );
    }

    // ctable2 - wrong header!
    if (! cstr_equal(header, "CTABLE V2", 9))
    {
        return false;
    }

    // read the table header
    pj_ctable ct;

    ct.id = std::string(header + 16, std::find(header + 16, header + 16 + 80, '\0'));
    //memcpy( &ct.ll.lam,  header +  96, 8 );
    //memcpy( &ct.ll.phi,  header + 104, 8 );
    //memcpy( &ct.del.lam, header + 112, 8 );
    //memcpy( &ct.del.phi, header + 120, 8 );
    //memcpy( &ct.lim.lam, header + 128, 4 );
    //memcpy( &ct.lim.phi, header + 132, 4 );
    memcpy( &ct.ll,  header +  96, 40 );

    // do some minimal validation to ensure the structure isn't corrupt
    if ( ct.lim.lam < 1 || ct.lim.lam > 100000
      || ct.lim.phi < 1 || ct.lim.phi > 100000 )
    {
        return false;
    }

    // trim white space and newlines off id
    boost::trim_right_if(ct.id, is_trimmable_char());

    //ct.cvs.clear();

    gridinfo.push_back(pj_gi(gridname, pj_gi::ctable2));
    gridinfo.back().ct = ct;

    return true;
}

/************************************************************************/
/*                    pj_gridinfo_init_ctable()                         */
/*                                                                      */
/*      Read the header portion of a "ctable" format grid.              */
/************************************************************************/

// Originally nad_ctable_init() defined in nad_init.c
template <typename IStream>
inline bool pj_gridinfo_init_ctable(std::string const& gridname,
                                    IStream & is,
                                    pj_gridinfo & gridinfo)
{
    BOOST_STATIC_ASSERT( sizeof(boost::int32_t) == 4 );
    BOOST_STATIC_ASSERT( sizeof(double) == 8 );

    // 80 + 2*8 + 2*8 + 2*4
    char header[120];

    // NOTE: in proj4 data is loaded directly into CTABLE

    is.read(header, sizeof(header));
    if( is.fail() )
    {
        return false;
    }

    // NOTE: in proj4 LSB is not checked here

    // read the table header
    pj_ctable ct;

    ct.id = std::string(header, std::find(header, header + 80, '\0'));
    memcpy( &ct.ll, header + 80, 40 );

    // do some minimal validation to ensure the structure isn't corrupt
    if ( ct.lim.lam < 1 || ct.lim.lam > 100000
      || ct.lim.phi < 1 || ct.lim.phi > 100000 )
    {
        return false;
    }

    // trim white space and newlines off id
    boost::trim_right_if(ct.id, is_trimmable_char());

    //ct.cvs.clear();

    gridinfo.push_back(pj_gi(gridname, pj_gi::ctable));
    gridinfo.back().ct = ct;

    return true;
}

/************************************************************************/
/*                          pj_gridinfo_init()                          */
/*                                                                      */
/*      Open and parse header details from a datum gridshift file       */
/*      returning a list of PJ_GRIDINFOs for the grids in that          */
/*      file.  This superceeds use of nad_init() for modern             */
/*      applications.                                                   */
/************************************************************************/

template <typename IStream>
inline bool pj_gridinfo_init(std::string const& gridname,
                             IStream & is,
                             pj_gridinfo & gridinfo)
{
    char header[160];

    // Check if the stream is opened.
    if (! is.is_open()) {
        return false;
    }

    // Load a header, to determine the file type.
    is.read(header, sizeof(header));

    if ( is.fail() ) {
        return false;
    }

    is.seekg(0);

    // Determine file type.
    if ( cstr_equal(header + 0, "HEADER", 6)
      && cstr_equal(header + 96, "W GRID", 6)
      && cstr_equal(header + 144, "TO      NAD83   ", 16) )
    {
        return pj_gridinfo_init_ntv1(gridname, is, gridinfo);
    }
    else if( cstr_equal(header + 0, "NUM_OREC", 8)
          && cstr_equal(header + 48, "GS_TYPE", 7) )
    {
        return pj_gridinfo_init_ntv2(gridname, is, gridinfo);
    }
    else if( boost::algorithm::ends_with(gridname, "gtx")
          || boost::algorithm::ends_with(gridname, "GTX") )
    {
        return pj_gridinfo_init_gtx(gridname, is, gridinfo);
    }
    else if( cstr_equal(header + 0, "CTABLE V2", 9) )
    {
        return pj_gridinfo_init_ctable2(gridname, is, gridinfo);
    }
    else
    {
        return pj_gridinfo_init_ctable(gridname, is, gridinfo);
    }
}

} // namespace detail

}}} // namespace boost::geometry::projections

#endif // BOOST_GEOMETRY_SRS_PROJECTIONS_IMPL_PJ_GRIDINFO_HPP