/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    This program is distributed in the hope that it will be useful,
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#include "main.hpp"
#include <hashclash/rng.hpp>
#include <hashclash/progress_display.hpp>
#include <hashclash/timer.hpp>
#include <boost/lexical_cast.hpp>

uint32 m[80];
uint32 m2[80];
uint32 Q[85];
uint32 Q2[85];
uint32 Qr30[85];
uint32 Q2r30[85];
uint32 dQ[85];
uint32 dT[85];
uint32 dR[85];
uint32 dF[80];

uint32 Qvaluemask[85];
uint32 Qvalue[85];
uint32 Qprev[85];
uint32 Qprev2[85];

void fill_tables(const sha1differentialpath& diffpath)
{
	for (int t = -4; t <= 80; ++t)
	{
		dF[t] = dQ[offset+t] = dT[offset+t] = dR[offset+t] = 0;
		Qvaluemask[offset+t] = Qvalue[offset+t] = 0;
		Qprev[offset+t] = Qprev2[offset+t] = 0;
	}

	// build tables
	for (int t = diffpath.tbegin(); t < diffpath.tend(); ++t)
	{
		dQ[offset+t] = diffpath[t].diff();
		Qprev[offset+t] = diffpath[t].prev() | diffpath[t].prevn();
		Qprev2[offset+t] = diffpath[t].prev2() | diffpath[t].prev2n();
		Qvaluemask[offset+t] = (~diffpath[t].set0()) | diffpath[t].set1()
							| diffpath[t].prev() | diffpath[t].prevn()
							| diffpath[t].prev2() | diffpath[t].prev2n();
		Qvalue[offset+t] = diffpath[t].set1() | diffpath[t].prevn() | diffpath[t].prev2n();
	}
	booleanfunction* F[4] = { &SHA1_F1_data, &SHA1_F2_data, &SHA1_F3_data, &SHA1_F4_data };
	for (int t = diffpath.tbegin()+4; t+1 < diffpath.tend(); ++t) {
		booleanfunction& FF = *F[t/20];
		uint32 dFt = 0;
		for (unsigned b = 0; b < 32; ++b)
			dFt += FF.outcome(diffpath[t-1][b],diffpath[t-2][(b+2)&31],diffpath[t-3][(b+2)&31])(0,b);
		dF[t] = dFt;
	}

	Q[0] = Qvalue[0]; Q2[0] = Q[0] + dQ[0];
	Q[1] = Qvalue[1]; Q2[1] = Q[1] + dQ[1];
	Q[2] = Qvalue[2]; Q2[2] = Q[2] + dQ[2];
	Q[3] = Qvalue[3]; Q2[3] = Q[3] + dQ[3];
	Q[4] = Qvalue[4]; Q2[4] = Q[4] + dQ[4];
}

unsigned analyze_tunnel(const sha1differentialpath& tunnel, bool verbose, bool deepanalyze)
{
#define AT_TEST_COUNT (1<<18) // 16
#define AT_MIN_PROB 0
#define AT_MIN_COUNT (AT_TEST_COUNT>>AT_MIN_PROB)

	vector< vector<unsigned> > qtbcnts(80, vector<unsigned>(32,0));
	static uint32 tcnts[80], qmask[80];
	static uint32 mmask[80], mset1[80];
	for (unsigned t = 0; t < 40; ++t) {
		tcnts[t] = 0;
		qmask[t] = maindiffpath[t].mask();
	}
	sha1differentialpath diffpath = tunnel;
	diffpath.get(-4);
	diffpath.get(80);
	fill_tables(diffpath);
	for (int t = 16; t < 80; ++t) {
		if (t-4 >= tunnel.tbegin() && t+1 < tunnel.tend()) {
			mmask[t] = ~tunnel.getme(t).mask;
			mset1[t] = tunnel.getme(t).set1conditions();
		} else {
			mmask[t] = ~uint32(0);
			mset1[t] = 0;
		}
	}

	for (unsigned k = 0; k < AT_TEST_COUNT; ++k) {
//		if (k == (AT_TEST_COUNT>>6) && double(tcnts[22])/double(k) >= 0.8) return 17;
		int t = -4;
		while (t < 17) {
			bool ok = false;
			for (unsigned j = 0; j < 3; ++j) {
				Q[offset+t] = (xrng128()&~Qvaluemask[offset+t]) ^ Qvalue[offset+t];
				if (t > -4)
					Q[offset+t] ^= Qprev[offset+t]&Q[offset+t-1];
				if (t > -3)
					Q[offset+t] ^= Qprev2[offset+t]&Q[offset+t-2];
				Q2[offset+t] = Q[offset+t] + dQ[offset+t];
				if (t >= 1) {
					m[t-1] = Q[offset+t] - (
						sha1_f1(Q[offset+t-2], rotate_left(Q[offset+t-3],30), rotate_left(Q[offset+t-4],30)) + sha1_ac[0] + rotate_left(Q[offset+t-1],5) + rotate_left(Q[offset+t-5],30)
						);
					m2[t-1] = Q2[offset+t] - (
						sha1_f1(Q2[offset+t-2], rotate_left(Q2[offset+t-3],30), rotate_left(Q2[offset+t-4],30)) + sha1_ac[0] + rotate_left(Q2[offset+t-1],5) + rotate_left(Q2[offset+t-5],30)
						);
					if ((m2[t-1]^m[t-1]) == diffpath.getme(t-1).mask) {
						ok = true;
						break;
					} else {
						if (verbose)
							cout << "(" << t << ":" << naf(m2[t-1]^m[t-1]) << "!=" << diffpath.getme(t-1) << ") " << flush;
						continue;
					}
				} else
					ok = true;
				break;
			}
			if (ok)
				++t;
			else
				t -= 4;
			if (t < -4) throw std::runtime_error("t < -4");
		}
		for (unsigned i = 16; i < 26; ++i) {
			m[i]=rotate_left(m[i-3] ^ m[i-8] ^ m[i-14] ^ m[i-16], 1);
			m2[i]=rotate_left(m2[i-3] ^ m2[i-8] ^ m2[i-14] ^ m2[i-16], 1);
			uint32 md = m[i]^m2[i];
			m[i] = (m[i]&mmask[i])^mset1[i];
			m2[i] = m[i] ^ md;
		}
		for (int t = 16; t < 20; ++t) {
			sha1_step_round1(t, Q, m);
			sha1_step_round1(t, Q2, m2);
		}
		for (int t = 20; t < 26; ++t) {
			sha1_step_round2(t, Q, m);
			sha1_step_round2(t, Q2, m2);
		}
		for (int t = 17; t < 27; ++t) {
			for (unsigned b = 0; b < 32; ++b)
				if ((Q2[offset+t]^Q[offset+t])&(1<<b))
					++qtbcnts[t][b];
		}
		int breakt = 80;
		for (int t = 17; t < 27; ++t) {
			if ((Q2[offset+t]^Q[offset+t])&(qmask[t]|0x00000000)) {
				breakt = t;
				for (int t1 = t; t1 < 27; ++t1)
					++tcnts[t1];
				break;
			}
		}
	}
	int tret = 80;
	for (int t = 17; t < 27; ++t)
	{
		if (tcnts[t] >= AT_MIN_COUNT) { tret = t; break; }
		if (double(tcnts[t]) >= double(AT_TEST_COUNT)*0.1) { tret = t; break; }
	}
	if (!deepanalyze || tret < mainparameters.mintunnelpov || tret == 80)
		return tret;
	cout << "Tunnel: " << endl;
	show_path(tunnel);
	cout << "  << ";
	for (int t1 = 17; t1 <= 25; ++t1)
		cout << t1 << ":" << ((double(100)*double(tcnts[t1]))/double(AT_TEST_COUNT)) << "% ";
	cout << ">>  " << flush;
	cout << tret << "b[";
	for (unsigned b = 0; b < 32; ++b)
		cout << b << ":" << unsigned((double(100)*double(qtbcnts[tret][b]))/double(AT_TEST_COUNT)) << "%" << (b==31?"]":",");
	cout << endl;
	return tret;
}

#define GOODBIT(i,t,b) ((goodqtb[i][t]>>b)&1)
#define BADBIT(i,t,b) ((badqtb[i][t]>>b)&1)
#define STAT_TEST(ba,go) ((double(ba)/double(ba+go))<0.6*(double(badqtb.size())/double(badqtb.size()+goodqtb.size())))
#define STAT_ANA(ba,go,s) if (STAT_TEST(ba,go)) { cout << "poss. bc: q_" << t << "[" << b << "]=" << s << ": \t" << (double(100*ba)/double(ba+go)) << "%" << endl; }
void stat_ana(vector< vector<uint32> >& goodqtb, vector< vector<uint32> >& badqtb
	, vector< vector<uint32> >& goodq2tb, vector< vector<uint32> >& badq2tb
	, int t, int b)
{
	unsigned g_zero = 0, g_one = 0, g_prev = 0, g_prevn = 0, g_prevr = 0, g_prevrn = 0, g_prev2 = 0, g_prev2n = 0;
	unsigned b_zero = 0, b_one = 0, b_prev = 0, b_prevn = 0, b_prevr = 0, b_prevrn = 0, b_prev2 = 0, b_prev2n = 0;
	for (unsigned i = 0; i < goodqtb.size(); ++i) {
		if (GOODBIT(i,t,b)==0) ++g_zero; else ++g_one;
		if (GOODBIT(i,t,b)==GOODBIT(i,t-1,b)) ++g_prev; else ++g_prevn;
		if (GOODBIT(i,t,b)==GOODBIT(i,t-1,((b+2)&31))) ++g_prevr; else ++g_prevrn;
		if (GOODBIT(i,t,b)==GOODBIT(i,t-2,((b+2)&31))) ++g_prev2; else ++g_prev2n;
	}
	for (unsigned i = 0; i < badqtb.size(); ++i) {
		if (BADBIT(i,t,b)==0) ++b_zero; else ++b_one;
		if (BADBIT(i,t,b)==BADBIT(i,t-1,b)) ++b_prev; else ++b_prevn;
		if (BADBIT(i,t,b)==BADBIT(i,t-1,((b+2)&31))) ++b_prevr; else ++b_prevrn;
		if (BADBIT(i,t,b)==BADBIT(i,t-2,((b+2)&31))) ++b_prev2; else ++b_prev2n;
	}
	double baseprob = double(badqtb.size())/double(badqtb.size()+goodqtb.size());
	STAT_ANA(b_zero,g_zero,"0");
	STAT_ANA(b_one,g_one,"1");
	STAT_ANA(b_prev,g_prev,"^");
	STAT_ANA(b_prevn,g_prevn,"!");
	STAT_ANA(b_prevr,g_prevr,"^r");
	STAT_ANA(b_prevrn,g_prevrn,"!r");
	STAT_ANA(b_prev2,g_prev2,"M");
	STAT_ANA(b_prev2n,g_prev2n,"#");
	if (b != 0) return;
	if (1) {
		map<sdr, pair<unsigned,unsigned> > dqtstat;
		for (unsigned i = 0; i < goodqtb.size(); ++i)
			++dqtstat[sdr(goodqtb[i][t],goodq2tb[i][t])].first;
		for (unsigned i = 0; i < badqtb.size(); ++i)
			++dqtstat[sdr(badqtb[i][t],badq2tb[i][t])].second;
		typedef map<sdr, pair<unsigned,unsigned> >::const_iterator dqtstatcit;
		vector<dqtstatcit> goodcits;
		unsigned goodcnt = 0, badcnt = 0;
		for (dqtstatcit cit = dqtstat.begin(); cit != dqtstat.end(); ++cit)
			if ((double(cit->second.second)/double(cit->second.first+cit->second.second))<0.6*baseprob) {
				goodcits.push_back(cit);
				goodcnt += cit->second.first;
				badcnt += cit->second.second;
			}
		if (goodcits.size() && double(goodcnt) > 0.25*double(goodqtb.size()) && goodcits.size()*2 < goodqtb.size()) {
			cout << "good dQ_" << t << " " << double(badcnt*100)/double(badcnt+goodcnt) << "% bad #=" << goodcits.size() << ":\t";
			for (unsigned i = 0; i < goodcits.size(); ++i)
				if (double(goodcits[i]->second.first) > 0.01*double(goodqtb.size()))
				cout << (goodcits[i]->first) << "(" << 100.0*double(goodcits[i]->second.first)/double(goodcnt) << "%) ";
			cout << endl;
		}
	}
	if (1) {
		map<sdr, pair<unsigned,unsigned> > dftstat;
		if (t < 20) {
			for (unsigned i = 0; i < goodqtb.size(); ++i) {
				uint32 F1 = sha1_f1(goodqtb[i][t-1], rotate_left(goodqtb[i][t-2],30), rotate_left(goodqtb[i][t-3],30));
				uint32 F2 = sha1_f1(goodq2tb[i][t-1], rotate_left(goodq2tb[i][t-2],30), rotate_left(goodq2tb[i][t-3],30));
				++dftstat[sdr(F1,F2)].first;
			}
			for (unsigned i = 0; i < badqtb.size(); ++i) {
				uint32 F1 = sha1_f1(badqtb[i][t-1], rotate_left(badqtb[i][t-2],30), rotate_left(badqtb[i][t-3],30));
				uint32 F2 = sha1_f1(badq2tb[i][t-1], rotate_left(badq2tb[i][t-2],30), rotate_left(badq2tb[i][t-3],30));
				++dftstat[sdr(F1,F2)].second;
			}
		} else {
			for (unsigned i = 0; i < goodqtb.size(); ++i) {
				uint32 F1 = sha1_f2(goodqtb[i][t-1], rotate_left(goodqtb[i][t-2],30), rotate_left(goodqtb[i][t-3],30));
				uint32 F2 = sha1_f2(goodq2tb[i][t-1], rotate_left(goodq2tb[i][t-2],30), rotate_left(goodq2tb[i][t-3],30));
				++dftstat[sdr(F1,F2)].first;
			}
			for (unsigned i = 0; i < badqtb.size(); ++i) {
				uint32 F1 = sha1_f2(badqtb[i][t-1], rotate_left(badqtb[i][t-2],30), rotate_left(badqtb[i][t-3],30));
				uint32 F2 = sha1_f2(badq2tb[i][t-1], rotate_left(badq2tb[i][t-2],30), rotate_left(badq2tb[i][t-3],30));
				++dftstat[sdr(F1,F2)].second;
			}
		}
		typedef map<sdr, pair<unsigned,unsigned> >::const_iterator dftstatcit;
		vector<dftstatcit> goodcits;
		unsigned goodcnt = 0, badcnt = 0;
		for (dftstatcit cit = dftstat.begin(); cit != dftstat.end(); ++cit)
			if ((double(cit->second.second)/double(cit->second.first+cit->second.second))<0.6*baseprob) {
				goodcits.push_back(cit);
				goodcnt += cit->second.first;
				badcnt += cit->second.second;
			}
		if (goodcits.size() && double(goodcnt) > 0.25*double(goodqtb.size()) && goodcits.size()*2 < goodqtb.size()) {
			cout << "good dF_" << t << " " << double(badcnt*100)/double(badcnt+goodcnt) << "% bad #=" << goodcits.size() << ":\t";
			for (unsigned i = 0; i < goodcits.size(); ++i)
				if (double(goodcits[i]->second.first) > 0.01*double(goodqtb.size()))
				cout << (goodcits[i]->first) << "(" << 100.0*double(goodcits[i]->second.first)/double(goodcnt) << "%) ";
			cout << endl;
		}
	}
}

