//============================================================================
// Name         : dnatransformationparameters.hpp
// Author       : Roger Fraser
// Contributors : Joshua Batchelor
// Version      : 1.00
// Copyright    : Copyright 2017 Geoscience Australia
//
//                Licensed under the Apache License, Version 2.0 (the "License");
//                you may not use this file except in compliance with the License.
//                You may obtain a copy of the License at
//
//                http ://www.apache.org/licenses/LICENSE-2.0
//
//                Unless required by applicable law or agreed to in writing, software
//                distributed under the License is distributed on an "AS IS" BASIS,
//                WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//                See the License for the specific language governing permissions and
//                limitations under the License.
//
// Description  : Declaration/definition of 7/14 parameters
//============================================================================

#ifndef DNATRANSFORM_PARAM_H_
#define DNATRANSFORM_PARAM_H_

#if defined(_MSC_VER)
	#if defined(LIST_INCLUDES_ON_BUILD)
		#pragma message("  " __FILE__)
	#endif
#endif

#include <string>
#include <iomanip>
#include <sstream>
#include <boost/exception_ptr.hpp>

#include <include/parameters/dnaepsg.hpp>

using namespace std;
using namespace boost;

using namespace dynadjust::epsg;

namespace dynadjust {
namespace datum_parameters {

typedef enum
{
	__paramForward__ = 0,
	__paramReverse__ = 1
} transformationDirection;

typedef enum
{
	__static_to_static__ =   0,
	__static_to_dynamic__ =  1,
	__static_to_step__ =     2,
	__dynamic_to_step__ =    3,
	__dynamic_to_static__ =  4,
	__dynamic_to_dynamic__ = 5,
	__step_to_dynamic__ =    6,
	__step_to_static__ =     7,
	__plate_motion_model__ = 8
} transformationType;

typedef enum
{
	__frame_frame_same__ = 0,
	__frame_frame_diff__ = 1
} frameSimilarity;

typedef enum
{
	__epoch_epoch_same__ = 0,
	__epoch_epoch_diff__ = 1
} epochSimilarity;

typedef struct transformation_parameter_set_ {
	uint32_uint32_pair			from_to_;				// integer epsg codes for 'from' and 'to' datums
	double						parameters_[14];		// the transformation parameters
	double						reference_epoch_;		// reference epoch for the parameters
	UINT32						reference_frame_;		// reference frame for the parameters
	transformationDirection		paramDirection_;		// the direction of the current set
	void reverse() {
		parameters_[0] *= -1.;
		parameters_[1] *= -1.;
		parameters_[2] *= -1.;
		parameters_[3] *= -1.;
		parameters_[4] *= -1.;
		parameters_[5] *= -1.;
		parameters_[6] *= -1.;
		parameters_[7] *= -1.;
		parameters_[8] *= -1.;
		parameters_[9] *= -1.;
		parameters_[10] *= -1.;
		parameters_[11] *= -1.;
		parameters_[12] *= -1.;
		parameters_[13] *= -1.;
	}
	void add(const double* parameters) {
		parameters_[0] += parameters[0];
		parameters_[1] += parameters[1];
		parameters_[2] += parameters[2];
		parameters_[3] += parameters[3];
		parameters_[4] += parameters[4];
		parameters_[5] += parameters[5];
		parameters_[6] += parameters[6];
		parameters_[7] += parameters[7];
		parameters_[8] += parameters[8];
		parameters_[9] += parameters[9];
		parameters_[10] += parameters[10];
		parameters_[11] += parameters[11];
		parameters_[12] += parameters[12];
		parameters_[13] += parameters[13];
	}
} transformation_parameter_set;

typedef vector<transformation_parameter_set> vtransparams;

////////////////////////////////////////////////////////////////////////////////////
// TODO - This file uses hard-coded values for defining transformation parameters!
// The most suitable approach is to load transformation parameters from an
// authoritative file.
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
//             SPECIAL NOTE ON ROTATION RATES FOR IERS PARAMETERS
//         ----------------------------------------------------------
//
// There are two different ways of applying the sign conventions for the rotations.
// In both cases a positive rotation is an anti-clockwise rotation, when viewed along
// the positive axis towards the origin but:
//
//   1. The IERS assumes the rotations to be of the points around the
//      cartesian axes, while;
//   2. The method historically used in Australia assumes the rotations
//      to be of the Cartesian axes around the points.
//
// Although these two conventions exist, to enforce the property that all rotations
// describe anticlockwise rotation as positive when viewed along the axis towards the
// origin, the rotation of the coordinate axes around the points should be a
// skew-symmetric matrix with the opposite sign to the rotation of the point(s)
// around the coordinate axis.
//
// To avoid confusion and to achieve consistency in the implementation of the
// transformation functions, all published IERS rotations and associated rotation rates
// have been reversed.
//
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRF2000 <-> ITRF...
// http://itrf.ensg.ign.fr/doc_ITRF/Transfo-ITRF2000_ITRFs.txt

////////////////////////////////////////////////////////////////////////////////////
//             SPECIAL NOTE ON IERS PARAMETERS for ITRF2000 -> ITRFxx
//         ----------------------------------------------------------
//
// Despite what the website says, the linear values for translation are in centimetres,
// not millimetres.
//
// To avoid confusion and to achieve consistency in the implementation of the
// transformation functions, all published IERS translations and associated
// translation rates have been converted to millimetres.
//
////////////////////////////////////////////////////////////////////////////////////

// ITRF 2000 -> ITRF 1997
template <class T, class U>
struct _itrf2000_to_itrf1997_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1997_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1997_<T, U>::reference_epoch = 1997.0;

template <class T, class U>
const T _itrf2000_to_itrf1997_<T, U>::transformationParameters[14] =
{
	  6.7,		// x translation (millimetres) - CONVERTED FROM CM
	  6.1,		// y translation (millimetres) - CONVERTED FROM CM
	-18.5,		// z translation (millimetres) - CONVERTED FROM CM
	 1.55,		// scale (ppb)
	  0.0,		// x rotation (milli-arc-seconds)
	  0.0,		// y rotation (milli-arc-seconds)
	  0.0,		// z rotation (milli-arc-seconds)

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1997 : public _itrf2000_to_itrf1997_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1996
template <class T, class U>
struct _itrf2000_to_itrf1996_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1996_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1996_<T, U>::reference_epoch = 1997.0;

template <class T, class U>
const T _itrf2000_to_itrf1996_<T, U>::transformationParameters[14] =
{
	  6.7,		// x translation (millimetres) - CONVERTED FROM CM
	  6.1,		// y translation (millimetres) - CONVERTED FROM CM
	-18.5,		// z translation (millimetres) - CONVERTED FROM CM
	 1.55,		// scale (ppb)
	  0.0,		// x rotation (milli-arc-seconds)
	  0.0,		// y rotation (milli-arc-seconds)
	  0.0,		// z rotation (milli-arc-seconds)

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1996 : public _itrf2000_to_itrf1996_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1994
template <class T, class U>
struct _itrf2000_to_itrf1994_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1994_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1994_<T, U>::reference_epoch = 1997.0;

template <class T, class U>
const T _itrf2000_to_itrf1994_<T, U>::transformationParameters[14] =
{
	  6.7,		// x translation (millimetres) - CONVERTED FROM CM
	  6.1,		// y translation (millimetres) - CONVERTED FROM CM
	-18.5,		// z translation (millimetres) - CONVERTED FROM CM
	 1.55,		// scale (ppb)
	  0.0,		// x rotation (milli-arc-seconds)
	  0.0,		// y rotation (milli-arc-seconds)
	  0.0,		// z rotation (milli-arc-seconds)

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1994 : public _itrf2000_to_itrf1994_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1993
template <class T, class U>
struct _itrf2000_to_itrf1993_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1993_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1993_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1993_<T, U>::transformationParameters[14] =
{
	 12.7,		// x translation (millimetres) - CONVERTED FROM CM
	  6.5,		// y translation (millimetres) - CONVERTED FROM CM
	-20.9,		// z translation (millimetres) - CONVERTED FROM CM
	 1.95,		// scale (ppb)
	 0.39,		// x rotation (milli-arc-seconds) - REVERSED
	-0.80,		// y rotation (milli-arc-seconds) - REVERSED
	 1.14,		// z rotation (milli-arc-seconds) - REVERSED

	 -2.9,		// x translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -0.2,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -0.6,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.11,		// x rotation rate (milli-arc-seconds p/yr) - REVERSED
	 0.19,		// y rotation rate (milli-arc-seconds p/yr) - REVERSED
	-0.07		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1993 : public _itrf2000_to_itrf1993_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1992
template <class T, class U>
struct _itrf2000_to_itrf1992_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1992_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1992_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1992_<T, U>::transformationParameters[14] =
{
	 14.7,		// x translation (millimetres) - CONVERTED FROM CM
	 13.5,		// y translation (millimetres) - CONVERTED FROM CM
	-13.9,		// z translation (millimetres) - CONVERTED FROM CM
	 0.75,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.18,		// z rotation (milli-arc-seconds) - REVERSED

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1992	: public _itrf2000_to_itrf1992_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1991
template <class T, class U>
struct _itrf2000_to_itrf1991_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1991_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1991_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1991_<T, U>::transformationParameters[14] =
{
	 26.7,		// x translation (millimetres) - CONVERTED FROM CM
	 27.5,		// y translation (millimetres) - CONVERTED FROM CM
	-19.9,		// z translation (millimetres) - CONVERTED FROM CM
	 2.15,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.18,		// z rotation (milli-arc-seconds) - REVERSED

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1991	: public _itrf2000_to_itrf1991_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1990
template <class T, class U>
struct _itrf2000_to_itrf1990_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1990_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1990_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1990_<T, U>::transformationParameters[14] =
{
	 24.7,		// x translation (millimetres) - CONVERTED FROM CM
	 23.5,		// y translation (millimetres) - CONVERTED FROM CM
	-35.9,		// z translation (millimetres) - CONVERTED FROM CM
	 2.45,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.18,		// z rotation (milli-arc-seconds) - REVERSED

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1990 : public _itrf2000_to_itrf1990_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1989
template <class T, class U>
struct _itrf2000_to_itrf1989_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1989_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1989_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1989_<T, U>::transformationParameters[14] =
{
	 29.7,		// x translation (millimetres) - CONVERTED FROM CM
	 47.5,		// y translation (millimetres) - CONVERTED FROM CM
	-73.9,		// z translation (millimetres) - CONVERTED FROM CM
	 5.85,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.18,		// z rotation (milli-arc-seconds) - REVERSED

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1989 : public _itrf2000_to_itrf1989_<T, U>
{
public:
};

// ITRF 2000 -> ITRF 1988
template <class T, class U>
struct _itrf2000_to_itrf1988_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_itrf1988_<T, U>::reference_frame = ITRF2000_i;

template <class T, class U>
const T _itrf2000_to_itrf1988_<T, U>::reference_epoch = 1988.0;

template <class T, class U>
const T _itrf2000_to_itrf1988_<T, U>::transformationParameters[14] =
{
	 24.7,		// x translation (millimetres) - CONVERTED FROM CM
	 11.5,		// y translation (millimetres) - CONVERTED FROM CM
	-97.9,		// z translation (millimetres) - CONVERTED FROM CM
	 8.95,		// scale (ppb)
	-0.10,		// x rotation (milli-arc-seconds) - REVERSED
	 0.00,		// y rotation (milli-arc-seconds)
	 0.18,		// z rotation (milli-arc-seconds) - REVERSED

	  0.0,		// x translation rate (millimetres p/yr)
	 -0.6,		// y translation rate (millimetres p/yr) - CONVERTED FROM CM
	 -1.4,		// z translation rate (millimetres p/yr) - CONVERTED FROM CM
	 0.01,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2000_ITRF1988 : public _itrf2000_to_itrf1988_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRF2005 <-> ITRF2000
// http://itrf.ensg.ign.fr/doc_ITRF/Transfo-ITRF2005_ITRF2000.txt

// ITRF 2005 -> ITRF 2000
template <class T, class U>
struct _itrf2005_to_itrf2000_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2005_to_itrf2000_<T, U>::reference_frame = ITRF2005_i;

template <class T, class U>
const T _itrf2005_to_itrf2000_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2005_to_itrf2000_<T, U>::transformationParameters[14] =
{
	  0.1,		// x translation (millimetres)
	 -0.8,		// y translation (millimetres)
	 -5.8,		// z translation (millimetres)
	 0.40,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	 -0.2,		// x translation rate (millimetres p/yr)
	  0.1,		// y translation rate (millimetres p/yr)
	 -1.8,		// z translation rate (millimetres p/yr)
	 0.08,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2005_ITRF2000 : public _itrf2005_to_itrf2000_<T, U>
{
public:
};
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRF2008 <-> ITRF...
// http://itrf.ensg.ign.fr/doc_ITRF/Transfo-ITRF2008_ITRFs.txt

// ITRF 2008 -> ITRF 2005
template <class T, class U>
struct _itrf2008_to_itrf2005_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf2005_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf2005_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf2005_<T, U>::transformationParameters[14] =
{
	 -2.0,		// x translation (millimetres). Note this differs from Zuheir's JOG paper DOI 10.1007/s00190-011-0444-4
	 -0.9,		// y translation (millimetres)
	 -4.7,		// z translation (millimetres)
	 0.94,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	  0.3,		// x translation rate (millimetres p/yr)
	  0.0,		// y translation rate (millimetres p/yr)
	  0.0,		// z translation rate (millimetres p/yr)
	 0.00,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2008_ITRF2005 : public _itrf2008_to_itrf2005_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 2000
template <class T, class U>
struct _itrf2008_to_itrf2000_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf2000_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf2000_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf2000_<T, U>::transformationParameters[14] =
{
	 -1.9,		// x translation (millimetres)
	 -1.7,		// y translation (millimetres)
	-10.5,		// z translation (millimetres)
	 1.34,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	  0.1,		// x translation rate (millimetres p/yr)
	  0.1,		// y translation rate (millimetres p/yr)
	 -1.8,		// z translation rate (millimetres p/yr)
	 0.08,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2008_ITRF2000 : public _itrf2008_to_itrf2000_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1997
template <class T, class U>
struct _itrf2008_to_itrf1997_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1997_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1997_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1997_<T, U>::transformationParameters[14] =
{
	  4.8,		// x translation (millimetres)
	  2.6,		// y translation (millimetres)
	-33.2,		// z translation (millimetres)
	 2.92,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1997 : public _itrf2008_to_itrf1997_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1996
template <class T, class U>
struct _itrf2008_to_itrf1996_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1996_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1996_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1996_<T, U>::transformationParameters[14] =
{
	  4.8,		// x translation (millimetres)
	  2.6,		// y translation (millimetres)
	-33.2,		// z translation (millimetres)
	 2.92,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1996 : public _itrf2008_to_itrf1996_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1994
template <class T, class U>
struct _itrf2008_to_itrf1994_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1994_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1994_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1994_<T, U>::transformationParameters[14] =
{
	  4.8,		// x translation (millimetres)
	  2.6,		// y translation (millimetres)
	-33.2,		// z translation (millimetres)
	 2.92,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1994 : public _itrf2008_to_itrf1994_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1993
template <class T, class U>
struct _itrf2008_to_itrf1993_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1993_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1993_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1993_<T, U>::transformationParameters[14] =
{
	-24.0,		// x translation (millimetres)
	  2.4,		// y translation (millimetres)
	-38.6,		// z translation (millimetres)
	 3.41,		// scale (ppb)
	 1.71,		// x rotation (milli-arc-seconds) - REVERSED
	 1.48,		// y rotation (milli-arc-seconds) - REVERSED
	 0.30,		// z rotation (milli-arc-seconds) - REVERSED

	 -2.8,		// x translation rate (millimetres p/yr)
	 -0.1,		// y translation rate (millimetres p/yr)
	 -2.4,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.11,		// x rotation rate (milli-arc-seconds p/yr) - REVERSED
	 0.19,		// y rotation rate (milli-arc-seconds p/yr) - REVERSED
	-0.07		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1993 : public _itrf2008_to_itrf1993_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1992
template <class T, class U>
struct _itrf2008_to_itrf1992_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1992_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1992_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1992_<T, U>::transformationParameters[14] =
{
	 12.8,		// x translation (millimetres)
	  4.6,		// y translation (millimetres)
	-41.2,		// z translation (millimetres)
	 2.21,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1992 : public _itrf2008_to_itrf1992_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1991
template <class T, class U>
struct _itrf2008_to_itrf1991_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1991_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1991_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1991_<T, U>::transformationParameters[14] =
{
	 24.8,		// x translation (millimetres)
	 18.6,		// y translation (millimetres)
	-47.2,		// z translation (millimetres)
	 3.61,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1991 : public _itrf2008_to_itrf1991_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1990
template <class T, class U>
struct _itrf2008_to_itrf1990_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1990_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1990_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1990_<T, U>::transformationParameters[14] =
{
	 22.8,		// x translation (millimetres)
	 14.6,		// y translation (millimetres)
	-63.2,		// z translation (millimetres)
	 3.91,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1990 : public _itrf2008_to_itrf1990_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1989
template <class T, class U>
struct _itrf2008_to_itrf1989_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1989_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1989_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1989_<T, U>::transformationParameters[14] =
{
	 27.8,		// x translation (millimetres)
	 38.6,		// y translation (millimetres)
   -101.2,		// z translation (millimetres)
	 7.31,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1989 : public _itrf2008_to_itrf1989_<T, U>
{
public:
};

// ITRF 2008 -> ITRF 1988
template <class T, class U>
struct _itrf2008_to_itrf1988_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_itrf1988_<T, U>::reference_frame = ITRF2008_i;

template <class T, class U>
const T _itrf2008_to_itrf1988_<T, U>::reference_epoch = 2000.0;

template <class T, class U>
const T _itrf2008_to_itrf1988_<T, U>::transformationParameters[14] =
{
	 22.8,		// x translation (millimetres)
	  2.6,		// y translation (millimetres)
   -125.2,		// z translation (millimetres)
	10.41,		// scale (ppb)
	-0.10,		// x rotation (milli-arc-seconds) - REVERSED
	 0.00,		// y rotation (milli-arc-seconds)
	-0.06,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.2,		// z translation rate (millimetres p/yr)
	 0.09,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2008_ITRF1988 : public _itrf2008_to_itrf1988_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRF2014 <-> ITRF2008
// http://itrf.ensg.ign.fr/doc_ITRF/Transfo-ITRF2014_ITRFs.txt

// ITRF 2014 -> ITRF 2008
template <class T, class U>
struct _itrf2014_to_itrf2008_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf2008_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf2008_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf2008_<T, U>::transformationParameters[14] =
{
	  1.6,		// x translation (millimetres)
	  1.9,		// y translation (millimetres)
	  2.4,		// z translation (millimetres)
	-0.02,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	  0.0,		// x translation rate (millimetres p/yr)
	  0.0,		// y translation rate (millimetres p/yr)
	 -0.1,		// z translation rate (millimetres p/yr)
	 0.03,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2014_ITRF2008 : public _itrf2014_to_itrf2008_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 2005
template <class T, class U>
struct _itrf2014_to_itrf2005_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf2005_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf2005_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf2005_<T, U>::transformationParameters[14] =
{
	  2.6,		// x translation (millimetres)
	  1.0,		// y translation (millimetres)
	 -2.3,		// z translation (millimetres)
	 0.92,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	  0.3,		// x translation rate (millimetres p/yr)
	  0.0,		// y translation rate (millimetres p/yr)
	 -0.1,		// z translation rate (millimetres p/yr)
	 0.03,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2014_ITRF2005 : public _itrf2014_to_itrf2005_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 2000
template <class T, class U>
struct _itrf2014_to_itrf2000_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf2000_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf2000_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf2000_<T, U>::transformationParameters[14] =
{
	  0.7,		// x translation (millimetres)
	  1.2,		// y translation (millimetres)
	-26.1,		// z translation (millimetres)
	 2.12,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	 0.00,		// z rotation (milli-arc-seconds)

	  0.1,		// x translation rate (millimetres p/yr)
	  0.1,		// y translation rate (millimetres p/yr)
	 -1.9,		// z translation rate (millimetres p/yr)
	 0.11,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 0.00		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2014_ITRF2000 : public _itrf2014_to_itrf2000_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1997
template <class T, class U>
struct _itrf2014_to_itrf1997_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1997_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1997_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1997_<T, U>::transformationParameters[14] =
{
	  7.4,		// x translation (millimetres)
	 -0.5,		// y translation (millimetres)
	-62.8,		// z translation (millimetres)
	 3.80,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1997 : public _itrf2014_to_itrf1997_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1996
template <class T, class U>
struct _itrf2014_to_itrf1996_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1996_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1996_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1996_<T, U>::transformationParameters[14] =
{
	  7.4,		// x translation (millimetres)
	 -0.5,		// y translation (millimetres)
	-62.8,		// z translation (millimetres)
	 3.80,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1996 : public _itrf2014_to_itrf1996_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1994
template <class T, class U>
struct _itrf2014_to_itrf1994_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1994_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1994_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1994_<T, U>::transformationParameters[14] =
{
	  7.4,		// x translation (millimetres)
	 -0.5,		// y translation (millimetres)
	-62.8,		// z translation (millimetres)
	 3.80,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1994 : public _itrf2014_to_itrf1994_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1993
template <class T, class U>
struct _itrf2014_to_itrf1993_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1993_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1993_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1993_<T, U>::transformationParameters[14] =
{
	-50.4,		// x translation (millimetres)
	  3.3,		// y translation (millimetres)
	-60.2,		// z translation (millimetres)
	 4.29,		// scale (ppb)
	 2.81,		// x rotation (milli-arc-seconds) - REVERSED
	 3.38,		// y rotation (milli-arc-seconds) - REVERSED
	-0.40,		// z rotation (milli-arc-seconds) - REVERSED

	 -2.8,		// x translation rate (millimetres p/yr)
	 -0.1,		// y translation rate (millimetres p/yr)
	 -2.5,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.11,		// x rotation rate (milli-arc-seconds p/yr) - REVERSED
	 0.19,		// y rotation rate (milli-arc-seconds p/yr) - REVERSED
	-0.07		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1993 : public _itrf2014_to_itrf1993_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1992
template <class T, class U>
struct _itrf2014_to_itrf1992_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1992_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1992_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1992_<T, U>::transformationParameters[14] =
{
	 15.4,		// x translation (millimetres)
	  1.5,		// y translation (millimetres)
	-70.8,		// z translation (millimetres)
	 3.09,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1992	 : public _itrf2014_to_itrf1992_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1991
template <class T, class U>
struct _itrf2014_to_itrf1991_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1991_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1991_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1991_<T, U>::transformationParameters[14] =
{
	 27.4,		// x translation (millimetres)
	 15.5,		// y translation (millimetres)
	-76.8,		// z translation (millimetres)
	 4.49,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1991 : public _itrf2014_to_itrf1991_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1990
template <class T, class U>
struct _itrf2014_to_itrf1990_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1990_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1990_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1990_<T, U>::transformationParameters[14] =
{
	 25.4,		// x translation (millimetres)
	 11.5,		// y translation (millimetres)
	-92.8,		// z translation (millimetres)
	 4.79,		// scale (ppb)
	 0.00,		// x rotation (milli-arc-seconds)
	 0.00,		// y rotation (milli-arc-seconds)
	-0.26,		// z rotation (milli-arc-seconds) - REVERSED

	  0.1,		// x translation rate (millimetres p/yr)
	 -0.5,		// y translation rate (millimetres p/yr)
	 -3.3,		// z translation rate (millimetres p/yr)
	 0.12,		// scale rate (ppb p/yr)
	 0.00,		// x rotation rate (milli-arc-seconds p/yr)
	 0.00,		// y rotation rate (milli-arc-seconds p/yr)
	-0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1990 : public _itrf2014_to_itrf1990_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1989
template <class T, class U>
struct _itrf2014_to_itrf1989_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1989_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1989_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1989_<T, U>::transformationParameters[14] =
{
	  30.4,		// x translation (millimetres)
	  35.5,		// y translation (millimetres)
	-130.8,		// z translation (millimetres)
	  8.19,		// scale (ppb)
	  0.00,		// x rotation (milli-arc-seconds)
	  0.00,		// y rotation (milli-arc-seconds)
	 -0.26,		// z rotation (milli-arc-seconds) - REVERSED

	   0.1,		// x translation rate (millimetres p/yr)
	  -0.5,		// y translation rate (millimetres p/yr)
	  -3.3,		// z translation rate (millimetres p/yr)
	  0.12,		// scale rate (ppb p/yr)
	  0.00,		// x rotation rate (milli-arc-seconds p/yr)
	  0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 -0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1989 : public _itrf2014_to_itrf1989_<T, U>
{
public:
};

// ITRF 2014 -> ITRF 1988
template <class T, class U>
struct _itrf2014_to_itrf1988_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_itrf1988_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_itrf1988_<T, U>::reference_epoch = 2010.0;

template <class T, class U>
const T _itrf2014_to_itrf1988_<T, U>::transformationParameters[14] =
{
	  25.4,		// x translation (millimetres)
	  -0.5,		// y translation (millimetres)
	-154.8,		// z translation (millimetres)
	 11.29,		// scale (ppb)
	 -0.10,		// x rotation (milli-arc-seconds) - REVERSED
	  0.00,		// y rotation (milli-arc-seconds)
	 -0.26,		// z rotation (milli-arc-seconds) - REVERSED

	   0.1,		// x translation rate (millimetres p/yr)
	  -0.5,		// y translation rate (millimetres p/yr)
	  -3.3,		// z translation rate (millimetres p/yr)
	  0.12,		// scale rate (ppb p/yr)
	  0.00,		// x rotation rate (milli-arc-seconds p/yr)
	  0.00,		// y rotation rate (milli-arc-seconds p/yr)
	 -0.02		// z rotation rate (milli-arc-seconds p/yr) - REVERSED
};

template <class T, class U>
class ITRF2014_ITRF1988 : public _itrf2014_to_itrf1988_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRF2014 <-> GDA2020
// PCG In-confidence PowerPoint (Perth PGC FTF March 2016)
// file://C:\Data\GEODESY\ICSM\DATUM\2016-03-09-pcg-perth\GDA94toGDA2020Transformation.pptx
//
// Replaced by ICSM Release note (March 2017)
// 'file://C:\Data\GEODESY\ICSM\DATUM\GDA2020 media\GDA2020 Release Note.docx'

// ITRF2014 -> GDA2020
template <class T, class U>
struct _itrf2014_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_gda2020_<T, U>::reference_frame = ITRF2014_i;

template <class T, class U>
const T _itrf2014_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf2014_to_gda2020_<T, U>::transformationParameters[14] =
{
	    0.0,	// x translation (millimetres)
	    0.0,	// y translation (millimetres)
	    0.0,	// z translation (millimetres)
	    0.0,	// scale (ppb)
	    0.0,	// x rotation (milli-arc-seconds)
	    0.0,	// y rotation (milli-arc-seconds)
	    0.0,	// z rotation (milli-arc-seconds)

	    0.0,	// x translation rate (millimetres p/yr)
	    0.0,	// y translation rate (millimetres p/yr)
	    0.0,	// z translation rate (millimetres p/yr)
	    0.0,	// scale rate (ppb p/yr)
	1.50379,	// x rotation rate (milli-arc-seconds p/yr)
	1.18346,	// y rotation rate (milli-arc-seconds p/yr)
	1.20716		// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2014_GDA2020 : public _itrf2014_to_gda2020_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////
// PLATE MOTION MODELS

// AUS
template <class T, class U>
class AUS_PLATE_MOTION_MODEL : public _itrf2014_to_gda2020_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// ITRFxxxx <-> GDA2020
// Dawson Fraser
// file://C:\Data\GEODESY\ICSM\DATUM\gda2020-itrf-transformation-parameters.xlsx

// ITRF1996 -> GDA2020
template <class T, class U>
struct _itrf1996_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf1996_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _itrf1996_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf1996_to_gda2020_<T, U>::transformationParameters[14] =
{
	-480.710,	// x translation (millimetres)
	  75.160,	// y translation (millimetres)
	 574.710,	// z translation (millimetres)
	  6.9950,	// scale (ppb)
	 10.2995,	// x rotation (milli-arc-seconds)
	 21.7458,	// y rotation (milli-arc-seconds)
	  9.8292,	// z rotation (milli-arc-seconds)

	 -21.800,	// x translation rate (millimetres p/yr)
	   4.710,	// y translation rate (millimetres p/yr)
	  26.270,	// z translation rate (millimetres p/yr)
	  0.3880,	// scale rate (ppb p/yr)
	  2.0203,	// x rotation rate (milli-arc-seconds p/yr)
	  2.1735,	// y rotation rate (milli-arc-seconds p/yr)
	  1.6290	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF1996_GDA2020 : public _itrf1996_to_gda2020_<T, U>
{
public:
};

// ITRF1997 -> GDA2020
template <class T, class U>
struct _itrf1997_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf1997_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _itrf1997_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf1997_to_gda2020_<T, U>::transformationParameters[14] =
{
	-176.680,	// x translation (millimetres)
	 -29.130,	// y translation (millimetres)
	 226.990,	// z translation (millimetres)
	 -3.1170,	// scale (ppb)
	  1.3427,	// x rotation (milli-arc-seconds)
	  6.1880,	// y rotation (milli-arc-seconds)
	  3.9809,	// z rotation (milli-arc-seconds)

	  -8.600,	// x translation rate (millimetres p/yr)
	   0.360,	// y translation rate (millimetres p/yr)
	  11.250,	// z translation rate (millimetres p/yr)
	  0.0070,	// scale rate (ppb p/yr)
	  1.6394,	// x rotation rate (milli-arc-seconds p/yr)
	  1.5198,	// y rotation rate (milli-arc-seconds p/yr)
	  1.3801	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF1997_GDA2020 : public _itrf1997_to_gda2020_<T, U>
{
public:
};

// ITRF 2000 -> GDA2020
template <class T, class U>
struct _itrf2000_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _itrf2000_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf2000_to_gda2020_<T, U>::transformationParameters[14] =
{
    -105.520,	// x translation (millimetres)
      51.580,	// y translation (millimetres)
     231.680,	// z translation (millimetres)
      3.5500,	// scale (ppb)
      4.2175,	// x rotation (milli-arc-seconds)
      6.3941,	// y rotation (milli-arc-seconds)
      0.8617,	// z rotation (milli-arc-seconds)

      -4.660,   // x translation rate (millimetres p/yr)
       3.550,	// y translation rate (millimetres p/yr)
      11.240,	// z translation rate (millimetres p/yr)
      0.2490,	// scale rate (ppb p/yr)
      1.7454,	// x rotation rate (milli-arc-seconds p/yr)
      1.4868,	// y rotation rate (milli-arc-seconds p/yr)
      1.2240	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2000_GDA2020 : public _itrf2000_to_gda2020_<T, U>
{
public:
};

// ITRF 2005 -> GDA2020
template <class T, class U>
struct _itrf2005_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2005_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _itrf2005_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf2005_to_gda2020_<T, U>::transformationParameters[14] =
{
      40.320,	// x translation (millimetres)
     -33.850,	// y translation (millimetres)
     -16.720,	// z translation (millimetres)
      4.2860,	// scale (ppb)
     -1.2893,	// x rotation (milli-arc-seconds)
     -0.8492,	// y rotation (milli-arc-seconds)
     -0.3342,	// z rotation (milli-arc-seconds)

       2.250,	// x translation rate (millimetres p/yr)
      -0.620,	// y translation rate (millimetres p/yr)
      -0.560,	// z translation rate (millimetres p/yr)
      0.2940,	// scale rate (ppb p/yr)
      1.4707,	// x rotation rate (milli-arc-seconds p/yr)
      1.1443,	// y rotation rate (milli-arc-seconds p/yr)
      1.1701	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2005_GDA2020 : public _itrf2005_to_gda2020_<T, U>
{
public:
};

// ITRF 2008 -> GDA2020
template <class T, class U>
struct _itrf2008_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _itrf2008_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf2008_to_gda2020_<T, U>::transformationParameters[14] =
{
      13.790,	// x translation (millimetres)
       4.550,	// y translation (millimetres)
      15.220,	// z translation (millimetres)
      2.5500,	// scale (ppb)
      0.2808,	// x rotation (milli-arc-seconds)
      0.2677,	// y rotation (milli-arc-seconds)
     -0.4638,	// z rotation (milli-arc-seconds)

       1.420,   // x translation rate (millimetres p/yr)
       1.340,	// y translation rate (millimetres p/yr)
       0.900,	// z translation rate (millimetres p/yr)
      0.1090,	// scale rate (ppb p/yr)
      1.5461,	// x rotation rate (milli-arc-seconds p/yr)
      1.1820,	// y rotation rate (milli-arc-seconds p/yr)
      1.1551	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2008_GDA2020 : public _itrf2008_to_gda2020_<T, U>
{
public:
};

////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// GDA94 <-> GDA2020
// PCG In-confidence PowerPoint (Perth PGC FTF March 2016)
// file://C:\Data\GEODESY\ICSM\DATUM\2016-03-09-pcg-perth\GDA94toGDA2020Transformation.pptx
//
// Replaced by ICSM Release note (March 2017)
// 'file://C:\onedrive\GEODESY\ICSM\DATUM\GDA2020%20media\GDA2020%20Release%20Note.docx'

// GDA94 -> GDA2020
template <class T, class U>
struct _gda94_to_gda2020_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _gda94_to_gda2020_<T, U>::reference_frame = GDA2020_i;

template <class T, class U>
const T _gda94_to_gda2020_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _gda94_to_gda2020_<T, U>::transformationParameters[14] =
{
	   61.55,	// x translation (millimetres)
	  -10.87,	// y translation (millimetres)
	  -40.19,	// z translation (millimetres)
	  -9.994,	// scale (ppb)
	-39.4924,	// x rotation (milli-arc-seconds)
	-32.7221,	// y rotation (milli-arc-seconds)
	-32.8979,	// z rotation (milli-arc-seconds)

		 0.0,	// x translation rate (millimetres p/yr)
		 0.0,	// y translation rate (millimetres p/yr)
		 0.0,	// z translation rate (millimetres p/yr)
		 0.0,	// scale rate (ppb p/yr)
		 0.0,	// x rotation rate (milli-arc-seconds p/yr)
		 0.0,	// y rotation rate (milli-arc-seconds p/yr)
		 0.0	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class GDA94_GDA2020 : public _gda94_to_gda2020_<T, U>
{
public:
};

// ITRF2014 at epoch 2020.0 -> GDA94.
// Effectively, these are the same as GDA2020 -> GDA94 parameters, plus
// the rotation rates from the Australian Plate Motion Model.
template <class T, class U>
struct _itrf2014_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2014_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf2014_to_gda94_<T, U>::reference_epoch = 2020.0;

template <class T, class U>
const T _itrf2014_to_gda94_<T, U>::transformationParameters[14] =
{
	  -61.55,	// x translation (millimetres)
	   10.87,	// y translation (millimetres)
	   40.19,	// z translation (millimetres)
	   9.994,	// scale (ppb)
	 39.4924,	// x rotation (milli-arc-seconds)
	 32.7221,	// y rotation (milli-arc-seconds)
	 32.8979,	// z rotation (milli-arc-seconds)

		 0.0,	// x translation rate (millimetres p/yr)
		 0.0,	// y translation rate (millimetres p/yr)
		 0.0,	// z translation rate (millimetres p/yr)
		 0.0,	// scale rate (ppb p/yr)
	 1.50379,	// x rotation rate (milli-arc-seconds p/yr)
	 1.18346,	// y rotation rate (milli-arc-seconds p/yr)
	 1.20716	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2014_GDA94 : public _itrf2014_to_gda94_<T, U>
{
public:
};
////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////
// GDA94 <-> ITRF... (Dawson and Woods)
// http://www.ga.gov.au/webtemp/image_cache/GA19050.pdf

// ITRF 2008 -> GDA94
template <class T, class U>
struct _itrf2008_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2008_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf2008_to_gda94_<T, U>::reference_epoch = 1994.0;

template <class T, class U>
const T _itrf2008_to_gda94_<T, U>::transformationParameters[14] =
{
	  -84.68,	// x translation (millimetres)
	  -19.42,	// y translation (millimetres)
	   32.01,	// z translation (millimetres)
	  9.7100,	// scale (ppb)
	 -0.4254,	// x rotation (milli-arc-seconds)
	  2.2578,	// y rotation (milli-arc-seconds)
	  2.4015,	// z rotation (milli-arc-seconds)

	   1.420,	// x translation rate (millimetres p/yr)
	   1.340,	// y translation rate (millimetres p/yr)
	   0.900,	// z translation rate (millimetres p/yr)
	  0.1090,	// scale rate (ppb p/yr)
	  1.5461,	// x rotation rate (milli-arc-seconds p/yr)
	  1.1820,	// y rotation rate (milli-arc-seconds p/yr)
	  1.1551 	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2008_GDA94 : public _itrf2008_to_gda94_<T, U>
{
public:
};

// ITRF 2005 -> GDA94
template <class T, class U>
struct _itrf2005_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2005_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf2005_to_gda94_<T, U>::reference_epoch = 1994.0;

template <class T, class U>
const T _itrf2005_to_gda94_<T, U>::transformationParameters[14] =
{
	  -79.73,	// x translation (millimetres)
	   -6.86,	// y translation (millimetres)
	   38.03,	// z translation (millimetres)
	  6.6360,	// scale (ppb)
	 -0.0351,	// x rotation (milli-arc-seconds)
	  2.1211,	// y rotation (milli-arc-seconds)
	  2.1411,	// z rotation (milli-arc-seconds)

	    2.25,	// x translation rate (millimetres p/yr)
	   -0.62,	// y translation rate (millimetres p/yr)
	   -0.56,	// z translation rate (millimetres p/yr)
	  0.2940,	// scale rate (ppb p/yr)
	  1.4707,	// x rotation rate (milli-arc-seconds p/yr)
	  1.1443,	// y rotation rate (milli-arc-seconds p/yr)
	  1.1701 	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2005_GDA94 : public _itrf2005_to_gda94_<T, U>
{
public:
};

// ITRF 2000 -> GDA94
template <class T, class U>
struct _itrf2000_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf2000_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf2000_to_gda94_<T, U>::reference_epoch = 1994.0;

template <class T, class U>
const T _itrf2000_to_gda94_<T, U>::transformationParameters[14] =
{
	  -45.91,	// x translation (millimetres)
	  -29.85,	// y translation (millimetres)
	  -20.37,	// z translation (millimetres)
	  7.0700,	// scale (ppb)
	 -1.6705,	// x rotation (milli-arc-seconds)
	  0.4594,	// y rotation (milli-arc-seconds)
	  1.9356,	// z rotation (milli-arc-seconds)

	   -4.66,	// x translation rate (millimetres p/yr)
	    3.55,	// y translation rate (millimetres p/yr)
	   11.24,	// z translation rate (millimetres p/yr)
	  0.2490,	// scale rate (ppb p/yr)
	  1.7454,	// x rotation rate (milli-arc-seconds p/yr)
	  1.4868,	// y rotation rate (milli-arc-seconds p/yr)
	  1.2240 	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF2000_GDA94 : public _itrf2000_to_gda94_<T, U>
{
public:
};

// ITRF 1997 -> GDA94
template <class T, class U>
struct _itrf1997_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf1997_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf1997_to_gda94_<T, U>::reference_epoch = 1994.0;

template <class T, class U>
const T _itrf1997_to_gda94_<T, U>::transformationParameters[14] =
{
	  -14.63,	// x translation (millimetres)
	  -27.62,	// y translation (millimetres)
	  -25.32,	// z translation (millimetres)
	  6.6950,	// scale (ppb)
	 -1.7893,	// x rotation (milli-arc-seconds)
	 -0.6047,	// y rotation (milli-arc-seconds)
	  0.9962,	// z rotation (milli-arc-seconds)

	   -8.60,	// x translation rate (millimetres p/yr)
	    0.36,	// y translation rate (millimetres p/yr)
	   11.25,	// z translation rate (millimetres p/yr)
	  0.0070,	// scale rate (ppb p/yr)
	  1.6394,	// x rotation rate (milli-arc-seconds p/yr)
	  1.5198,	// y rotation rate (milli-arc-seconds p/yr)
	  1.3801	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF1997_GDA94 : public _itrf1997_to_gda94_<T, U>
{
public:
};

// ITRF 1996 -> GDA94
template <class T, class U>
struct _itrf1996_to_gda94_
{
	static const U reference_frame;
	static const T reference_epoch;
	static const T transformationParameters[14];
};

template <class T, class U>
const U _itrf1996_to_gda94_<T, U>::reference_frame = GDA94_i;

template <class T, class U>
const T _itrf1996_to_gda94_<T, U>::reference_epoch = 1994.0;

template <class T, class U>
const T _itrf1996_to_gda94_<T, U>::transformationParameters[14] =
{
	   24.54,	// x translation (millimetres)
	  -36.43,	// y translation (millimetres)
	  -68.12,	// z translation (millimetres)
	  6.9010,	// scale (ppb)
	 -2.7359,	// x rotation (milli-arc-seconds)
	 -2.0431,	// y rotation (milli-arc-seconds)
	  0.3731,	// z rotation (milli-arc-seconds)

	  -21.80,	// x translation rate (millimetres p/yr)
	    4.71,	// y translation rate (millimetres p/yr)
	   26.27,	// z translation rate (millimetres p/yr)
	  0.3880,	// scale rate (ppb p/yr)
	  2.0203,	// x rotation rate (milli-arc-seconds p/yr)
	  2.1735,	// y rotation rate (milli-arc-seconds p/yr)
	  1.6290 	// z rotation rate (milli-arc-seconds p/yr)
};

template <class T, class U>
class ITRF1996_GDA94 : public _itrf1996_to_gda94_<T, U>
{
public:
};

}	// namespace datum_parameters
}	// namespace dynadjust

#endif /* DNATRANSFORM_PARAM_H_ */


#include "ext/lda/lda.h"
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

ldaModel::ldaModel(const Corpus& training, const Corpus& test):
tfile(test),
dfile(training)
{
    if (test.size() > 0)
        testing_type = SEPARATE_TEST;
    else
        testing_type = SELF_TEST;

    M = 0;
    V = 0;
    K = 100;

    alpha = 50.0 / K;
    beta = 0.1;

    z = NULL;
    n_wk.clear();
    n_k.clear();

    p = NULL;
    nd_m = NULL;
    rev_mapper = NULL;

    n_iters = 1000;
    n_save = 200;
    n_topWords = 0;

    test_n_iters = 10;
    test_M = 0;
    test_z = NULL;
    test_n_wk = NULL;
    test_n_mk = NULL;
    test_n_k = NULL;

    time_ellapsed.reserve(50);
    likelihood.reserve(50);

    ddir = "./";
    mdir = "./";
}

ldaModel::~ldaModel()
{

    if (z)
    {
        for (int m = 0; m < M; m++)
        {
            if (z[m])
            {
                delete[] z[m];
            }
        }
        delete[] z;
    }

    n_wk.clear();
    n_k.clear();

    if (p)		delete[] p;
    if (nd_m)	delete[] nd_m;
    if (rev_mapper)	delete[] rev_mapper;

    if (test_z)
    {
        for (int m = 0; m < test_M; m++)
        {
            if (test_z[m])
            {
                delete[] test_z[m];
            }
        }
        delete[] test_z;
        test_z = nullptr;
    }

    if (test_n_wk)
    {
        for (int w = 0; w < V; w++)
        {
            if (test_n_wk[w])
            {
                delete[] test_n_wk[w];
            }
        }
        delete[] test_n_wk;
        test_n_wk = nullptr;
    }

    if (test_n_mk)
    {
        for (int m = 0; m < test_M; m++)
        {
            if (test_n_mk[m])
            {
                delete[] test_n_mk[m];
            }
        }
        delete[] test_n_mk;
        test_n_mk = nullptr;
    }

    if (test_n_k)	delete[] test_n_k;

}

ldaModel* ldaModel::init(variables_map vm)
{
    double _alpha = -1.0;

    mdir = vm["lda-model"].as<string>();
    alpha = vm["lda-alpha"].as<cnn::real>();
    beta = vm["lda-beta"].as<cnn::real>();
    K = vm["lda-num-topics"].as<int>();
    n_iters = vm["lda-num-iterations"].as<int>();
    n_save = vm["lda-output-state-interval"].as<int>();
    n_topWords = vm["lda-num-top-words"].as<int>();

    //Check specific parameter
    if (testing_type == SEPARATE_TEST)
    {
        if (tfile.size() == 0)
        {
            throw("Error: test corpus is empty");
        }
    }

    std::cout << "data dir = " << ddir << std::endl;
    std::cout << "ldaModel dir = " << mdir << std::endl;
    std::cout << "n_iters = " << n_iters << std::endl;
    std::cout << "alpha = " << alpha << std::endl;
    std::cout << "beta = " << beta << std::endl;
    std::cout << "K = " << K << std::endl;

    return this;
}

int ldaModel::train()
{
    std::chrono::high_resolution_clock::time_point ts, tn;
    std::cout << "Sampling " << n_iters << " iterations!" << std::endl;

    init_train();

    for (int iter = 1; iter <= n_iters; ++iter)
    {
        std::cout << "Iteration " << iter << " ..." << std::endl;
        ts = std::chrono::high_resolution_clock::now();

        // for each document
        for (int m = 0; m < M; ++m)
            sampling(m);

        tn = std::chrono::high_resolution_clock::now();
        time_ellapsed.push_back(std::chrono::duration_cast<std::chrono::milliseconds>(tn - ts).count());

#if COMP_LLH
        test(sd);
#endif

        if (n_save > 0)
        {
            if (iter % n_save == 0)
            {
                // saving the ldaModel
                std::cout << "Saving the ldaModel at iteration " << iter << "..." << std::endl;
                save_ldaModel(iter);
            }
        }
    }

    std::cout << "Gibbs sampling completed!" << std::endl;
    std::cout << "Saving the final ldaModel!" << std::endl;
    save_ldaModel(-1);

    return 0;
}

int ldaModel::test(Dict& sd)
{
    init_test();

    if (testing_type == SELF_TEST)
    {
        // just do MAP estimates
        likelihood.push_back(llhw());
        std::cout << "Likelihood on training documents: " << likelihood.back() << " at time " << time_ellapsed.back() << std::endl;
    }
    else if (testing_type == SEPARATE_TEST)
    {
        for (int iter = 1; iter <= test_n_iters; ++iter)
        {
            // for each doc
            for (int m = 0; m < test_M; m++)
                vanilla_sampling(m);
        }
        likelihood.push_back(newllhw());
        std::cout << "Likelihood on held out documents: " << likelihood.back() << std::endl;

        /// compute the topic of each test sentence
        for (int m = 0; m < test_M; m++)
        {
            int this_topic = topic_of(m);
            string ostr = "";
            for (int n = 0; n < testdata[m].size(); n++)
            {
                int w = testdata[m][n];
                string wrd = sd.Convert(w);
                ostr = ostr + wrd + " ";
            }
            cout << ostr << " ||| topic " << this_topic;
            if (testresponses.size() > 0)
            {
                string ostr = "";
                for (auto & w : testresponses[m])
                {
                    string wrd = sd.Convert(w);
                    ostr = ostr + wrd + " ";
                }
                cout << " ||| " << ostr << endl;
            }
        }
    }
    return 0;
}

int ldaModel::test(Dict& sd, const SentencePair& obs)
{
    init_test();

    memset(test_n_wk, 0, sizeof(int)* V * K);
    memset(test_n_mk, 0, sizeof(int)* K);
    memset(test_n_k, 0, sizeof(int)* K);
    for (int n = 0; n < obs.first.size(); n++)
    {
        int w = obs.first[n];
        test_z[0][n] = rand0n_uniform(K - 1);

        int topic = test_z[0][n];

        // number of instances of word i assigned to topic j
        test_n_wk[w][topic] += 1;
        // number of words in document i assigned to topic j
        test_n_mk[0][topic] += 1;
        // total number of words assigned to topic j
        test_n_k[topic] += 1;
    }

    for (int iter = 1; iter <= test_n_iters; ++iter)
    {
        // for each doc
        for (int m = 0; m < test_M; m++)
            vanilla_sampling(obs.first);
    }
    likelihood.push_back(newllhw());
    std::cout << "Likelihood on held out documents: " << likelihood.back() << std::endl;

    /// compute the topic of each test sentence
    for (int m = 0; m < test_M; m++)
    {
        int this_topic = topic_of(m);
        string ostr = "";
        for (int n = 0; n < testdata[m].size(); n++)
        {
            int w = testdata[m][n];
            string wrd = sd.Convert(w);
            ostr = ostr + wrd + " ";
        }
        cout << ostr << " ||| topic " << this_topic;
        if (testresponses.size() > 0)
        {
            string ostr = "";
            for (auto & w : testresponses[m])
            {
                string wrd = sd.Convert(w);
                ostr = ostr + wrd + " ";
            }
            cout << " ||| " << ostr << endl;
        }
    }
    return 0;
}

int ldaModel::read_data(const Corpus & training, const Dict& sd, const Corpus& testing)
{
    flatten_corpus(training, trngdata, trngresponses);
    V = sd.size();
    M = trngdata.size();

    flatten_corpus(testing, testdata, testresponses);
    test_M = testdata.size();

    return training.size();
}

int ldaModel::init_train()
{
    Vbeta = V * beta;

    // allocate heap memory for ldaModel variables
    n_wk.resize(V);
    for (int w = 0; w < V; w++)
    {
        n_wk[w] = vector<int>(K, 0);
    }

    n_mks.resize(M);

    n_k.resize(K, 0);

    // random consistent assignment for ldaModel variables
    z = new int*[M];
    for (int m = 0; m < trngdata.size(); m++)
    {
        int N = trngdata[m].size();
        std::map<int, int > map_nd_m;
        z[m] = new int[N];

        // initialize for z
        for (int n = 0; n < N; n++)
        {
            int topic = rand0n_uniform(K - 1);
            z[m][n] = topic;
            int w = trngdata[m][n];

            // number of instances of word i assigned to topic j
            n_wk[w][topic] += 1;
            // number of words in document i assigned to topic j
            map_nd_m[topic] += 1;
            // total number of words assigned to topic j
            n_k[topic] += 1;
        }
        // transfer to sparse representation
        for (auto myc : map_nd_m)
            n_mks[m].push_back(myc);
    }

    time_ellapsed.reserve(n_iters);
    likelihood.reserve(n_iters);

    // allocate heap memory for temporary variables
    p = new double[K];
    nd_m = new int[K];
    rev_mapper = new int[K];
    for (int k = 0; k < K; ++k)
    {
        nd_m[k] = 0;
        rev_mapper[k] = -1;
    }

    return 0;
}

int ldaModel::init_test()
{
    // initialise variables for testing
    Vbeta = V * beta;
    if (test_n_wk == nullptr)
    {
        test_n_wk = new int*[V];
        for (int w = 0; w < V; w++)
        {
            test_n_wk[w] = new int[K];
            for (int k = 0; k < K; k++)
            {
                test_n_wk[w][k] = 0;
            }
        }
    }

    if (test_n_mk == nullptr)
    {
        test_n_mk = new int*[test_M];
        for (int m = 0; m < test_M; m++)
        {
            test_n_mk[m] = new int[K];
            for (int k = 0; k < K; k++)
            {
                test_n_mk[m][k] = 0;
            }
        }
    }

    if (test_n_k == nullptr)
    {
        test_n_k = new int[K];
        for (int k = 0; k < K; k++)
        {
            test_n_k[k] = 0;
        }
    }

    if (test_z == nullptr)
    {
        test_z = new int*[test_M];
        for (int m = 0; m < testdata.size(); m++)
        {
            int N = testdata[m].size();
            test_z[m] = new int[N];

            // assign values for n_wk, n_mk, n_k
            for (int n = 0; n < N; n++)
            {
                int w = testdata[m][n];
                int topic = rand0n_uniform(K - 1);
                test_z[m][n] = topic;

                // number of instances of word i assigned to topic j
                test_n_wk[w][topic] += 1;
                // number of words in document i assigned to topic j
                test_n_mk[m][topic] += 1;
                // total number of words assigned to topic j
                test_n_k[topic] += 1;
            }
        }
    }

    // allocate heap memory for temporary variables
    if (p == nullptr)
        p = new double[K];
    if (nd_m == nullptr)
        nd_m = new int[K];
    if (rev_mapper == nullptr)
        rev_mapper = new int[K];
    for (int k = 0; k < K; ++k)
    {
        nd_m[k] = 0;
        rev_mapper[k] = -1;
    }

    return 0;
}

int ldaModel::sampling(int m)
{
    int kc = 0;
    for (const auto& k : n_mks[m])
    {
        nd_m[k.first] = k.second;
        rev_mapper[k.first] = kc++;
    }
    for (int n = 0; n < trngdata[m].size(); ++n)
    {
        int w = trngdata[m][n];

        // remove z_ij from the count variables
        int topic = z[m][n]; int old_topic = topic;
        remove_from_topic(w, m, topic);

        // do multinomial sampling via cumulative method
        double temp = 0;
        for (int k = 0; k < K; k++)
        {
            temp += (nd_m[k] + alpha) * (n_wk[w][k]  + beta) / (n_k[k] + Vbeta);
            p[k] = temp;
        }

        // scaled sample because of unnormalized p[]
        double u = rand01() * temp;

        // Do a binary search instead!
        topic = std::lower_bound(p, p + K, u) - p;

        // add newly estimated z_i to count variables
        add_to_topic(w, m, topic, old_topic);
        nd_m[topic] += 1;
        z[m][n] = topic;
    }
    for (const auto& k : n_mks[m])
    {
        nd_m[k.first] = 0;
        rev_mapper[k.first] = -1;
    }
    return 0;
}

int ldaModel::vanilla_sampling(int m)
{
    for (int n = 0; n < testdata[m].size(); n++)
    {
        int w = testdata[m][n];

        // remove z_i from the count variables
        int topic = test_z[m][n];
        test_n_wk[w][topic] -= 1;
        test_n_mk[m][topic] -= 1;
        test_n_k[topic] -= 1;

        double psum = 0;
        // do multinomial sampling via cumulative method
        for (int k = 0; k < K; k++)
        {
            psum += (test_n_mk[m][k] + alpha) * (n_wk[w][k] + test_n_wk[w][k] + beta) / (n_k[k]+ test_n_k[k] + Vbeta);
            p[k] = psum;
        }

        // scaled sample because of unnormalized p[]
        double u = rand01() * psum;
        topic = std::lower_bound(p, p + K, u) - p;

        // add newly estimated z_i to count variables
        test_n_wk[w][topic] += 1;
        test_n_mk[m][topic] += 1;
        test_n_k[topic] += 1;
        test_z[m][n] = topic;
    }

    return 0;
}

int ldaModel::vanilla_sampling(const Sentence& obs)
{
    int n = 0;
    for (auto & w : obs)
    {
        // remove z_i from the count variables
        int topic = test_z[0][n];
        test_n_wk[w][topic] -= 1;
        test_n_mk[0][topic] -= 1;
        test_n_k[topic] -= 1;

        double psum = 0;
        // do multinomial sampling via cumulative method
        for (int k = 0; k < K; k++)
        {
            psum += (test_n_mk[0][k] + alpha) * (n_wk[w][k] + test_n_wk[w][k] + beta) / (n_k[k] + test_n_k[k] + Vbeta);
            p[k] = psum;
        }

        // scaled sample because of unnormalized p[]
        double u = rand01() * psum;
        topic = std::lower_bound(p, p + K, u) - p;

        // add newly estimated z_i to count variables
        test_n_wk[w][topic] += 1;
        test_n_mk[0][topic] += 1;
        test_n_k[topic] += 1;
        test_z[0][n] = topic;

        n++;
    }

    return 0;
}

/// get the majority topic of document m
int ldaModel::topic_of(int m)
{
    vanilla_sampling(m);
    std::vector<int> v(K,0);

    for (int n = 0; n < testdata[m].size(); n++)
    {
        int topic = test_z[m][n];
        v[topic] ++;
    }

    std::vector<int>::iterator idx = std::max_element(v.begin(), v.end());
    int topic = std::distance(v.begin(), idx);
    return topic;
}

double ldaModel::newllhw() const
{
    double sum = 0;
    int num_tokens = 0;
    for (int m = 0; m < test_M; ++m)
    {
        double dsum = 0;
        num_tokens += testdata[m].size();
        for (int n = 0; n < testdata[m].size(); n++)
        {
            double wsum = 0;
            int w = testdata[m][n];
            for (int k = 0; k<K; k++)
            {
                wsum += (test_n_mk[m][k] + alpha) * (n_wk[w][k] + test_n_wk[w][k] + beta) / (n_k[k]+ test_n_k[k] + Vbeta);
            }
            dsum += log(wsum);
        }
        sum += dsum - testdata[m].size()*log(testdata[m].size() + K * alpha);
    }
    return sum / num_tokens;
}

double ldaModel::llhw() const
{
    double sum = 0;
    int num_tokens = 0;
    for (int m = 0; m < M; m++)
    {
        for (auto k = n_mks[m].begin(); k != n_mks[m].end(); ++k)
        {
            nd_m[k->first] = k->second;
        }

        double dsum = 0;
        num_tokens += trngdata[m].size();
        for (int n = 0; n < trngdata[m].size(); n++)
        {
            double wsum = 0;
            int w = trngdata[m][n];
            for (int k = 0; k<K; k++)
            {
                wsum += (nd_m[k] + alpha) * (n_wk[w][k] + beta) / (n_k[k]+ Vbeta);
            }
            wsum /= (trngdata[m].size() + K * alpha);
            dsum += log(wsum);
        }
        sum += dsum;
        for (auto k = n_mks[m].begin(); k != n_mks[m].end(); ++k)
        {
            nd_m[k->first] = 0;
        }
    }
    return sum / num_tokens;
}

int ldaModel::save_ldaModel(int iter) const
{
    std::string ldaModel_name = mdir + "-";
    if (iter >= 0)
    {
        std::ostringstream sstr1;
        sstr1 << std::setw(5) << std::setfill('0') << iter;
        ldaModel_name += sstr1.str();
    }
    else
    {
        ldaModel_name += "final";
    }

    if (save_ldaModel_time(ldaModel_name + ".time"))
    {
        return 1;
    }
    std::cout << "time done" << std::endl;
    if (save_ldaModel_llh(ldaModel_name + ".llh"))
    {
        return 1;
    }
    std::cout << "llh done" << std::endl;

    string fname = ldaModel_name + ".mdl";
    ofstream on(fname);
    boost::archive::text_oarchive oa(on);
    oa << *this;

    std::cout << "others done" << std::endl;
    if (n_topWords > 0)
    {
        //if (save_ldaModel_twords(mdir + ldaModel_name + ".twords"))
        //ff{
        //	return 1;
        //}
        //std::cout << "twords done" << std::endl;
    }
    //if (ldaModel_status == ldaModel_SELF_TEST)
    //{
    //	if (save_ldaModel_phi(mdir + ldaModel_name + ".phi"))
    //	{
    //		return 1;
    //	}
    //	std::cout << "phi done" << std::endl;
    //}
    return 0;
}

int ldaModel::load_ldaModel(int iter)
{
    std::string ldaModel_name = mdir + "-";
    if (iter >= 0)
    {
        std::ostringstream sstr1;
        sstr1 << std::setw(5) << std::setfill('0') << iter;
        ldaModel_name += sstr1.str();
    }
    else
    {
        ldaModel_name += "final";
    }

    string fname = ldaModel_name + ".mdl";
    ifstream in(fname);
    boost::archive::text_iarchive ia(in);
    ia >> *this;

    return 0;
}

int ldaModel::load_ldaModel(const string & filename)
{
    string fname = filename;
    ifstream in(fname);
    if (!in.is_open())
    {
        cerr << "cannot open " << fname << endl;
        throw("cannot open " + fname);
    }
    boost::archive::text_iarchive ia(in);
    ia >> *this;

    return 0;
}

int ldaModel::save_ldaModel_time(std::string filename) const
{
    std::ofstream fout(filename);
    if (!fout)
    {
        std::cout << "Error: Cannot open file to save: " << filename << std::endl;
        return 1;
    }

    for (unsigned r = 0; r < time_ellapsed.size(); ++r)
    {
        fout << time_ellapsed[r] << std::endl;
    }

    fout.close();

    return 0;
}

int ldaModel::save_ldaModel_llh(std::string filename) const
{
    std::ofstream fout(filename);
    if (!fout)
    {
        std::cout << "Error: Cannot open file to save: " << filename << std::endl;
        return 1;
    }

    for (unsigned r = 0; r < likelihood.size(); ++r)
    {
        fout << likelihood[r] << std::endl;
    }

    fout.close();

    return 0;
}

int ldaModel::save_ldaModel_topWords(std::string filename, Dict& sd) const
{
    std::ofstream fout(filename);
    if (!fout)
    {
        std::cout << "Error: Cannot open file to save: " << filename << std::endl;
        return 1;
    }

    int _n_topWords = n_topWords;
    if (_n_topWords > V)	_n_topWords = V;

    std::map<int, std::string>::const_iterator it;

    for (int k = 0; k < K; k++)
    {
        std::vector<std::pair<int, int> > words_probs(V);
        std::pair<int, int> word_prob;
        for (int w = 0; w < V; w++)
        {
            word_prob.first = w;
            word_prob.second = n_wk[w][k];
            words_probs[w] = word_prob;
        }

        // quick sort to sort word-topic probability
        std::sort(words_probs.begin(), words_probs.end());

        fout << "Topic " << k << "th:" << std::endl;
        for (int i = 0; i < _n_topWords; i++)
        {
            string it = sd.Convert(words_probs[i].first);
            fout << "\t" << it << "   " << words_probs[i].second << std::endl;
        }
    }

    fout.close();

    return 0;
}

int ldaModel::save_ldaModel_phi(std::string filename) const
{
    std::ofstream fout(filename);
    if (!fout)
    {
        std::cout << "Error: Cannot open file to save: " << filename << std::endl;
        return 1;
    }

    for (int k = 0; k < K; k++)
    {
        for (int w = 0; w < V; w++)
        {
            fout << (n_wk[w][k] + beta) / (n_k[k]+ Vbeta) << " ";
        }
        fout << std::endl;
    }

    fout.close();

    return 0;
}

int ldaModel::sanity() const
{
    long tott = 0;
    for (int m = 0; m < M; ++m)
    {
        int sumd = 0;
        for (const auto &t : n_mks[m])
            sumd += t.second;
        if (sumd == trngdata[m].size())
            tott += sumd;
        else
            std::cout << "Length mismatch at doc: " << m << std::endl;
    }
    std::cout << "Total number of training tokens: " << tott << std::endl;
    return 0;
}


/***
十：模板模板参数
英文名：Template Template Parameters。就是：让模板参数本身成为模板。
a)int：类型，简单类型/内部类型
b)vector,list：是C++标准库中的容器，类模板（类名），vector<int>或者list<double>就属于模板被实例化了的参数，称呼为类型（类类型）。
创建myclass的类模板，成员变量myc，是个容器，
复习一下：a）类型模板参数，b)非类型模板参数，c)模板模板参数。
十一：共用体模板（联合模板）
union
可以把联合理解成一种类类型（不要理解成类）。联合也支持模板化。
共用体模板的英文名：Union Templates。
***/

#include <iostream>
#include <vector>
#include <list>

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996)

namespace _nmsp1
{
	//T：类型模板参数，代表容器中元素类型。
	//Container：代表的不是一个类型（不能是一个类型模板参数），而是一个类模板（类名）
	//Container，不叫做类型模板参数，而叫做 模板 模板参数,表示这个模板参数本身又是一个模板。
	template <typename T,
		      //typename Container = std::vector
		      //template <class> class Container = std::vector          //这就是一个模板模板参数，写法比较固定，这里的名字叫Container，叫U也可以。
		      template <typename W> typename  Container = std::vector>  //也要理解这种写法,W可以省略，Container如果myclass类模板中不使用，也可以省略，从而出现了typename后面直接接=的写法。

	class myclass
	{
	public:
		Container<T> myc;

	public:
		void func();
		myclass()                        //构造函数
		{
			for (int i = 0; i < 10; ++i)
			{
				myc.push_back(i);        //这几行代码是否正确取决于实例化该类模板时所提供的模板参数类型。
			}
		}
	};

	//成员函数在类外定义
	template <typename T,
		      template <typename W> typename  Container>
	void myclass<T, Container>::func()
	{
		cout << "good!" << endl;
	}

	//模模参数的模板参数
	template <//.....
		      template <typename W, W* point> typename  Container>
	class myclass2
	{
		//W* m_p;  //错误，不可以在这里使用W（W叫做：模板模板参数Container的模板参数）
	};

	//省略Container
	template <
		     //......
		     template <typename W> typename  = std::vector>
	class myclass3
	{

	};
}

namespace _nmsp2
{
	template<typename T,typename U>
	union myuni
	{
		T carnum;    //轿车编号，4个字节。
		U cartype;   //轿车的类型，比如微型车，小型车，中，中大。1个字节。
		U cname[60]; //轿车名，60个字节
	};
}

int main()
{
	//测试_nmsp1
	_nmsp1::myclass<int, vector> myvectorobj;    //int是容器中的元素类型,vector是容器类型。
	_nmsp1::myclass<double, list> mylistobj;     //double是容器中的元素类型，list是容器类型。

	_nmsp1::myclass<double, list> mylistobj2;    //double是容器中的元素类型，list是容器类型
	mylistobj2.func();

	//测试_nmsp2
	_nmsp2::myuni<int, char> myu;
	myu.carnum = 156;
	cout << myu.carnum << endl;

	return 0;
}


#pragma once
//=====================================================================//
/*!	@file
	@brief	Arithmetic クラス（ヘッダー）@n
			※テキストの数式を展開して、実計算結果を得る。@n
			簡易演算式解析 @n
			Copyright 2017 Kunihito Hiramatsu
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <string>
#include <bitset>
#include <boost/unordered_map.hpp>

namespace utils {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	Arithmetic クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	struct arith {

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief	エラー・タイプ
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		struct error {
			enum type {
				none,				///< エラー無

				fatal,				///< エラー
				number_fatal,		///< 数字の変換に関するエラー
				zero_divide,		///< ０除算エラー
				binary_fatal,		///< ２進データの変換に関するエラー
				octal_fatal,		///< ８進データの変換に関するエラー
				hexdecimal_fatal,	///< １６進データの変換に関するエラー
				integer_fatal,		///< 整数データの変換に関するエラー
				float_fatal,		///< 浮動小数点データの変換に関するエラー
				double_fatal,		///< 倍精度浮動小数点データの変換に関するエラー
				symbol_fatal,		///< シンボルデータの変換に関するエラー

				limit_
			};
		};

	private:
		const char*		tx_;
		char			ch_;

		class value {
		public:
			std::bitset<error::limit_>	error_;

			bool	hex_value_;
			long	integer_;

			bool	point_;

			float	float_;
			float	f_scale_;

			bool	dbe_;
			double	double_;
			double	d_scale_;

			value() : hex_value_(false), integer_(0), point_(false),
					  float_(0.0f), f_scale_(1.0f),
					  dbe_(false), double_(0.0), d_scale_(1.0) { }

			void sum(char c);
			void neg();
			void inv();

			value& operator += (const value& v) {
				integer_ += v.integer_;
				float_ += v.float_;
				if(dbe_) double_ += v.double_;
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator -= (const value& v) {
				integer_ -= v.integer_;
				float_ -= v.float_;
				if(dbe_) double_ -= v.double_;
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator &= (const value& v) {
				integer_ &= v.integer_;
				error_.set(error::float_fatal);
				if(dbe_) error_.set(error::double_fatal);
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator ^= (const value& v) {
				integer_ ^= v.integer_;
				error_.set(error::float_fatal);
				if(dbe_) error_.set(error::double_fatal);
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator |= (const value& v) {
				integer_ |= v.integer_;
				error_.set(error::float_fatal);
				if(dbe_) error_.set(error::double_fatal);
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator *= (const value& v) {
				integer_ *= v.integer_;
				float_ *= v.float_;
				if(dbe_) double_ *= v.double_;
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator /= (const value& v) {
				if(v.integer_ == 0 || v.float_ == 0.0f || v.double_ == 0.0) {
					error_.set(error::zero_divide);
				} else {
					integer_ /= v.integer_;
					float_ /= v.float_;
					if(dbe_) double_ /= v.double_;
				}
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator %= (const value& v) {
				if(v.integer_ == 0 || v.float_ == 0.0f || v.double_ == 0.0) {
					error_.set(error::zero_divide);
				} else {
					integer_ %= v.integer_;
					error_.set(error::float_fatal);
					if(dbe_) error_.set(error::double_fatal);
				}
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator <<= (const value& v) {
				integer_ <<= v.integer_;
				error_.set(error::float_fatal);
				if(dbe_) error_.set(error::double_fatal);
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}

			value& operator >>= (const value& v) {
				integer_ >>= v.integer_;
				error_.set(error::float_fatal);
				if(dbe_) error_.set(error::double_fatal);
				if(v.point_) point_ = v.point_;
				error_ |= v.error_;
				return *this;
			}
		};

		value		value_;

		typedef boost::unordered_map<std::string, value>					symbol_map;
		typedef boost::unordered_map<std::string, value>::iterator			symbol_map_it;
		typedef boost::unordered_map<std::string, value>::const_iterator	symbol_map_cit;
		symbol_map	symbol_;

		value number_();
		value factor_();
		value term_();
		value expression_();
	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		*/
		//-----------------------------------------------------------------//
		arith() : tx_(0), ch_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	デストラクター
		*/
		//-----------------------------------------------------------------//
		~arith() { }

		//-----------------------------------------------------------------//
		/*!
			@brief	シンボルに値を登録
			@param[in]	name	シンボル名
			@param[in]	val		整数値
		*/
		//-----------------------------------------------------------------//
		void set_symbol(const std::string& name, long val);

		//-----------------------------------------------------------------//
		/*!
			@brief	シンボルに値を登録
			@param[in]	name	シンボル名
			@param[in]	val		浮動小数点
		*/
		//-----------------------------------------------------------------//
		void set_symbol(const std::string& name, float val);

		//-----------------------------------------------------------------//
		/*!
			@brief	シンボルに値を登録
			@param[in]	name	シンボル名
			@param[in]	val		倍精度浮動小数点
		*/
		//-----------------------------------------------------------------//
		void set_symbol(const std::string& name, double val);

		//-----------------------------------------------------------------//
		/*!
			@brief	解析を開始
			@param[in]	text	解析テキスト
			@return	文法にエラーがあった場合、「false」
		*/
		//-----------------------------------------------------------------//
		bool analize(const std::string& text);

		//-----------------------------------------------------------------//
		/*!
			@brief	エラーを受け取る
			@param[in]	type	エラーの種類
			@return エラーがある場合「true」
		*/
		//-----------------------------------------------------------------//
		bool get_error(error::type t) const { return value_.error_[t]; }

		//-----------------------------------------------------------------//
		/*!
			@brief	小数点表記か検査する
			@return 小数点表記なら「true」
		*/
		//-----------------------------------------------------------------//
		bool is_point() const { return value_.point_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	整数を取得
			@return 解析結果（整数）を受け取る
		*/
		//-----------------------------------------------------------------//
		int get_integer() const { return value_.integer_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	整数を取得
			@return 解析結果（32 ビット浮動小数点）を受け取る
		*/
		//-----------------------------------------------------------------//
		float get_float() const { return value_.float_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	整数を取得
			@return 解析結果（64 ビット浮動小数点）を受け取る
		*/
		//-----------------------------------------------------------------//
		double get_double() const { return value_.double_; }

	};

}