unsigned analyze_bc_tunnel(const sha1differentialpath& tunnel)
{
#define ATLC_TEST_COUNT (1<<18) // 16
#define ATLC_MIN_PROB 0
#define ATLC_MIN_COUNT (ATLC_TEST_COUNT>>ATLC_MIN_PROB)
	unsigned tend = analyze_tunnel(tunnel, false, mainparameters.changebitstat);
	memset(m, 0, sizeof(uint32)*16);
	for (int i = 0; i < 16; ++i)
		if (i >= tunnel.tbegin() && i < tunnel.tend())
			m[i] = tunnel.getme(i).mask;
	for (unsigned i = 16; i < 32; ++i)
		m[i]=rotate_left(m[i-3] ^ m[i-8] ^ m[i-14] ^ m[i-16], 1);
//	if (tend < 21)
//		return tend;
	show_path(tunnel);
	cout << tend << endl;
	vector< vector<uint32> > dmes(80);
	unsigned tend2 = tend;
	uint64 mcnt = 1;
	for (unsigned i = 16; i < tend; ++i) {
		sdr tmp;
		tmp.mask = m[i];
		tmp.sign = 0;
		do {
			dmes[i].push_back(tmp.adddiff());
			tmp.sign += ~tmp.mask + 1;
			tmp.sign &= tmp.mask;
		} while (tmp.sign != 0);
		sort(dmes[i].begin(), dmes[i].end());
		dmes[i].erase( unique(dmes[i].begin(),dmes[i].end()), dmes[i].end());
		if ((mcnt * dmes[i].size()) > (1<<6)) {
			// don't let mcnt grow too big
			tend2 = i;
			break;
		}
		mcnt *= uint64(dmes[i].size());
	}
	cout << mcnt << endl;
	uint32 qmask[80];
	for (unsigned t = 0; t < 40; ++t)
		qmask[t] = maindiffpath[t].mask();

	for (unsigned mi = 0; mi < mcnt; ++mi) {
		vector< vector<uint32> > goodqtb, badqtb, goodq2tb, badq2tb;
		vector<unsigned> dmesindex(80,0);
		unsigned mi2 = mi;
		cout << mi << ": ";
		for (unsigned i = 16; i < tend2; ++i)
			if (dmes[i].size() > 1) {
				dmesindex[i] = mi2 % dmes[i].size();
				mi2 /= dmes[i].size();
				cout << i << naf(dmes[i][dmesindex[i]]);
			}
		cout << ": ";
		sha1differentialpath diffpath = tunnel;
		diffpath.get(-4);
		diffpath.get(80);
		fill_tables(diffpath);
		for (unsigned k = 0; k < ATLC_TEST_COUNT; ++k) {
			int t = -4;
			while (t < 17) {
				bool ok = false;
				for (unsigned j = 0; j < 3; ++j) {
					Q[offset+t] = (xrng128()&~Qvaluemask[offset+t]) ^ Qvalue[offset+t];
					if (t > -4)
						Q[offset+t] ^= Qprev[offset+t]&Q[offset+t-1];
					if (t > -3)
						Q[offset+t] ^= Qprev2[offset+t]&Q[offset+t-2];
					Q2[offset+t] = Q[offset+t] + dQ[offset+t];
					if (t >= 1) {
						m[t-1] = Q[offset+t] - (
							sha1_f1(Q[offset+t-2], rotate_left(Q[offset+t-3],30), rotate_left(Q[offset+t-4],30)) + sha1_ac[0] + rotate_left(Q[offset+t-1],5) + rotate_left(Q[offset+t-5],30)
							);
						m2[t-1] = Q2[offset+t] - (
							sha1_f1(Q2[offset+t-2], rotate_left(Q2[offset+t-3],30), rotate_left(Q2[offset+t-4],30)) + sha1_ac[0] + rotate_left(Q2[offset+t-1],5) + rotate_left(Q2[offset+t-5],30)
							);
						if ((m2[t-1]^m[t-1]) == diffpath.getme(t-1).mask) {
							ok = true;
							break;
						} else {
							continue;
						}
					} else
						ok = true;
					break;
				}
				if (ok)
					++t;
				else
					t -= 4;
				if (t < -4) throw std::runtime_error("t < -4");
			}
			for (unsigned i = 16; i < 26; ++i) {
				m[i]=rotate_left(m[i-3] ^ m[i-8] ^ m[i-14] ^ m[i-16], 1);
				if (i < tend2)
					m2[i] = m[i] + dmes[i][dmesindex[i]];
				else
					m2[i]=rotate_left(m2[i-3] ^ m2[i-8] ^ m2[i-14] ^ m2[i-16], 1);
			}
			for (int t = 16; t < 20; ++t) {
				sha1_step_round1(t, Q, m);
				sha1_step_round1(t, Q2, m2);
			}
			for (int t = 20; t < 26; ++t) {
				sha1_step_round2(t, Q, m);
				sha1_step_round2(t, Q2, m2);
			}
			for (int t = 17; t < 27; ++t) {
				if ((Q2[offset+t]^Q[offset+t])&(qmask[t]|0x00000000)) {
					static vector<uint32> tmpqtb(27);
					for (int t1 = 0; t1 < tmpqtb.size(); ++t1)
						tmpqtb[t1] = Q[4+t1];
					if (t <= tend)
						badqtb.push_back(tmpqtb);
					else
						goodqtb.push_back(tmpqtb);
					for (int t1 = 0; t1 < tmpqtb.size(); ++t1)
						tmpqtb[t1] = Q2[4+t1];
					if (t <= tend)
						badq2tb.push_back(tmpqtb);
					else
						goodq2tb.push_back(tmpqtb);
					break;
				}
			}
		}
		cout << goodqtb.size() << " vs " << badqtb.size() << ": " << (double(100)*double(badqtb.size()))/double(goodqtb.size()+badqtb.size()) << "% bad" << endl;
		for (unsigned t = 16-4; t < tend; ++t)
			for (unsigned b = 0; b < 32; ++b) {
				stat_ana(goodqtb, badqtb, goodq2tb, badq2tb, t, b);
			}
	}
	return tend;
}

void find_best_tunnel(vector< sha1differentialpath >& tunnels, const sha1differentialpath& mypath, const vector< vector<uint32> >& bitrels, bool force_lc = false)
{
//#define ADD_ME_CARRIES 0
	unsigned ADD_ME_CARRIES = mainparameters.mecarry;
	if (mainparameters.tunnelfilterbitcondition)
		filter_tunnels_bitconditions(tunnels, mypath);
	static vector<sdr> sdrsvec[16];
	sdr metbu;
	static vector<unsigned> iQvec, ncvec;
	iQvec.assign(tunnels.size(),0);
	ncvec.assign(tunnels.size(),1<<20);
//	cout << tunnels.size() << endl;
	for (unsigned i = 0; i < tunnels.size(); ++i) {
		uint64 cnt = 1;
		for (int t = 0; t < 16; ++t) {
			sdrsvec[t].clear();
			if (t < tunnels[i].tbegin() || t >= tunnels[i].tend()) continue;
			metbu = tunnels[i].getme(t);
			table_sdrs(sdrsvec[t], metbu.adddiff(), hw(metbu.mask)+ADD_ME_CARRIES);
			unsigned j = 0;
			while (j < sdrsvec[t].size()) {
				if (sdrsvec[t][j].get(31) == -1) {
					swap(sdrsvec[t][j], sdrsvec[t][sdrsvec[t].size()-1]);
					sdrsvec[t].pop_back();
				} else
					++j;
			}
			cnt *= sdrsvec[t].size();
		}
		if (cnt == 0)
			throw std::runtime_error("cnt == 0 ??");
		unsigned bestiQ = 0, bestnc = 1<<20;
		uint64 bestc = cnt;
		unsigned basenc = tunnels[i].nrcond();
		for (uint64 c = 0; c < cnt; ++c) {
			uint64 cc = c;
			for (int t = 0; t < 16; ++t) {
				if (sdrsvec[t].size()>1) {
					uint64 k = cc % sdrsvec[t].size();
					cc /= sdrsvec[t].size();
					tunnels[i].getme(t) = sdrsvec[t][k];
				}
			}
			if (!filter_tunnel_bitrelations(tunnels[i],bitrels)) continue;
			unsigned iQ;
			if (force_lc)
				iQ = analyze_bc_tunnel(tunnels[i]);
			else
				iQ = analyze_tunnel(tunnels[i], false, mainparameters.changebitstat);
			if (iQ < bestiQ) continue;
			if (iQ > bestiQ) {
				bestiQ = iQ;
				bestnc = 1<<20;
				bestc = c;
			}
			unsigned nc = basenc;
			for (int t = tunnels[i].tbegin(); t < tunnels[i].tend(); ++t)
				nc += hw(tunnels[i].getme(t).mask&0x7FFFFFFF);
//			if (nc >= 15) continue;
			if (nc < bestnc) {
				bestnc = nc;
				bestc = c;
			}
		}
		if (bestc == cnt) {
			tunnels[i].clear();
			continue;
		}
		uint64 cc = bestc;
		for (int t = 0; t < 16; ++t) {
			if (sdrsvec[t].size()) {
				uint64 k = cc % sdrsvec[t].size();
				cc /= sdrsvec[t].size();
				tunnels[i].getme(t) = sdrsvec[t][k];
			}
		}
		iQvec[i] = bestiQ;
		ncvec[i] = bestnc;
//		if (bestiQ >= 23) break;
	}
	unsigned bestiQ = 0, bestnc = 1<<20, besti = tunnels.size(), minnc = 1<<20;
	for (unsigned i = 0; i < tunnels.size(); ++i) {
		if (tunnels[i].path.size()) {
			if (iQvec[i] > bestiQ) {
				bestiQ = iQvec[i];
				bestnc = ncvec[i];
				besti = i;
			} else if (iQvec[i] == bestiQ && ncvec[i] < bestnc) {
				bestnc = ncvec[i];
				besti = i;
			}
			if (ncvec[i] < minnc)
				minnc = ncvec[i];
		}
	}
	if (besti < tunnels.size()) {
//		if (bestiQ >= 21)
			cout << endl << bestiQ << "(" << bestnc << "," << minnc << ") " << flush;
		if (bestiQ >= 16) { //21
			//show_path(tunnels[besti]);
#if 0
			analyze_tunnel(tunnels[besti], false, true);
#else
			for (unsigned j = 0; j < tunnels.size(); ++j)
				if (iQvec[j] == iQvec[besti])
					analyze_tunnel(tunnels[j], false, true);
#endif
		}
		sha1differentialpath bestpath;
		bestpath.swap(tunnels[besti]);
		tunnels.resize(1);
		bestpath.swap(tunnels[0]);

		if (bestiQ < 19) tunnels.clear();

	} else
		tunnels.clear();
}

void create_tunnel_stept(vector< sha1differentialpath >& tunnels, sha1differentialpath& tunnel, int t, bool force_lc = false)
{
	if (t >= 0 && t+1 >= tunnel.tend()) {
		create_tunnel_stept(tunnels, tunnel, t-1, force_lc);
		return;
	}

	if (t-4 < tunnel.tbegin() || t < 0) {
		tunnels.push_back(tunnel);
		return;
	}
	if (tunnel[t-1].diff() == 0 && tunnel[t-2].diff() == 0 && tunnel[t-3].diff() == 0) {
		// dFt = 0
		uint32 dme = tunnel[t+1].diff() - tunnel[t].getsdr().rotate_left(5).adddiff() - tunnel[t-4].getsdr().rotate_left(30).adddiff();
		tunnel.getme(t) = naf(dme);
		create_tunnel_stept(tunnels, tunnel, t-1, force_lc);
		return;
	}

	bf_conditions newconditions[32][2];
	bool bitactive[32];
	unsigned cnt = 1;
	for (unsigned b = 0; b < 32; ++b) {
		bitactive[b] = false;
		//sha1_f1(Q[offset+t-1], rotate_left(Q[offset+t-2],30), rotate_left(Q[offset+t-3],30))
		bf_outcome bo = SHA1_F1_data.outcome(tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31));
		if (bo.size() == 0) throw std::runtime_error("bo.size() == 0");
		if (bo.size() == 1) continue;
		if (bo.size() == 2) {
			if (/*b == 31 &&*/ bo[0] != bc_constant && bo[1] != bc_constant)
				continue;
			if (force_lc) {
				bf_conditions bf;
				if (bo[0] != bc_constant)
					bf = SHA1_F1_data.backwardconditions( tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31), bo[0] );
				else
					bf = SHA1_F1_data.backwardconditions( tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31), bo[1] );
				tunnel.setbitcondition(t-1,b,bf.first);
				tunnel.setbitcondition(t-2,(b+2)&31,bf.second);
				tunnel.setbitcondition(t-3,(b+2)&31,bf.third);
				continue;
			}
			newconditions[b][0] = SHA1_F1_data.backwardconditions( tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31), bo[0]);
			newconditions[b][1] = SHA1_F1_data.backwardconditions( tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31), bo[1]);
			cnt *= 2;
			bitactive[b] = true;
		} else { // bo.size() == 3
			bf_conditions bf = SHA1_F1_data.backwardconditions( tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31), bc_constant );
#if 1
			if (mainparameters.simpletunnels) {
				tunnel.setbitcondition(t-1,b,bf.first);
				tunnel.setbitcondition(t-2,(b+2)&31,bf.second);
				tunnel.setbitcondition(t-3,(b+2)&31,bf.third);
				continue;
			}
#endif
			if (force_lc) {
				tunnel.setbitcondition(t-1,b,bf.first);
				tunnel.setbitcondition(t-2,(b+2)&31,bc_prevn);
				tunnel.setbitcondition(t-3,(b+2)&31,bf.third);
				continue;
			}
			if (bf.second != bc_prev) throw std::runtime_error("bf.second != bc_prev");
			newconditions[b][0] = bf;
			bf.second = bc_prevn;
			newconditions[b][1] = bf;
			cnt *= 2;
			bitactive[b] = true;
		}
	}
	for (unsigned c = 0; c < cnt; ++c) {
		unsigned cc = c;
		uint32 dFt = 0;
		for (unsigned b = 0; b < 32; ++b) {
			if (bitactive[b]) {
				unsigned k = cc % 2;
				cc /= 2;
				tunnel.setbitcondition(t-1,b,newconditions[b][k].first);
				tunnel.setbitcondition(t-2,((b+2)&31),newconditions[b][k].second);
				tunnel.setbitcondition(t-3,((b+2)&31),newconditions[b][k].third);
			}
			dFt += SHA1_F1_data.outcome(tunnel(t-1,b), tunnel(t-2,(b+2)&31), tunnel(t-3,(b+2)&31))(0,b);
		}

		uint32 dme = tunnel[t+1].diff() - dFt - tunnel[t].getsdr().rotate_left(5).adddiff() - tunnel[t-4].getsdr().rotate_left(30).adddiff();
		tunnel.getme(t) = naf(dme);
		create_tunnel_stept(tunnels, tunnel, t-1, force_lc);
	}
}

void analyze_tunnels_diffpath(const sha1differentialpath& mypath, const vector< vector<uint32> >& bitrels, vector<sha1differentialpath> tunnelsin)
{
	cout << "Analyzing tunnels for differential path:" << endl;
	sha1differentialpath path = mypath;
	cleanup_path(path);
	show_path(path);
	maindiffpath = path;
	cout << "Message space determined by " << bitrels.size() << " bitrelations." << endl;

	bool deep_analysis = mainparameters.tunneldeepanalysis;

	if (tunnelsin.size()) {
		vector<sha1differentialpath> tunnelstmp;
		for (unsigned i = 0; i < tunnelsin.size(); ++i)
			create_tunnel_stept(tunnelstmp, tunnelsin[i], 15);
		find_best_tunnel(tunnelstmp, path, bitrels, deep_analysis);
		return;
	}

	sha1differentialpath tunnelpath;
	vector< sha1differentialpath > tunnels, tunnelstmp, tunnelstmp2;

	cout << "Generating tunnels..." << endl;
	set<int> goodbits;
	set< pair<int,int> > good2bits;
#if 1
	for (int bit1 = 0; bit1 < 16*32; ++bit1) {
		int t1 = bit1 / 32;
		int b1 = bit1 % 32;
		tunnelpath.clear();
		tunnelpath.get(t1-4);
		if (t1 + 6 < 16)
			tunnelpath.get(t1+6);
		else
			tunnelpath.get(16);
		tunnelpath.setbitcondition(t1+1,b1,bc_plus);
		tunnelstmp.clear();
		create_tunnel_stept(tunnelstmp, tunnelpath, 15);
		find_best_tunnel(tunnelstmp, path, bitrels, deep_analysis);
		if (tunnelstmp.size()) {
			goodbits.insert(bit1);
			continue;
		}
	}
#if 1
	for (int bit1 = 0; bit1 < 16*32; ++bit1) {
		for (int bit2 = bit1+1; bit2 < 16*32; ++bit2) {
			if (goodbits.find(bit1) != goodbits.end()) continue;
			if (goodbits.find(bit2) != goodbits.end()) continue;
			int t1 = bit1 / 32;
			int b1 = bit1 % 32;
			int t2 = bit2/32;
			int b2 = bit2%32;
			tunnelpath.clear();
			tunnelpath.get(t1-4);
			if (t2 + 6 < 16)
				tunnelpath.get(t2+6);
			else
				tunnelpath.get(16);
			tunnelpath.setbitcondition(t1+1,b1,bc_plus);
			tunnelpath.setbitcondition(t2+1,b2,bc_plus);
			tunnelstmp.clear();
			create_tunnel_stept(tunnelstmp, tunnelpath, 15);
			find_best_tunnel(tunnelstmp, path, bitrels, deep_analysis);
			if (tunnelstmp.size())
				good2bits.insert(pair<int,int>(bit1,bit2));
		}
	}
#endif
//	exit(0);
#endif
	for (int bit1 = 16*32-1; bit1 >= 0; --bit1) {
//	for (int bit1 = 169; bit1 < 16*32; ++bit1) {
		cout << "[" << bit1 << "] " << flush;
		for (int bit2 = bit1; bit2 < 16*32; ++bit2) {
			for (int bit2sign = 0; bit2sign < 1; ++bit2sign)
			for (int bit3 = bit2; bit3 < 16*32; ++bit3)
			for (int bit3sign = 0; bit3sign < 1; ++bit3sign)
			{
				//if (bit2 != bit1 || bit3 != bit1) continue; // force single bit difference
				if (!mainparameters.threebittunnels && bit3 != bit2) continue; // force max 2 bit difference
				if (bit1 == bit2 && bit2sign == 1) continue;
				if (bit3 == bit2 && bit3sign != bit2sign) continue;
				if (bit1 == bit2 && bit3 > bit2) continue;
				if (goodbits.find(bit1) != goodbits.end()) continue;
				if (goodbits.find(bit2) != goodbits.end()) continue;
				if (goodbits.find(bit3) != goodbits.end()) continue;
				if (good2bits.find(pair<int,int>(bit1,bit2)) != good2bits.end()) continue;
				if (good2bits.find(pair<int,int>(bit1,bit3)) != good2bits.end()) continue;
				if (good2bits.find(pair<int,int>(bit2,bit3)) != good2bits.end()) continue;

				//cout << "[" << bit1 << "," << bit2 << "] " << flush;
				int t1 = bit1 / 32;
				int b1 = bit1 % 32;
				int t2 = bit2 / 32;
				int b2 = bit2 % 32;
				int t3 = bit3 / 32;
				int b3 = bit3 % 32;
				tunnelpath.clear();
				tunnelpath.get(t1-4);
				if (t3 + 6 < 16)
					tunnelpath.get(t3+6);
				else
					tunnelpath.get(16);
				tunnelpath.setbitcondition(t1+1,b1,bc_plus);
				if (bit2sign == 0)
					tunnelpath.setbitcondition(t2+1,b2,bc_plus);
				else
					tunnelpath.setbitcondition(t2+1,b2,bc_minus);
				if (bit3sign == 0)
					tunnelpath.setbitcondition(t3+1,b3,bc_plus);
				else
					tunnelpath.setbitcondition(t3+1,b3,bc_minus);
				tunnelstmp.clear();
				create_tunnel_stept(tunnelstmp, tunnelpath, 15);
				find_best_tunnel(tunnelstmp, path, bitrels, deep_analysis);
			}
		}
	}
	exit(0);
}

struct ihv_type {
	uint32 ihv[5];
	bool operator<(const ihv_type& r) const {
		for (int i = 0; i < 4; ++i) {
			if (ihv[i] < r.ihv[i]) return true;
			if (ihv[i] > r.ihv[i]) return false;
		}
		if (ihv[4] < r.ihv[4]) return true;
		return false;
	}
	bool operator==(const ihv_type& r) const {
		for (int i = 0; i < 5; ++i)
			if (ihv[i] != r.ihv[i]) return false;
		return true;
	}
};

void analyze_indepsection_prob()
{
	vector< vector< vector<uint32> > > pathbitrelationsmatrix;
	mespace_to_pathbitrelationsmatrix(mainmespace, pathbitrelationsmatrix);
	sha1differentialpath path = maindiffpath;
	int tbegin = 16;
	for (; tbegin < 80; ++tbegin)
		if (0 == (path[tbegin].diff() | path[tbegin-1].diff() | path[tbegin-2].diff() | path[tbegin-3].diff() | path[tbegin-4].diff()))
			break;
	while (tbegin < 80) {
		int tend = tbegin+1;
		for (; tend < 80; ++tend)
			if (0 == (path[tend].diff() | path[tend-1].diff() | path[tend-2].diff() | path[tend-3].diff() | path[tend-4].diff()))
				break;
		if (0 != (path[tend].diff() | path[tend-1].diff() | path[tend-2].diff() | path[tend-3].diff() | path[tend-4].diff()))
			break;
		if (tend == tbegin+1) {
			tbegin = tend;
			continue;
		}
		cout << "Prob t=[" << tbegin << "-" << tend << "): \t" << flush;
#if 0
		tbegin = tend; cout << endl; continue;
#endif
		uint64 cnt = 0;
		uint32 okcnt = 0;
		while (okcnt < (1<<15)) {
			++cnt; if (hw(cnt)==1) cout << cnt << " " << flush;
			random_me(m, pathbitrelationsmatrix);
			for (int t = 0; t < 16; ++t)
				m2[t] = m[t] ^ path.getme(t).mask;
			for (int t = 16; t < 80; ++t)
				m2[t]=rotate_left(m2[t-3] ^ m2[t-8] ^ m2[t-14] ^ m2[t-16], 1);
			for (int t = tbegin-4; t <= tbegin; ++t)
				Q2[offset+t] = Q[offset+t] = xrng64();
			for (int t = tbegin; t < tend; ++t) {
				sha1_step(t, Q, m);
				sha1_step(t, Q2, m2);
			}
			bool ok = true;
			for (int t = tend-4; t <= tend; ++t)
				if (Q2[offset+t] != Q[offset+t]) {
					ok = false;
					break;
				}
			if (ok)
				if (hw(++okcnt)==1) cout << "[" << okcnt << "] " << flush;
		}
		cout << " prob=" << log(double(okcnt)/double(cnt))/log(2.0) << endl;

		tbegin = tend;
	}
	cout << "Prob t=[" << tbegin << "-" << 80 << "):" << endl;
	vector<ihv_type> target_dihvs(20);
	vector<ihv_type> dihvs(1<<28);
	progress_display pd(dihvs.size());
	for (unsigned i = 0; i < dihvs.size(); ++i,++pd) {
		random_me(m, pathbitrelationsmatrix);
		for (int t = 0; t < 16; ++t)
			m2[t] = m[t] ^ path.getme(t).mask;
		for (int t = 16; t < 80; ++t)
			m2[t]=rotate_left(m2[t-3] ^ m2[t-8] ^ m2[t-14] ^ m2[t-16], 1);
		for (int t = tbegin-4; t <= tbegin; ++t)
			Q2[offset+t] = Q[offset+t] = xrng64();
		for (int t = -4; t <= 0; ++t)
			Q2[offset+t] = Q[offset+t] = xrng64();
		for (int t = tbegin; t < 80; ++t) {
			sha1_step(t, Q, m);
			sha1_step(t, Q2, m2);
		}
		dihvs[i].ihv[0] = rotate_left(Q2[offset+80-4],30)-rotate_left(Q[offset+80-4],30);
		dihvs[i].ihv[1] = rotate_left(Q2[offset+80-3],30)-rotate_left(Q[offset+80-3],30);
		dihvs[i].ihv[2] = rotate_left(Q2[offset+80-2],30)-rotate_left(Q[offset+80-2],30);
		dihvs[i].ihv[3] = Q2[offset+80-1]-Q[offset+80-1];
		dihvs[i].ihv[4] = Q2[offset+80]-Q[offset+80];
	}
	sort(dihvs.begin(), dihvs.end());
	unsigned i = 0;
	vector<unsigned> bestcnts;
	while (i < dihvs.size()) {
		unsigned j = i+1;
		while (j < dihvs.size() && dihvs[j] == dihvs[i]) ++j;
		bestcnts.push_back(j-i);
		i = j;
	}
	cout << "Prob t=[" << tbegin << "-" << 80 << "): " << flush;
	sort(bestcnts.begin(),bestcnts.end());
	unsigned bestcnt = bestcnts[bestcnts.size()-1];
#if 1
	target_dihvs.clear();
	i = 0;
	while (i < dihvs.size()) {
		unsigned j = i+1;
		while (j < dihvs.size() && dihvs[j] == dihvs[i]) ++j;
		if ((j-i)*2 >= bestcnt) {
			cout << "\tprob=" << log(double(j-i)/double(dihvs.size()))/log(2.0) << ":\t";
			for (unsigned k = 0; k < 5; ++k)
				cout << naf(dihvs[i].ihv[k]) << " ";
			cout << endl;
			target_dihvs.push_back(dihvs[i]);
		}
		i = j;
	}
	{ vector<ihv_type> tmptmp(1); dihvs.swap(tmptmp); } // free memory
	uint64 cnt = 0, okcnt = 0;
	ihv_type dihv;
	while (true) {
		random_me(m, pathbitrelationsmatrix);
		for (int t = 0; t < 16; ++t)
			m2[t] = m[t] ^ path.getme(t).mask;
		for (int t = 16; t < 80; ++t)
			m2[t]=rotate_left(m2[t-3] ^ m2[t-8] ^ m2[t-14] ^ m2[t-16], 1);
		for (int t = tbegin-4; t <= tbegin; ++t)
			Q2[offset+t] = Q[offset+t] = xrng64();
		for (int t = -4; t <= 0; ++t)
			Q2[offset+t] = Q[offset+t] = xrng64();
		for (int t = tbegin; t < 80; ++t) {
			sha1_step(t, Q, m);
			sha1_step(t, Q2, m2);
		}
		dihv.ihv[0] = rotate_left(Q2[offset+80-4],30)-rotate_left(Q[offset+80-4],30);
		dihv.ihv[1] = rotate_left(Q2[offset+80-3],30)-rotate_left(Q[offset+80-3],30);
		dihv.ihv[2] = rotate_left(Q2[offset+80-2],30)-rotate_left(Q[offset+80-2],30);
		dihv.ihv[3] = Q2[offset+80-1]-Q[offset+80-1];
		dihv.ihv[4] = Q2[offset+80]-Q[offset+80];
		++cnt;
		for (unsigned k = 0; k < target_dihvs.size(); ++k)
			if (dihv == target_dihvs[k]) {
				++okcnt;
				break;
			}
		if (hw(cnt)+hw(cnt>>32)==1 && okcnt > 1) {
			cout << "[" << cnt << ":p=" << log(double(okcnt)/double(cnt))/log(2.0) << "]" << flush;
		}
	}
#else
	for (unsigned i = 0; i < bestcnts.size(); ++i)
		if (bestcnts[i]*2 >= bestcnt)
			cout << " prob=" << log(double(bestcnts[i])/double(dihvs.size()))/log(2.0) << flush;
#endif
	cout << endl;
	exit(0);
}


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

#include "GafferScene/Constraint.h"

#include "Gaffer/StringPlug.h"

#include "IECoreScene/CurvesPrimitive.h"
#include "IECoreScene/MeshPrimitive.h"
#include "IECoreScene/PointsPrimitive.h"

#include <boost/format.hpp>

#include <algorithm>
#include <cassert>
#include <cmath>
#include <limits>

using namespace Imath;
using namespace IECore;
using namespace Gaffer;
using namespace GafferScene;

namespace
{

template< typename Indexer >
bool convexPolygon( const Indexer vertices, const int n )
{
	assert( n >= 3 );

	if( n == 3 )
	{
		return true;
	}

	int c[ 2 ] = { 0, 0 };

	for( int i = 0; i < n; ++i )
	{
		const int ip = ( i + n - 1 ) % n;
		const int in = ( i     + 1 ) % n;

		const Imath::V2f vi = vertices( i );

		const float Av = ( vi - vertices( ip ) ) % ( vertices( in ) - vi );

		// NOTE : colinear edges are ok

		if( Av == 0.f )
		{
			c[ 0 ] += 1;
			c[ 1 ] += 1;
		}
		else
		{
			c[ std::signbit( Av ) ? 0 : 1 ] += 1;
		}
	}

	return
		( c[ 0 ] == n ) ||
		( c[ 1 ] == n );
}

template< typename Indexer >
bool ptInPolygon( const Indexer vertices, const Imath::V2f v, const int n )
{
	bool result = false;
	for( int i = 0; i < n; ++i )
	{
		const Imath::V2f v0 = vertices( i );
		const Imath::V2f v1 = vertices( ( i + 1 ) % n );

		// NOTE: Algorithm 3.5, "Collision Detection in Interactive 3d Environments", Gino Van Den Bergen
		//       with modifications to include end points and points on axis aligned edges.

		if( v0 == v )
		{
			return true; // NOTE : end point
		}
		else if(
			( ( v0.y == v1.y ) && ( v.y == v0.y ) && ( ( v0.x >= v.x ) != ( v1.x >= v.x ) ) ) ||
			( ( v0.x == v1.x ) && ( v.x == v0.x ) && ( ( v0.y >= v.y ) != ( v1.y >= v.y ) ) ) )
		{
			return true; // NOTE : axis aligned edge
		}
		else if( ( v0.y >= v.y ) != ( v1.y >= v.y ) )
		{
			// Edge crosses horizontal line `y == v.y`

			if( ( v0.x >= v.x ) != ( v1.x >= v.x ) )
			{
				// Edge crosses vertical line `x == v.x`

				if( v0.x + ( v.y - v0.y ) * ( v1.x - v0.x ) / ( v1.y - v0.y ) >= v.x )
				{
					// Edge crosses the ray `y == v.y, x >= v.x`

					result = !result;
				}
			}
			else if( v0.x >= v.x )
			{
				// Edge crosses the ray `y == v.y, x >= v.x`

				result = !result;
			}
		}
	}

	return result;
}

template< typename PIndexer, typename UVIndexer >
Imath::V3f interpolateConvexPolygon( const PIndexer points, const UVIndexer uvs, const Imath::V2f uv, const int n )
{
	// NOTE : compute vertex and edge triangle areas
	//
	//        Av[ i ] is 2 * area of the triangle formed by the vertices v(i-1), v(i) and v(i+1)
	//        Ae[ i ] is 2 * area of the triangle formed by the vertices v(i), v(i+1) and uv

	std::unique_ptr< float[] > Av( new float[ n ] );
	std::unique_ptr< float[] > Ae( new float[ n ] );

	for( int i = 0; i < n; ++i )
	{
		const Imath::V2f vi = uvs( i );

		// NOTE : avoid zero length edges by skipping past adjacent duplicate uv vertices
		//        in both directions, c is the number of times the duplicate vertex occurs
		//        in a consecutive run including the current vertex (vi). It is important
		//        that only adjacent duplicated vertices are included. The computed vertex
		//        area is weighted by the reciprocal of c to average the influence of the
		//        positions corresponding to the duplicated uv vertices.

		float c = 1.f;

		Imath::V2f vp;
		for( int j = 1; j < n; ++j )
		{
			vp = uvs( ( i + n - j ) % n );
			if( vp != vi ) break;
			c += 1.f;
		}

		Imath::V2f vn;
		for( int j = 1; j < n; ++j )
		{
			vn = uvs( ( i + j ) % n );
			if( vn != vi ) break;
			c += 1.f;
		}

		Av[ i ] = ( vi - vp ) % ( vn - vi );
		Ae[ i ] = ( vn - vi ) % ( uv - vn );

		// NOTE : sign of vertex area depends on winding.

		if( std::signbit( Av[ i ] ) )
		{
			Av[ i ] = -( Av[ i ] );
			Ae[ i ] = -( Ae[ i ] );
		}

		// NOTE : clamp edge area to minimum of zero to prevent negative weights

		Ae[ i ] = std::max( Ae[ i ], 0.f );

		// NOTE : this clamp is done in two steps to prevent underflow to zero

		Av[ i ] = std::max( Av[ i ], c * std::numeric_limits< float >::min() );
		Av[ i ] /= c;

		// NOTE : uv is considered on an edge when the edge area is below threshold in which
		//        case lerp between average of all positions corresponding to end vertices

		if(
			( Ae[ i ] < std::sqrt( std::numeric_limits< float >::min() ) ) &&
			( ( ( uv - vi ) ^ ( uv - vn ) ) < std::numeric_limits< float >::min() ) )
		{
			const float l2 = ( vn - vi ).length2();
			const float t = std::min( std::max(
				( l2 > ( 2.f * std::numeric_limits< float >::min() ) )
					? std::sqrt( ( uv - vn ).length2() / l2 ) : 0.f, 0.f ), 1.f );

			Imath::V3f pv( 0.f );
			Imath::V3f pn( 0.f );

			float pvc = 0.f;
			float pnc = 0.f;

			for( int j = 0; j < n; ++j )
			{
				if( uvs( j ) == vi )
				{
					pv += points( j );
					pvc += 1.f;
				}
				if( uvs( j ) == vn )
				{
					pn += points( j );
					pnc += 1.f;
				}
			}

			assert( pvc != 0.f );
			assert( pnc != 0.f );

			return
				( pv / pvc ) * (       t ) +
				( pn / pnc ) * ( 1.0 - t );
		}
	}

	// NOTE : uv is not on any edges or coincident with any vertices use wachspress coordinates
	//        the factor of 2 in the denominator cancels out during normalisation

	float ws = 0.f;
	Imath::V3f p( 0.f );

	for( int i = 0; i < n; ++i )
	{
		const Imath::V2f vi = uvs( i );

		int ip = 0;
		for( int j = 1; j < n; ++j )
		{
			ip = ( i + n - j ) % n;
			if( uvs( ip ) != vi )
				break;
		}

		const float Ad = Ae[ ip ] * Ae[ i ];

		if( Ad != 0.f )
		{
			const float w = Av[ i ] / Ad;
			p += points( i ) * w;
			ws += w;
		}
	}

	if( ws != 0.f )
	{
		p /= ws;
	}

	return p;
}

template< typename PIndexer, typename UVIndexer >
Imath::V3f interpolateNonConvexPolygon( const PIndexer points, const UVIndexer uvs, const Imath::V2f uv, const int n )
{
	// NOTE : a simple triangle fan from any vertex will cover the non convex polygon
	//        the generated triangles may cover areas outside the polygon this is fine

	Imath::V3f p( 0.f );

	for( int i = 2; i < n; ++i )
	{
		const Imath::V2f tuv[ 3 ] =
		{
			uvs( 0 ),
			uvs( i - 1 ),
			uvs( i )
		};

		if( ptInPolygon( [ & tuv ]( const int ii ){ return tuv[ ii ]; }, uv, 3 ) )
		{
			const float w = ( tuv[ 2 ] - tuv[ 0 ] ) % ( tuv[ 1 ] - tuv[ 0 ] );

			if( w == 0.f )
			{
				// NOTE : uv triangle numerically has zero area so is effectively a line or point.
				//        position in 3d space is ambiguous unless the triangle in 3d space is a point.

				const Imath::V3f& vp = points( i - 1 );

				if(
					( ( points( 0 ) - vp ).length2() <= ( 2.f * std::numeric_limits< float >::min() ) ) &&
					( ( points( i ) - vp ).length2() <= ( 2.f * std::numeric_limits< float >::min() ) ) )
				{
					p = vp;

					break;
				}

				throw IECore::InvalidArgumentException( (
					boost::format(
						"Gaffer::Constraint : UV coordinates \"%s\" map to ambiguous point(s) in 3d space."
					) % ( uv ) ).str() );
			}

			// NOTE : ensure that the positive barycentric coordinates sum to a maximum of one.

			const float b0 = std::max( 0.f, ( ( tuv[ 1 ] - tuv[ 2 ] ) % ( uv - tuv[ 1 ] ) ) / w );
			const float b1 = std::max( 0.f, ( ( tuv[ 2 ] - tuv[ 0 ] ) % ( uv - tuv[ 2 ] ) ) / w );
			const float b2 = std::max( 0.f, ( ( tuv[ 0 ] - tuv[ 1 ] ) % ( uv - tuv[ 0 ] ) ) / w );

			const float bs = b0 + b1 + b2;

			if( bs != 0.f )
			{
				p =
					points(     0 ) * ( b0 / bs ) +
					points( i - 1 ) * ( b1 / bs ) +
					points( i     ) * ( b2 / bs );

				break;
			}
		}
	}

	return p;
}

void constructMatrix( Imath::M44f& m, const Imath::V3f& p, const Imath::V3f& x, const Imath::V3f& y, const Imath::V3f& z )
{
	m[ 0 ][ 0 ] = x[ 0 ];
	m[ 0 ][ 1 ] = x[ 1 ];
	m[ 0 ][ 2 ] = x[ 2 ];
	m[ 0 ][ 3 ] = 0.f;

	m[ 1 ][ 0 ] = y[ 0 ];
	m[ 1 ][ 1 ] = y[ 1 ];
	m[ 1 ][ 2 ] = y[ 2 ];
	m[ 1 ][ 3 ] = 0.f;

	m[ 2 ][ 0 ] = z[ 0 ];
	m[ 2 ][ 1 ] = z[ 1 ];
	m[ 2 ][ 2 ] = z[ 2 ];
	m[ 2 ][ 3 ] = 0.f;

	m[ 3 ][ 0 ] = p[ 0 ];
	m[ 3 ][ 1 ] = p[ 1 ];
	m[ 3 ][ 2 ] = p[ 2 ];
	m[ 3 ][ 3 ] = 1.f;
}

void constructLocalFrame( Imath::M44f& m, const Imath::V3f& p, const Imath::V3f& t, const Imath::V3f& b, const Imath::V3f& n )
{
	// NOTE : use tangent and bitangent when both have non zero length and are not colinear
	//        and either the normal has zero length or both the tangent and bitangent are
	//        not colinear with the normal, otherwise use tangent and normal when they both
	//        have non zero length and are not colinear, otherwise use bitangent and normal
	//        when they both have non zero length and are not colinear

	const Imath::V3f nt = t.normalized();
	const Imath::V3f nb = b.normalized();
	const Imath::V3f nn = n.normalized();
	const Imath::V3f bxt = nb % nt;
	const Imath::V3f txn = nt % nn;
	const Imath::V3f nxb = nn % nb;

	if(
		( bxt.length2() > ( 2.f * std::numeric_limits< float >::min() ) ) &&
		( ( n.length2() < ( 2.f * std::numeric_limits< float >::min() ) ) ||
			( ( std::fabs( nt ^ nn ) < 0.999f ) && ( std::fabs( nb ^ nn ) < 0.999f ) ) ) )
	{
		Imath::V3f y = bxt.normalized();

		// NOTE : ensure y axis of local frame points in same direction as normal

		if( n.dot( y ) < 0.f )
		{
			y *= -1.f;
		}

		constructMatrix( m, p, nt, y, ( nt % y ).normalized() );
	}
	else if( txn.length2() > ( 2.f * std::numeric_limits< float >::min() ) )
	{
		const Imath::V3f z = txn.normalized();
		constructMatrix( m, p, nt, ( z % nt ).normalized(), z );
	}
	else if( nxb.length2() > ( 2.f * std::numeric_limits< float >::min() ) )
	{
		const Imath::V3f x = nxb.normalized();
		constructMatrix( m, p, x, ( nb % x ).normalized(), nb );
	}
	else
	{
		m.translate( p );
	}
}

struct UVIndexer
{
	UVIndexer( const IECoreScene::MeshPrimitive& primitive, const std::string& uvSet, const bool throwOnError )
	: m_indices( nullptr )
	, m_view()
	{
		const IECoreScene::PrimitiveVariableMap::const_iterator it = primitive.variables.find( uvSet );

		if(
			( it == primitive.variables.end() ) ||
			( ( *it ).second.data->typeId() != IECore::V2fVectorDataTypeId ) )
		{
			if( throwOnError )
			{
				throw IECore::InvalidArgumentException( (
					boost::format(
						"Gaffer::Constraint : MeshPrimitive has no V2fVectorData primitive variable named \"%s\"."
					) % ( uvSet ) ).str() );
			}
			else
			{
				return;
			}
		}

		// NOTE : for vertex and varying interpolation we need to redirect through the primitive indices

		if(
			( ( *it ).second.interpolation == IECoreScene::PrimitiveVariable::Vertex ) ||
			( ( *it ).second.interpolation == IECoreScene::PrimitiveVariable::Varying ) )
		{
			m_indices = &( primitive.vertexIds()->readable() );
		}
		else if( ( *it ).second.interpolation != IECoreScene::PrimitiveVariable::FaceVarying )
		{
			if( throwOnError )
			{
				throw IECore::InvalidArgumentException( (
					boost::format(
						"Gaffer::Constraint : Primitive variable named \"%s\" has incorrect interpolation, must be either Vertex, Varying or FaceVarying"
					) % ( uvSet ) ).str() );
			}
			else
			{
				return;
			}
		}

		m_view = IECoreScene::PrimitiveVariable::IndexedView< Imath::V2f >( ( *it ).second );
	}

	bool valid() const
	{
		return static_cast< bool >( m_view );
	}

	const Imath::V2f& operator[]( const int i ) const
	{
		assert( valid() );
		const int index = ( m_indices != nullptr ) ? ( ( *m_indices )[ i ] ) : i;
		return ( *m_view )[ index ];
	}

private:

	const std::vector< int >* m_indices;
	std::optional<IECoreScene::PrimitiveVariable::IndexedView<Imath::V2f>> m_view;
};

void computePrimitiveVertexLocalFrame( const IECoreScene::Primitive& primitive, Imath::M44f& m, const int vertexId, const bool throwOnError )
{
	const IECore::V3fVectorData* const pdata = primitive.variableData< IECore::V3fVectorData >( "P" );
	if( pdata == nullptr )
	{
		if( throwOnError )
		{
			throw IECore::InvalidArgumentException( "Gaffer::Contraint : Primitive has no Vertex \"P\" primitive variable." );
		}
		else
		{
			return;
		}
	}
	const IECore::V3fVectorData::ValueType& points = pdata->readable();

	if( ( vertexId < 0 ) || ( vertexId >= static_cast< int >( points.size() ) ) )
	{
		if( throwOnError )
		{
			throw IECore::InvalidArgumentException( (
				boost::format(
					"Gaffer::Constraint : Vertex id \"%d\" is out of range."
				) % ( vertexId ) ).str() );
		}
		else
		{
			return;
		}
	}

	m.translate( points[ vertexId ] );
}

void computeMeshVertexLocalFrame( const IECoreScene::MeshPrimitive& primitive, Imath::M44f& m, const int vertexId, const std::string& uvSet, const bool throwOnError, const IECore::Canceller* const canceller )
{
	const IECore::V3fVectorData* const pdata = primitive.variableData< IECore::V3fVectorData >( "P" );

	if( pdata == nullptr )
	{
		if( throwOnError )
		{
			throw IECore::InvalidArgumentException( "Gaffer::Contraint : MeshPrimitive has no Vertex \"P\" primitive variable." );
		}
		else
		{
			return;
		}
	}

	const IECore::V3fVectorData::ValueType& points = pdata->readable();

	if( ( vertexId < 0 ) || ( vertexId >= static_cast< int >( points.size() ) ) )
	{
		if( throwOnError )
		{
			throw IECore::InvalidArgumentException( (
				boost::format(
					"Gaffer::Constraint : Vertex id \"%d\" is out of range."
				) % ( vertexId ) ).str() );
		}
		else
		{
			return;
		}
	}

	const IECore::IntVectorData::ValueType& indices = primitive.vertexIds()->readable();
	const IECore::IntVectorData::ValueType& faces = primitive.verticesPerFace()->readable();

	const UVIndexer uvs( primitive, uvSet, throwOnError );

	Imath::V3f t( 0.f );
	Imath::V3f b( 0.f );
	Imath::V3f n( 0.f );

	if( uvs.valid() )
	{
		int js = 0;
		for( int i = 0; i < static_cast< int >( faces.size() ); ++i )
		{
			const int ni = faces[ i ];

			// canceller support

			if( ( i % 100 ) == 0 )
			{
				IECore::Canceller::check( canceller );
			}

			// find matching face vertex index

			int jm = 0;
			for( ; jm < ni; ++jm )
			{
				if( indices[ js + jm ] == vertexId )
				{
					break;
				}
			}

			if( jm < ni )
			{
				// compute face normal, tangent and bitangent

				Imath::V3f ft( 0.f );
				Imath::V3f fb( 0.f );
				Imath::V3f fn( 0.f );

				float w = 0.f;

				for( int j = 0; j < ni; ++j )
				{
					const int iv = js + j;
					const int ip = js + ( ( j + ni - 1 ) % ni );
					const int in = js + ( ( j      + 1 ) % ni );

					const Imath::V3f pv = points[ indices[ iv ] ];
					const Imath::V3f v0 = points[ indices[ ip ] ] - pv;
					const Imath::V3f v2 = points[ indices[ in ] ] - pv;

					const Imath::V2f uv = uvs[ iv ];
					const Imath::V2f e0 = uvs[ ip ] - uv;
					const Imath::V2f e2 = uvs[ in ] - uv;

					ft += ( v0 * -e2.y + v2 * e0.y ).normalized();
					fb += ( v0 * -e2.x + v2 * e0.x ).normalized();
					fn += ( -v0 % v2 ).normalized();

					if( j == jm )
					{
						const float lv2 = v0.length2() * v2.length2();

						if( lv2 > ( 2.f * std::numeric_limits< float >::min() ) )
						{
							const float lv = std::sqrt( lv2 );

							if( lv != 0.f )
							{
								w = std::acos( std::min( std::max( ( v0 ^ v2 ) / lv, -1.f ), 1.f ) );
							}
						}
					}
				}

				// accumulate angle weighted normal, tangent and bitangent

				t += ft.normalized() * w;
				b += fb.normalized() * w;
				n += fn.normalized() * w;
			}

			js += ni;
		}
	}

	constructLocalFrame( m, points[ vertexId ], t, b, n );
}

void computeVertexLocalFrame( const IECore::Object& object, Imath::M44f& m, const int vertexId, const std::string& uvSet, const bool throwOnError, const IECore::Canceller* const canceller )
{
	switch( static_cast< IECoreScene::TypeId >( object.typeId() ) )
	{
		case IECoreScene::CurvesPrimitiveTypeId:
		case IECoreScene::PointsPrimitiveTypeId:
			computePrimitiveVertexLocalFrame( static_cast< const IECoreScene::Primitive& >( object ), m, vertexId, throwOnError );
			break;
		case IECoreScene::MeshPrimitiveTypeId:
			computeMeshVertexLocalFrame( static_cast< const IECoreScene::MeshPrimitive& >( object ), m, vertexId, uvSet, throwOnError, canceller );
			break;
		default:
			if( throwOnError )
			{
				throw IECore::InvalidArgumentException( (
					boost::format(
						"Gaffer::Constraint : Target primitive of type \"%s\" is not supported in Vertex target mode."
					) % ( object.typeName() ) ).str() );
			}
			break;
	}
}

void computeMeshUVLocalFrame( const IECoreScene::MeshPrimitive& primitive, Imath::M44f& m, const Imath::V2f& uv, const std::string& uvSet, const bool throwOnError, const IECore::Canceller* const canceller )
{
	const IECore::V3fVectorData* const pdata = primitive.variableData< IECore::V3fVectorData >( "P" );

	if( pdata == nullptr )
	{
		if( throwOnError )
		{
			throw IECore::InvalidArgumentException( "Gaffer::Contraint : MeshPrimitive has no Vertex \"P\" primitive variable." );
		}
		else
		{
			return;
		}
	}

	const IECore::V3fVectorData::ValueType& points = pdata->readable();
	const IECore::IntVectorData::ValueType& indices = primitive.vertexIds()->readable();
	const IECore::IntVectorData::ValueType& faces = primitive.verticesPerFace()->readable();

	const UVIndexer uvs( primitive, uvSet, throwOnError );
	if( ! uvs.valid() )
	{
		return;
	}

	int js = 0;
	for( int i = 0; i < static_cast< int >( faces.size() ); ++i )
	{
		const int ni = faces[ i ];

		// canceller support

		if( ( i % 100 ) == 0 )
		{
			IECore::Canceller::check( canceller );
		}

		// points and uv indexers

		const auto pIndexer = [ & points, & indices, js ]( const int ii )
		{
			return points[ indices[ js + ii ] ];
		};

		const auto uvIndexer = [ & uvs, js ]( const int ii )
		{
			return uvs[ js + ii ];
		};

		// determine if uv coordinate is inside face

		if( ptInPolygon( uvIndexer, uv, ni ) )
		{
			Imath::V3f fp( 0.f );
			Imath::V3f ft( 0.f );
			Imath::V3f fb( 0.f );
			Imath::V3f fn( 0.f );

			try
			{
				fp = ( convexPolygon( uvIndexer, ni ) )
					? interpolateConvexPolygon( pIndexer, uvIndexer, uv, ni )
					: interpolateNonConvexPolygon( pIndexer, uvIndexer, uv, ni );

				// compute face tangent and bitangent

				for( int j = 0; j < ni; ++j )
				{
					const int iv = js + j;
					const int ip = js + ( ( j + ni - 1 ) % ni );
					const int in = js + ( ( j      + 1 ) % ni );

					const Imath::V3f pv = points[ indices[ iv ] ];
					const Imath::V3f v0 = points[ indices[ ip ] ] - pv;
					const Imath::V3f v2 = points[ indices[ in ] ] - pv;

					const Imath::V2f uv = uvs[ iv ];
					const Imath::V2f e0 = uvs[ ip ] - uv;
					const Imath::V2f e2 = uvs[ in ] - uv;

					ft += ( v0 * -e2.y + v2 * e0.y ).normalized();
					fb += ( v0 * -e2.x + v2 * e0.x ).normalized();
					fn += ( -v0 % v2 ).normalized();
				}
			}
			catch( const IECore::Exception& e )
			{
				if( throwOnError )
				{
					throw;
				}
			}

			constructLocalFrame( m, fp, ft, fb, fn );

			return;
		}

		js += ni;
	}

	if( throwOnError )
	{
		throw IECore::InvalidArgumentException( (
			boost::format(
				"Gaffer::Constraint : UV coordinates \"%s\" are out of range."
			) % ( uv ) ).str() );
	}
}

void computeUVLocalFrame( const IECore::Object& object, Imath::M44f& m, const Imath::V2f& uv, const std::string& uvSet, const bool throwOnError, const IECore::Canceller* const canceller )
{
	switch( static_cast< IECoreScene::TypeId >( object.typeId() ) )
	{
		case IECoreScene::MeshPrimitiveTypeId:
			computeMeshUVLocalFrame( static_cast< const IECoreScene::MeshPrimitive& >( object ), m, uv, uvSet, throwOnError, canceller );
			break;
		default:
			if( throwOnError )
			{
				throw IECore::InvalidArgumentException( (
					boost::format(
						"Gaffer::Constraint : Target primitive of type \"%s\" is not supported in UV target mode."
					) % ( object.typeName() ) ).str() );
			}
			break;
	}
}

} // namespace

GAFFER_NODE_DEFINE_TYPE( Constraint );

size_t Constraint::g_firstPlugIndex = 0;

Constraint::Constraint( const std::string &name )
	:	SceneElementProcessor( name, IECore::PathMatcher::NoMatch )
{
	storeIndexOfNextChild( g_firstPlugIndex );
	addChild( new ScenePlug( "targetScene" ) );
	addChild( new StringPlug( "target" ) );
	addChild( new BoolPlug( "ignoreMissingTarget" ) );
	addChild( new IntPlug( "targetMode", Plug::In, Constraint::Origin, Constraint::Origin, Constraint::Vertex ) );
	addChild( new V2fPlug( "targetUV" ) );
	addChild( new IntPlug( "targetVertex", Plug::In, 0, 0 ) );
	addChild( new V3fPlug( "targetOffset" ) );

	// Pass through things we don't want to modify
	outPlug()->attributesPlug()->setInput( inPlug()->attributesPlug() );
	outPlug()->objectPlug()->setInput( inPlug()->objectPlug() );
}

Constraint::~Constraint()
{
}

ScenePlug *Constraint::targetScenePlug()
{
	return getChild<ScenePlug>( g_firstPlugIndex );
}

const ScenePlug *Constraint::targetScenePlug() const
{
	return getChild<ScenePlug>( g_firstPlugIndex );
}

Gaffer::StringPlug *Constraint::targetPlug()
{
	return getChild<Gaffer::StringPlug>( g_firstPlugIndex + 1 );
}

const Gaffer::StringPlug *Constraint::targetPlug() const
{
	return getChild<Gaffer::StringPlug>( g_firstPlugIndex + 1 );
}

Gaffer::BoolPlug *Constraint::ignoreMissingTargetPlug()
{
	return getChild<Gaffer::BoolPlug>( g_firstPlugIndex + 2 );
}

const Gaffer::BoolPlug *Constraint::ignoreMissingTargetPlug() const
{
	return getChild<Gaffer::BoolPlug>( g_firstPlugIndex + 2 );
}

Gaffer::IntPlug *Constraint::targetModePlug()
{
	return getChild<Gaffer::IntPlug>( g_firstPlugIndex + 3 );
}

const Gaffer::IntPlug *Constraint::targetModePlug() const
{
	return getChild<Gaffer::IntPlug>( g_firstPlugIndex + 3 );
}

Gaffer::V2fPlug *Constraint::targetUVPlug()
{
	return getChild<Gaffer::V2fPlug>( g_firstPlugIndex + 4 );
}

const Gaffer::V2fPlug *Constraint::targetUVPlug() const
{
	return getChild<Gaffer::V2fPlug>( g_firstPlugIndex + 4 );
}

Gaffer::IntPlug *Constraint::targetVertexPlug()
{
	return getChild<Gaffer::IntPlug>( g_firstPlugIndex + 5 );
}

const Gaffer::IntPlug *Constraint::targetVertexPlug() const
{
	return getChild<Gaffer::IntPlug>( g_firstPlugIndex + 5 );
}

Gaffer::V3fPlug *Constraint::targetOffsetPlug()
{
	return getChild<Gaffer::V3fPlug>( g_firstPlugIndex + 6 );
}

const Gaffer::V3fPlug *Constraint::targetOffsetPlug() const
{
	return getChild<Gaffer::V3fPlug>( g_firstPlugIndex + 6 );
}

void Constraint::affects( const Gaffer::Plug *input, AffectedPlugsContainer &outputs ) const
{
	SceneElementProcessor::affects( input, outputs );

	if(
		input == targetPlug() ||
		input == ignoreMissingTargetPlug() ||
		input == inPlug()->existsPlug() ||
		input == inPlug()->transformPlug() ||
		input == inPlug()->boundPlug() ||
		input == targetScenePlug()->existsPlug() ||
		input == targetScenePlug()->transformPlug() ||
		input == targetScenePlug()->boundPlug() ||
		input == targetScenePlug()->objectPlug() ||
		input == targetModePlug() ||
		input->parent<Plug>() == targetOffsetPlug() ||
		input->parent<Plug>() == targetUVPlug() ||
		input == targetVertexPlug() ||
		// TypeId comparison is necessary to avoid calling pure virtual
		// if we're called before being fully constructed.
		( typeId() != staticTypeId() && affectsConstraint( input ) )
	)
	{
		outputs.push_back( outPlug()->transformPlug() );
		outputs.push_back( outPlug()->boundPlug() );
	}
}

bool Constraint::processesTransform() const
{
	return true;
}

void Constraint::hashProcessedTransform( const ScenePath &path, const Gaffer::Context *context, IECore::MurmurHash &h ) const
{
	auto targetOpt = target();
	if( !targetOpt )
	{
		// Pass through input unchanged
		h = inPlug()->transformPlug()->hash();
		return;
	}

	ScenePath parentPath = path;
	parentPath.pop_back();
	h.append( inPlug()->fullTransformHash( parentPath ) );

	h.append( targetOpt->scene->fullTransformHash( targetOpt->path ) );

	const TargetMode targetMode = (TargetMode)targetModePlug()->getValue();
	h.append( targetMode );
	switch( targetMode )
	{
		case Constraint::BoundMin:
		case Constraint::BoundMax:
		case Constraint::BoundCenter:
			h.append( targetOpt->scene->boundHash( targetOpt->path ) );
			break;
		case Constraint::UV:
			h.append( targetOpt->scene->objectHash( targetOpt->path ) );
			ignoreMissingTargetPlug()->hash( h );
			targetUVPlug()->hash( h );
			break;
		case Constraint::Vertex:
			h.append( targetOpt->scene->objectHash( targetOpt->path ) );
			ignoreMissingTargetPlug()->hash( h );
			targetVertexPlug()->hash( h );
			break;
		default:
			break;
	}

	targetOffsetPlug()->hash( h );

	hashConstraint( context, h );
}

Imath::M44f Constraint::computeProcessedTransform( const ScenePath &path, const Gaffer::Context *context, const Imath::M44f &inputTransform ) const
{
	auto targetOpt = target();
	if( !targetOpt )
	{
		return inputTransform;
	}

	ScenePath parentPath = path;
	parentPath.pop_back();

	const M44f parentTransform = inPlug()->fullTransform( parentPath );
	const M44f fullInputTransform = inputTransform * parentTransform;

	M44f fullTargetTransform = targetOpt->scene->fullTransform( targetOpt->path );

	const TargetMode targetMode = (TargetMode)targetModePlug()->getValue();

	switch( targetMode )
	{
		case Constraint::BoundMin:
		{
			const Box3f targetBound = targetOpt->scene->bound( targetOpt->path );
			if( ! targetBound.isEmpty() )
			{
				fullTargetTransform.translate( targetBound.min );
			}
			break;
		}
		case Constraint::BoundMax:
		{
			const Box3f targetBound = targetOpt->scene->bound( targetOpt->path );
			if( ! targetBound.isEmpty() )
			{
				fullTargetTransform.translate( targetBound.max );
			}
			break;
		}
		case Constraint::BoundCenter:
		{
			const Box3f targetBound = targetOpt->scene->bound( targetOpt->path );
			if( ! targetBound.isEmpty() )
			{
				fullTargetTransform.translate( targetBound.center() );
			}
			break;
		}
		case Constraint::UV:
		{
			const IECore::ConstObjectPtr object = targetOpt->scene->object( targetOpt->path );
			Imath::M44f surfaceTransform;
			const Imath::V2f uv = targetUVPlug()->getValue();
			const bool throwOnError = !( ignoreMissingTargetPlug()->getValue() );
			computeUVLocalFrame( *object, surfaceTransform, uv, "uv", throwOnError, context->canceller() );
			fullTargetTransform = surfaceTransform * fullTargetTransform;
			break;
		}
		case Constraint::Vertex:
		{
			const IECore::ConstObjectPtr object = targetOpt->scene->object( targetOpt->path );
			Imath::M44f surfaceTransform;
			const int vertexId = targetVertexPlug()->getValue();
			const bool throwOnError = !( ignoreMissingTargetPlug()->getValue() );
			computeVertexLocalFrame( *object, surfaceTransform, vertexId, "uv", throwOnError, context->canceller() );
			fullTargetTransform = surfaceTransform * fullTargetTransform;
			break;
		}
		default:
			break;
	}

	fullTargetTransform.translate( targetOffsetPlug()->getValue() );

	const M44f fullConstrainedTransform = computeConstraint( fullTargetTransform, fullInputTransform, inputTransform );
	return fullConstrainedTransform * parentTransform.inverse();
}

std::optional<Constraint::Target> Constraint::target() const
{
	std::string targetPathAsString = targetPlug()->getValue();
	if( targetPathAsString == "" )
	{
		return std::nullopt;
	}

	ScenePath targetPath;
	ScenePlug::stringToPath( targetPathAsString, targetPath );

	const ScenePlug *targetScene = targetScenePlug();
	if( !targetScene->getInput() )
	{
		// Backwards compatibility for time when there was
		// no `targetScene` plug.
		targetScene = inPlug();
	}

	if( !targetScene->exists( targetPath ) )
	{
		if( ignoreMissingTargetPlug()->getValue() )
		{
			return std::nullopt;
		}
		else
		{
			throw IECore::Exception( boost::str(
				boost::format( "Constraint target does not exist: \"%s\".  Use 'ignoreMissingTarget' option if you want to just skip this constraint" ) % targetPathAsString ) );
		}
	}

	return Target( { targetPath, targetScene } );
}


#ifndef ZOBRIST_HASH_HPP_
#define ZOBRIST_HASH_HPP_

#include <type_traits>
#include <boost/assert.hpp>

#include "Board.hpp"
#include "Point.hpp"
#include "util/define.hpp"
#include "util/Random.hpp"

namespace slide
{

template<int H, int W>
class FixedZobristTableVariables
{
protected:
    ull table[MAX_DIVISION_NUM * MAX_DIVISION_NUM][H][W];    // [id][pos.y][pos.x]
    ull selectedTable[MAX_DIVISION_NUM * MAX_DIVISION_NUM];  // [id]

    FixedZobristTableVariables() = default;
    FixedZobristTableVariables(const FixedZobristTableVariables&) = default;

    constexpr int height() const {
        return H;
    }
    constexpr int width() const {
        return W;
    }

#ifdef NDEBUG
    void resize(int, int) {}
#else
    void resize(int h, int w) {
    	BOOST_ASSERT(h == H && w == W);
    }
#endif
};

class FlexibleZobristTableVariables
{
protected:
    int h, w;
    ull table[MAX_DIVISION_NUM * MAX_DIVISION_NUM][MAX_DIVISION_NUM][MAX_DIVISION_NUM];
    ull selectedTable[MAX_DIVISION_NUM * MAX_DIVISION_NUM];

    FlexibleZobristTableVariables() = default;
    FlexibleZobristTableVariables(const FlexibleZobristTableVariables&) = default;
    constexpr FlexibleZobristTableVariables(int h, int w) : h(h), w(w), table(), selectedTable() {}

public:
    constexpr int height() const {
        return h;
    }
    constexpr int width() const {
        return w;
    }
    void resize(int h, int w) {
        this->h = h;
        this->w = w;
    }
};

template<int H, int W>
using ZobristTableVariables = typename std::conditional<H != Flexible, FixedZobristTableVariables<H, W>, FlexibleZobristTableVariables>::type;

template<int _H, int _W = _H>
class ZobristTable : ZobristTableVariables<_H, _W>
{
private:
    using Base = ZobristTableVariables<_H, _W>;
    using Base::table;
    using Base::selectedTable;

public:
    static constexpr int H = _H;
    static constexpr int W = _W;

    using Base::height;
    using Base::width;

    ZobristTable(){
        static_assert((H != Flexible && W != Flexible) || (H == Flexible && W == Flexible),
            "Both of H and W must be Flexible or must be non-Flexible simultaniously!");
        init();
    };

    ZobristTable(int h, int w) {
        init(h, w);
    }

    void init()
    {
        rep(i, MAX_DIVISION_NUM * MAX_DIVISION_NUM) rep(y, height()) rep(x, width()){
            table[i][y][x] = util::Random::nextULL();
        }
        rep(i, MAX_DIVISION_NUM * MAX_DIVISION_NUM){
        	selectedTable[i] = util::Random::nextULL();
        }
    }

    void init(int h, int w)
    {
        Base::resize(h, w);
        init();
    }

    ull look(uchar id, Point pos) const
    {
        BOOST_ASSERT(Point(id).isIn(height(), width()));
        BOOST_ASSERT(pos.isIn(height(), width()));

        return table[id][pos.y][pos.x];
    }

    ull lookSelected(uchar id) const
    {
        BOOST_ASSERT(Point(id).isIn(H, W));
        return selectedTable[id];
    }

    ull& look(uchar id, Point pos)
    {
        BOOST_ASSERT(Point(id).isIn(height(), width()));
        BOOST_ASSERT(pos.isIn(height(), width()));

        return table[id][pos.y][pos.x];
    }

    ull& lookSelected(uchar id)
    {
        BOOST_ASSERT(Point(id).isIn(H, W));
        return selectedTable[id];
    }
};

} // end of namespace slide

#endif


#include "libmovavg.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <boost/program_options.hpp>
namespace po = boost::program_options;

std::vector<std::string> split(std::string s, std::string delimiter)
{
    std::vector<std::string> v;

    size_t pos = 0;
    std::string token;
    while ((pos = s.find(delimiter)) != std::string::npos)
    {
        token = s.substr(0, pos);
        v.push_back(token);
        s.erase(0, pos + delimiter.length());
    }
    v.push_back(s);
    return v;
}

void loopLine(std::istream &f, const size_t mva_len, const std::string colDelim, const size_t colPos, bool showErr, const std::string nullstr)
{

    std::string line;

    Moving_Average<double, double> ma;
    ma.setSizeOf(mva_len);

    while (getline(f, line))
    {
        try
        {
            ma(std::stod(split(line, ",").at(colPos)));
            if (ma.isNull())
            {
                std::cout << line << colDelim << nullstr << '\n';
            }
            else
            {
                std::cout << line << colDelim << ma << '\n';
            }
        }
        catch (const std::exception &e)
        {
            if (showErr)
            {
                std::cout << e.what() << std::endl;
            }
            else
            {
                if (ma.isNull())
                {
                    std::cout << line << colDelim << nullstr << '\n';
                }
                else
                {
                    std::cout << line << colDelim << ma << '\n';
                }
            }
        }
    }
    if (f.bad())
        perror("error while reading file");
}

int main(int ac, char *av[])
{

    try
    {

        po::options_description desc("Allowed options");
        desc.add_options()("help,h", "produce help message")("showErr,e", "show errors")
                ("len,l", po::value<size_t>(), "set moving avg len, 0 for none moving, default 7")
                ("coldelim,d", po::value<std::string>(), "set column delimiter, default ',' ")
                ("colnum,n", po::value<size_t>(), "set column number, default 0")
                ("nullstr", po::value<std::string>(), "set null string, default 'null' ");

        po::variables_map vm;
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);

        if (vm.count("help"))
        {
            std::cout << desc << "\n";
            return 0;
        }

        bool showErr = false;
        if (vm.count("showErr"))
        {
            showErr = true;
        }

        size_t len = 7;
        if (vm.count("len"))
        {
            len = vm["len"].as<size_t>();
        }
        std::string coldelim = ",";
        if (vm.count("coldelim"))
        {
            coldelim = vm["coldelim"].as<std::string>();
        }
        size_t colnum = 0;
        if (vm.count("colnum"))
        {
            colnum = vm["colnum"].as<size_t>();
        }
        std::string nullstr = "null";
        if (vm.count("nullstr"))
        {
            coldelim = vm["nullstr"].as<std::string>();
        }

        loopLine(std::cin, len, coldelim, colnum, showErr, nullstr);
    }
    catch (std::exception &e)
    {
        std::cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch (...)
    {
        std::cerr << "Exception of unknown type!\n";
    }

    return 0;
}


#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>


/*
 * (C) Copyright 2017-2019 UCAR
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 */

#include "ioda/distribution/Halo.h"

#include <algorithm>
#include <iostream>
#include <numeric>
#include <set>
#include <vector>

#include <boost/make_unique.hpp>

#include "oops/mpi/mpi.h"
#include "oops/util/DateTime.h"
#include "oops/util/Logger.h"

#include "ioda/distribution/DistributionFactory.h"
#include "ioda/distribution/GeneralDistributionAccumulator.h"
#include "eckit/config/LocalConfiguration.h"
#include "eckit/exception/Exceptions.h"

namespace ioda {

// -----------------------------------------------------------------------------
static DistributionMaker<Halo> maker("Halo");

// -----------------------------------------------------------------------------
/*!
 * \brief Halo selector
 *
 * \details This distribution puts observations within a halo on the same
 *          processor
 *
 *
 */
// -----------------------------------------------------------------------------
Halo::Halo(const eckit::mpi::Comm & Comm,
           const eckit::Configuration & config) :
          Distribution(Comm) {
  // extract center point from configuration
  // if no patch center defined, distribute centers equi-distant along the equator
  std::vector<double> centerd(2, 0.0);
  if (config.has("center")) {
    centerd = config.getDoubleVector("center", centerd);
  } else {
    centerd[0] = static_cast<double>(comm_.rank())*
                             (360.0/static_cast<double>(comm_.size()));
    centerd[1] = 0.0;
  }
  eckit::geometry::Point2 center(centerd[0], centerd[1]);
  center_ = center;

  // assign radius that is a sum of the patch and localization radii
  // (1) this radius is the radius of the "patch"
  //     if not specified, use radius big enough to encompass all obs. on Earth
  double radius = config.getDouble("radius", 50000000.0);

  // (2) add localization radius (i.e. the "halo" radius)
  double locRadius = config.getDouble("obs localization.lengthscale", 0.0);
  radius += locRadius;

  radius_ = radius;

  oops::Log::debug() << "Halo constructed: center: " << center_ << " radius: "
                     << radius_ << std::endl;
}

// -----------------------------------------------------------------------------
Halo::~Halo() {
  oops::Log::trace() << "Halo destructed" << std::endl;
}

// -----------------------------------------------------------------------------
void Halo::assignRecord(const std::size_t RecNum, const std::size_t LocNum,
                        const eckit::geometry::Point2 & point) {
  if (recordsOutsideHalo_.find(RecNum) != recordsOutsideHalo_.end()) {
    // We've already seen the first location in this record, and it was too far away from center_.
    return;
  }

  if (recordsInHalo_.find(RecNum) == recordsInHalo_.end()) {
    // This is the first location from this record. Find out whether to assign it to this PE.

    const double dist = eckit::geometry::Sphere::distance(radius_earth_, center_, point);
    oops::Log::debug() << "Point: " << point << " distance to center: " << center_
          << " = " << dist << std::endl;
    if (dist <= radius_) {
      // Yes!
      recordsInHalo_.insert(RecNum);
      recordDistancesFromCenter_[RecNum] = dist;
    } else {
      // No, it's too far from center_.
      recordsOutsideHalo_.insert(RecNum);
      return;
    }
  }

  // Now we know this record has been assigned to this PE. Store information about location LocNum.
  haloLocVector_.push_back(LocNum);
  haloLocRecords_.push_back(RecNum);
}

// -----------------------------------------------------------------------------
bool Halo::isMyRecord(std::size_t RecNum) const {
    return (recordsInHalo_.count(RecNum) > 0);
}

// -----------------------------------------------------------------------------
void Halo::computePatchLocs() {
  // define some constants for this PE
  double inf = std::numeric_limits<double>::infinity();
  size_t myRank = comm_.rank();

  // All records have now been assigned, so this container is no longer needed.
  recordsOutsideHalo_.clear();

  // Find the maximum global location index (plus 1)
  size_t nglocs = 0;
  if (!haloLocVector_.empty())
    nglocs = haloLocVector_.back() + 1;
  comm_.allReduceInPlace(nglocs, eckit::mpi::max());

  if ( nglocs > 0 ) {
    // make structures holding pairs of {distance,rank} for reduce operation later
    // initialize the local array to dist == inf
    std::vector<std::pair<double, int>> dist_and_lidx_loc(nglocs);
    std::vector<std::pair<double, int>> dist_and_lidx_glb(nglocs);
    for ( size_t jj = 0; jj < nglocs; ++jj ) {
      dist_and_lidx_loc[jj] = std::make_pair(inf, myRank);
    }

    // assign actual distances to local obs (stored in haloLocVector_)
    for (size_t loc = 0; loc < haloLocVector_.size(); ++loc) {
      const size_t gloc = haloLocVector_[loc];
      const size_t recNum = haloLocRecords_[loc];
      const double dist = recordDistancesFromCenter_.at(recNum);
      dist_and_lidx_loc[gloc] = std::make_pair(dist, myRank);
    }

    // use reduce operation to find PE rank with minimal distance
    comm_.allReduce(dist_and_lidx_loc, dist_and_lidx_glb, eckit::mpi::minloc());

    // ids of patch observations owned by this PE
    std::unordered_set<std::size_t> patchObsLoc;

    // if this PE has the minimum distance then this PE owns this ob. as patch
    for (auto i : haloLocVector_) {
      if ( dist_and_lidx_glb[i].second == myRank ) {
        patchObsLoc.insert(i);
      }
    }

    // convert storage from unodered sets to a bool vector
    for (size_t jj = 0; jj < haloLocVector_.size(); ++jj) {
      if ( patchObsLoc.count(haloLocVector_[jj]) ) {
        patchObsBool_.push_back(true);
      } else {
        patchObsBool_.push_back(false);
      }
    }

    // now that we have patchObsBool_ computed we can free memory occupied by some temp objects
    recordDistancesFromCenter_.clear();
    haloLocRecords_.clear();
    haloLocRecords_.shrink_to_fit();

    computeGlobalUniqueConsecutiveLocIndices(dist_and_lidx_glb);

    // and now the remaining temp object
    haloLocVector_.clear();
    haloLocVector_.shrink_to_fit();
  }
}

// -----------------------------------------------------------------------------
void Halo::computeGlobalUniqueConsecutiveLocIndices(
    const std::vector<std::pair<double, int>> &dist_and_lidx_glb) {
  const double inf = std::numeric_limits<double>::infinity();

  globalUniqueConsecutiveLocIndices_.reserve(haloLocVector_.size());

  // Step 0: enable quick checks of whether a location belongs to this rank's halo.
  std::unordered_set<size_t> haloLocSet(haloLocVector_.begin(), haloLocVector_.end());

  // Step 1: index patch observations owned by each rank consecutively (starting from 0 on each
  // rank). For each observation i held on this rank, set globalConsecutiveLocIndices_[i] to
  // the index of the corresponding patch observation on the rank that owns it.
  std::vector<size_t> patchObsCountOnRank(comm_.size(), 0);
  for (size_t gloc = 0, nglocs = dist_and_lidx_glb.size(); gloc < nglocs; ++gloc) {
    if (dist_and_lidx_glb[gloc].first < inf) {
      // This obs is held on at least one rank (this won't be the case e.g. for observations
      // outside the assimilation window)
      const size_t rankOwningPatchObs = dist_and_lidx_glb[gloc].second;
      if (haloLocSet.find(gloc) != haloLocSet.end()) {
        // This obs is held on the current PE (but not necessarily as a patch obs)
        globalUniqueConsecutiveLocIndices_.push_back(patchObsCountOnRank[rankOwningPatchObs]);
      }
      ++patchObsCountOnRank[rankOwningPatchObs];
    }
  }

  // Step 2: make indices of patch observations globally unique by incrementing the index
  // of each patch observation held by rank r by the total number of patch observations owned
  // by ranks r' < r.

  // Perform an exclusive scan
  std::vector<size_t> patchObsCountOnPreviousRanks(patchObsCountOnRank.size(), 0);
  for (size_t rank = 1; rank < patchObsCountOnRank.size(); ++rank) {
    patchObsCountOnPreviousRanks[rank] =
        patchObsCountOnPreviousRanks[rank - 1] + patchObsCountOnRank[rank - 1];
  }

  // Increment patch observation indices
  for (size_t loc = 0; loc < globalUniqueConsecutiveLocIndices_.size(); ++loc) {
    const size_t gloc = haloLocVector_[loc];
    const size_t rankOwningPatchObs = dist_and_lidx_glb[gloc].second;
    globalUniqueConsecutiveLocIndices_[loc] += patchObsCountOnPreviousRanks[rankOwningPatchObs];
  }
}

// -----------------------------------------------------------------------------
void Halo::patchObs(std::vector<bool> & patchObsVec) const {
  patchObsVec = patchObsBool_;
}

// -----------------------------------------------------------------------------
void Halo::min(int & x) const {
  minImpl(x);
}

void Halo::min(std::size_t & x) const {
  minImpl(x);
}

void Halo::min(float & x) const {
  minImpl(x);
}

void Halo::min(double & x) const {
  minImpl(x);
}

void Halo::min(std::vector<int> & x) const {
  minImpl(x);
}

void Halo::min(std::vector<std::size_t> & x) const {
  minImpl(x);
}

void Halo::min(std::vector<float> & x) const {
  minImpl(x);
}

void Halo::min(std::vector<double> & x) const {
  minImpl(x);
}

template <typename T>
void Halo::minImpl(T & x) const {
  reductionImpl(x, eckit::mpi::min());
}

// -----------------------------------------------------------------------------
void Halo::max(int & x) const {
  maxImpl(x);
}

void Halo::max(std::size_t & x) const {
  maxImpl(x);
}

void Halo::max(float & x) const {
  maxImpl(x);
}

void Halo::max(double & x) const {
  maxImpl(x);
}

void Halo::max(std::vector<int> & x) const {
  maxImpl(x);
}

void Halo::max(std::vector<std::size_t> & x) const {
  maxImpl(x);
}

void Halo::max(std::vector<float> & x) const {
  maxImpl(x);
}

void Halo::max(std::vector<double> & x) const {
  maxImpl(x);
}

template <typename T>
void Halo::maxImpl(T & x) const {
  reductionImpl(x, eckit::mpi::max());
}

// -----------------------------------------------------------------------------
template <typename T>
void Halo::reductionImpl(T & x, eckit::mpi::Operation::Code op) const {
  comm_.allReduceInPlace(x, op);
}

template <typename T>
void Halo::reductionImpl(std::vector<T> & x, eckit::mpi::Operation::Code op) const {
  comm_.allReduceInPlace(x.begin(), x.end(), op);
}

// -----------------------------------------------------------------------------
std::unique_ptr<Accumulator<int>>
Halo::createAccumulatorImpl(int init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<std::size_t>>
Halo::createAccumulatorImpl(std::size_t init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<float>>
Halo::createAccumulatorImpl(float init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<double>>
Halo::createAccumulatorImpl(double init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<std::vector<int>>>
Halo::createAccumulatorImpl(const std::vector<int> &init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<std::vector<std::size_t>>>
Halo::createAccumulatorImpl(const std::vector<std::size_t> &init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<std::vector<float>>>
Halo::createAccumulatorImpl(const std::vector<float> &init) const {
  return createAccumulatorImplT(init);
}

std::unique_ptr<Accumulator<std::vector<double>>>
Halo::createAccumulatorImpl(const std::vector<double> &init) const {
  return createAccumulatorImplT(init);
}

template <typename T>
std::unique_ptr<Accumulator<T>>
Halo::createAccumulatorImplT(const T &init) const {
  return boost::make_unique<GeneralDistributionAccumulator<T>>(init, comm_, patchObsBool_);
}

// -----------------------------------------------------------------------------
void Halo::allGatherv(std::vector<size_t> &x) const {
  allGathervImpl(x);
}

void Halo::allGatherv(std::vector<int> &x) const {
  allGathervImpl(x);
}

void Halo::allGatherv(std::vector<float> &x) const {
  allGathervImpl(x);
}

void Halo::allGatherv(std::vector<double> &x) const {
  allGathervImpl(x);
}

/// overloading for util::DateTime
void Halo::allGatherv(std::vector<util::DateTime> &x) const {
  allGathervImpl(x);
}

/// overloading for std::string
void Halo::allGatherv(std::vector<std::string> &x) const {
  allGathervImpl(x);
}

template <typename T>
void Halo::allGathervImpl(std::vector<T> &x) const {
  // operation is only well defined if size x is the size of local obs
  ASSERT(x.size() == patchObsBool_.size());

  // make a temp vector that only holds patch obs.
  std::vector<T> xtmp(std::count(patchObsBool_.begin(), patchObsBool_.end(), true));
  size_t counter = 0;
  for (size_t ii = 0; ii < x.size(); ++ii) {
    if ( patchObsBool_[ii] ) {
      xtmp[counter] = x[ii];
      ++counter;
    }
  }
  // gather all patch obs into a single vector
  oops::mpi::allGatherv(comm_, xtmp);

  // return the result
  x = xtmp;
}

// -----------------------------------------------------------------------------

size_t Halo::globalUniqueConsecutiveLocationIndex(size_t loc) const {
  return globalUniqueConsecutiveLocIndices_[loc];
}

// -----------------------------------------------------------------------------

}  // namespace ioda


/*****************************************************************************
 * Licensed to Qualys, Inc. (QUALYS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * QUALYS licenses this file to You 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.
 ****************************************************************************/

/**
 * @file
 * @brief IronBee --- CLIPP Split Modifier
 *
 * @author Christopher Alfeld <calfeld@qualys.com>
 */

#ifndef __IRONBEE__CLIPP__SPLIT_MODIFIER__
#define __IRONBEE__CLIPP__SPLIT_MODIFIER__

#include <clipp/input.hpp>

#include <boost/shared_ptr.hpp>

namespace IronBee {
namespace CLIPP {

/**
 * Splits data events into multiple data events.
 *
 * First event retains pre-delay and final event gets post-delay.
 * Intermediate events are not delayed.
 **/
class SplitDataModifier
{
public:
    /**
     * Constructor.
     *
     * @param[in] n Number of bytes per data event.
     **/
    explicit
    SplitDataModifier(size_t n = 1);

    //! Process an input.
    bool operator()(Input::input_p& input);

    /**
     * Split with sizes chosen uniformly from [@a min, @a max].
     *
     * @param[in] min Minimum size (except for last).
     * @param[in] max Maximum size.
     **/
    static
    SplitDataModifier uniform(unsigned int min, unsigned int max);

    /**
     * Split with sizes chosen from binomial distribution.
     *
     * n is chosen as the number of successful trials out of @a t where
     * success occurs with probability @a p.
     *
     * @param[in] t Number of trials.
     * @param[in] p Probability of success.
     **/
    static
    SplitDataModifier binomial(unsigned int t, double p);

    /**
     * Split with sizes chosen from geometric distribution.
     *
     * n is chosen as the number trials before a failure of probability
     * 1-@a p.
     *
     * @param[in] p Probability of success.
     **/
    static
    SplitDataModifier geometric(double p);

    /**
     * Split with sizes chosen from poisson distribution.
     *
     * n is chosen from a poisson distribution with mean @a mean.
     *
     * @param[in] mean Mean of distribution.
     **/
    static
    SplitDataModifier poisson(double mean);

public:
    struct State;
    boost::shared_ptr<State> m_state;
};

/**
 * Splits header events into multiple header events.
 *
 * First event retains pre-delay and final event gets post-delay.
 * Intermediate events are not delayed.
 **/
class SplitHeaderModifier
{
public:
    /**
     * Constructor.
     *
     * @param[in] n Number of header lines per header event.
     **/
    explicit
    SplitHeaderModifier(size_t n = 1);

    //! Process an input.
    bool operator()(Input::input_p& input);

    /**
     * Split with sizes chosen uniformly from [@a min, @a max].
     *
     * @param[in] min Minimum size (except for last).
     * @param[in] max Maximum size.
     **/
    static
    SplitHeaderModifier uniform(unsigned int min, unsigned int max);

    /**
     * Split with sizes chosen from binomial distribution.
     *
     * n is chosen as the number of successful trials out of @a t where
     * success occurs with probability @a p.
     *
     * @param[in] t Number of trials.
     * @param[in] p Probability of success.
     **/
    static
    SplitHeaderModifier binomial(unsigned int t, double p);

    /**
     * Split with sizes chosen from geometric distribution.
     *
     * n is chosen as the number trials before a failure of probability
     * 1-@a p.
     *
     * @param[in] p Probability of success.
     **/
    static
    SplitHeaderModifier geometric(double p);

    /**
     * Split with sizes chosen from poisson distribution.
     *
     * n is chosen from a poisson distribution with mean @a mean.
     *
     * @param[in] mean Mean of distribution.
     **/
    static
    SplitHeaderModifier poisson(double mean);

public:
    struct State;
    boost::shared_ptr<State> m_state;
};

} // CLIPP
} // IronBee

#endif


#include <boost/preprocessor/arithmetic/inc.hpp>


/*!
 * @file
 * Defines the @ref Ring typeclass.
 *
 *
 * @copyright Louis Dionne 2014
 * Distributed under the Boost Software License, Version 1.0.
 *         (See accompanying file LICENSE.md or copy at
 *             http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_MPL11_RING_HPP
#define BOOST_MPL11_RING_HPP

#include <boost/mpl11/fwd/ring.hpp>

#include <boost/mpl11/bool.hpp>
#include <boost/mpl11/core.hpp>
#include <boost/mpl11/detail/left_folds/variadic.hpp>

namespace boost { namespace mpl11 {
    template <typename Left, typename Right, typename>
    struct Ring : false_ { };

    template <typename x1, typename x2, typename ...xn>
    struct mult
        : detail::left_folds::variadic<mult, x1, x2, xn...>
    { };

    template <typename x, typename y>
    struct mult<x, y> :
        Ring<
            typename datatype<typename x::type>::type,
            typename datatype<typename y::type>::type
        >::template mult_impl<typename x::type, typename y::type>
    { };

    template <typename Datatype>
    struct one : Ring<Datatype>::template one_impl<> { };
}} // end namespace boost::mpl11

#endif // !BOOST_MPL11_RING_HPP


#include "mesh.hpp"
#include <boost/python.hpp>
#include <stdio.h>
#include <algorithm>
#include "polynomial.hpp"

namespace gd {

using namespace boost::python;
using namespace boost;
using namespace std;

void py_export_mesh() {
	class_<BasisTriMesh1dRegular<> >("BasisTriMesh1dRegular", init<double, double, int>())
		.def("test", (&BasisTriMesh1dRegular<>::test))
		.def("testprofile", (&BasisTriMesh1dRegular<>::testprofile))
		;
	{
		typedef MeshRegularNodal1d<LagrangeBasis<1>> T;
		class_<T>("MeshRegularNodal1dLagrange1", init<double, double, int, Transformation1d_in_3d*>())
			//.def("test", (&BasisTriMesh1dRegular<>::test))
			.def("testprofile", (&T::testprofile))
			.def("get_dof", (&T::get_dof))
			;
	}
	{
		typedef MeshRegularNodal1d<LagrangeBasis<2>> T;
		class_<T>("MeshRegularNodal1dLagrange2", init<double, double, int, Transformation1d_in_3d*>())
			//.def("test", (&BasisTriMesh1dRegular<>::test))
			.def("testprofile", (&T::testprofile))
			.def("get_dof", (&T::get_dof))
			;
	}
	{
		typedef MeshRegularNodal1d<LagrangeBasis<3>> T;
		class_<T>("MeshRegularNodal1dLagrange3", init<double, double, int, Transformation1d_in_3d*>())
			//.def("test", (&BasisTriMesh1dRegular<>::test))
			.def("testprofile", (&T::testprofile))
			.def("get_dof", (&T::get_dof))
			;
	}
}

}


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

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

#include <boost/simd/config.hpp>
#include <boost/simd/detail/dispatch/function/make_callable.hpp>
#include <boost/simd/detail/dispatch/hierarchy/functions.hpp>
#include <boost/simd/detail/dispatch.hpp>

namespace boost { namespace simd
{
  namespace tag
  {
    BOOST_DISPATCH_MAKE_TAG(ext, erfc_, boost::dispatch::elementwise_<erfc_>);
  }

  namespace ext
  {
    BOOST_DISPATCH_FUNCTION_DECLARATION(tag, erfc_);
  }

  BOOST_DISPATCH_CALLABLE_DEFINITION(tag::erfc_,erfc);

} }

#endif


//==================================================================================================
/*!
  @file

  @copyright 2015 NumScale SAS
  @copyright 2015 J.T. Lapreste

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

#include <boost/simd/function/rec.hpp>
#include <boost/dispatch/function/overload.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  BOOST_DISPATCH_OVERLOAD ( rec_
                          , (typename T)
                          , bd::cpu_
                          , bd::generic_<bd::unspecified_<T>>
                          , boost::simd::fast_tag
                          )
  {
    BOOST_FORCEINLINE T operator()(T const& a
                                  , fast_tag const& ) const BOOST_NOEXCEPT
    {
      return rec(a);
    }
  };
} } }

#endif