/*
    libmaus2
    Copyright (C) 2015 German Tischler

    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/>.
*/
#if ! defined(LIBMAUS2_DAZZLER_ALIGN_OVERLAPDATA_HPP)
#define LIBMAUS2_DAZZLER_ALIGN_OVERLAPDATA_HPP

#include <libmaus2/autoarray/AutoArray.hpp>
#include <libmaus2/util/LELoad.hpp>
#include <libmaus2/dazzler/align/Overlap.hpp>
#include <libmaus2/dazzler/align/AlignmentFile.hpp>
#include <libmaus2/dazzler/align/AlignmentFileConstants.hpp>

namespace libmaus2
{
	namespace dazzler
	{
		namespace align
		{
			struct OverlapData
			{
				typedef OverlapData this_type;
				typedef std::unique_ptr<this_type> unique_ptr_type;
				typedef std::shared_ptr<this_type> shared_ptr_type;

				struct OverlapOffset
				{
					uint64_t offset;
					uint64_t length;

					OverlapOffset()
					{

					}

					OverlapOffset(uint64_t const roffset, uint64_t const rlength)
					: offset(roffset), length(rlength)
					{

					}
				};

				struct DataIndex
				{
					uint64_t parserid;
					OverlapData const * data;
					uint64_t index;

					DataIndex() : parserid(0), data(0), index(0) {}
					DataIndex(
						uint64_t const rparserid,
						OverlapData const * rdata,
						uint64_t const rindex
					) : parserid(rparserid), data(rdata), index(rindex) {}
				};

				libmaus2::autoarray::AutoArray<uint8_t> Adata;
				libmaus2::autoarray::AutoArray<OverlapOffset> Aoffsets;
				uint64_t overlapsInBuffer;

				OverlapData() : overlapsInBuffer(0) {}

				uint64_t clone(uint64_t const i, libmaus2::autoarray::AutoArray<uint8_t> & A) const
				{
					std::pair<uint8_t const *, uint8_t const *> const P = getData(i);
					A.ensureSize(P.second-P.first);
					std::copy(P.first,P.second,A.begin());
					uint64_t const l = P.second - P.first;
					return l;
				}

				std::pair<uint8_t const *, uint8_t const *> getData(uint64_t const i) const
				{
					if ( i < overlapsInBuffer )
					{
						return
							std::pair<uint8_t const *, uint8_t const *>(
								Adata.begin() + Aoffsets[i].offset,
								Adata.begin() + Aoffsets[i].offset + Aoffsets[i].length
							);
					}
					else
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "OverlapParser::getData(): index " << i << " is out of range" << std::endl;
						lme.finish();
						throw lme;
					}
				}

				std::pair<uint8_t *, uint8_t *> getData(uint64_t const i)
				{
					if ( i < overlapsInBuffer )
					{
						return
							std::pair<uint8_t *, uint8_t *>(
								Adata.begin() + Aoffsets[i].offset,
								Adata.begin() + Aoffsets[i].offset + Aoffsets[i].length
							);
					}
					else
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "OverlapParser::getData(): index " << i << " is out of range" << std::endl;
						lme.finish();
						throw lme;
					}
				}

				std::string getDataAsString(uint64_t const i) const
				{
					std::pair<uint8_t const *, uint8_t const *> P = getData(i);
					return std::string(P.first,P.second);
				}

				uint64_t size() const
				{
					return overlapsInBuffer;
				}

				void swap(OverlapData & rhs)
				{
					Adata.swap(rhs.Adata);
					Aoffsets.swap(rhs.Aoffsets);
					// Alength.swap(rhs.Alength);
					std::swap(overlapsInBuffer,rhs.overlapsInBuffer);
				}

				static std::ostream & toString(std::ostream & out, uint8_t const * p)
				{
					out << "OverlapData(";
					out << "flags=" << getFlags(p) << ";";
					out << "aread=" << getARead(p) << ";";
					out << "bread=" << getBRead(p) << ";";
					out << "tlen=" << getTLen(p) << ";";
					out << "diffs=" << getDiffs(p) << ";";
					out << "abpos=" << getABPos(p) << ";";
					out << "bbpos=" << getBBPos(p) << ";";
					out << "aepos=" << getAEPos(p) << ";";
					out << "bepos=" << getBEPos(p);
					out << ")";
					return out;
				}

				static int32_t getTLen(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 0*sizeof(int32_t));
				}

				static int32_t getDiffs(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 1*sizeof(int32_t));
				}

				static int32_t getABPos(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 2*sizeof(int32_t));
				}

				static int32_t getBBPos(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 3*sizeof(int32_t));
				}

				static int32_t getAEPos(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 4*sizeof(int32_t));
				}

				static int32_t getBEPos(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 5*sizeof(int32_t));
				}

				static uint32_t getFlags(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 6*sizeof(int32_t));
				}

				static void putFlags(uint8_t * p, uint32_t const f)
				{
					p += 6*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putABPos(uint8_t * p, uint32_t const f)
				{
					p += 2*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putBBPos(uint8_t * p, uint32_t const f)
				{
					p += 3*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putAEPos(uint8_t * p, uint32_t const f)
				{
					p += 4*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putBEPos(uint8_t * p, uint32_t const f)
				{
					p += 5*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putARead(uint8_t * p, uint32_t const f)
				{
					p += 7*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void putBRead(uint8_t * p, uint32_t const f)
				{
					p += 8*sizeof(int32_t);

					p[0] = ((f>> 0) & 0xFF);
					p[1] = ((f>> 8) & 0xFF);
					p[2] = ((f>>16) & 0xFF);
					p[3] = ((f>>24) & 0xFF);
				}

				static void addPrimaryFlag(uint8_t * p)
				{
					putFlags(
						p,
						getFlags(p)
						|
						libmaus2::dazzler::align::Overlap::getPrimaryFlag()
					);
				}

				static bool getPrimaryFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getPrimaryFlag()) != 0;
				}

				static bool getTrueFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getTrueFlag()) != 0;
				}

				static bool getInverseFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getInverseFlag()) != 0;
				}

				static bool getACompFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getACompFlag()) != 0;
				}

				static bool getStartFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getStartFlag()) != 0;
				}

				static bool getNextFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getNextFlag()) != 0;
				}

				static bool getBestFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getBestFlag()) != 0;
				}

				static bool getHaploFlag(uint8_t const * p)
				{
					uint32_t const flags = getFlags(p);
					return (flags & libmaus2::dazzler::align::Overlap::getHaploFlag()) != 0;
				}

				static int32_t getARead(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 7*sizeof(int32_t));
				}

				static int32_t getBRead(uint8_t const * p)
				{
					return libmaus2::util::loadValueLE4(p + 8*sizeof(int32_t));
				}

				static uint8_t const * getTraceData(uint8_t const * p)
				{
					return p + 10*sizeof(int32_t);
				}

				static uint64_t decodeTraceVector(uint8_t const * p, libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A, bool const small)
				{
					int32_t const tlen = getTLen(p);
					assert ( (tlen & 1) == 0 );

					int32_t const tlen2 = tlen/2;
					if ( tlen2 > static_cast<int64_t>(A.size()) )
						A.resize(tlen2);

					if ( small )
					{
						uint8_t const * tp = getTraceData(p);

						for ( int32_t i = 0; i < tlen2; ++i )
						{
							A[i].first = tp[0];
							A[i].second = tp[1];
							tp += 2;
						}
					}
					else
					{
						uint8_t const * tp = getTraceData(p);

						for ( int32_t i = 0; i < tlen2; ++i )
						{
							A[i].first  = libmaus2::util::loadValueLE2(tp);
							A[i].second = libmaus2::util::loadValueLE2(tp+sizeof(int16_t));
							tp += 2*sizeof(int16_t);
						}
					}

					return tlen2;
				}

				static uint64_t getTracePairs(
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > & P,
					int64_t const tspace
				)
				{
					bool const small = libmaus2::dazzler::align::AlignmentFile::tspaceToSmall(tspace);

					int32_t const tlen = getTLen(p);
					assert ( (tlen & 1) == 0 );
					int32_t const tlen2 = tlen/2;

					// no trace info?
					if ( ! tlen )
					{
						assert ( getABPos(p) == getAEPos(p) );
						assert ( getBBPos(p) == getBEPos(p) );

						uint64_t po = 0;
						P.push(po,std::pair<uint64_t,uint64_t>(getABPos(p),getBBPos(p)));
						return po;
					}

					int64_t const abpos = getABPos(p);
					int64_t const bbpos = getBBPos(p);
					uint64_t po = 0;

					if ( small )
					{
						uint8_t const * tp = getTraceData(p);
						tp += 1;

						int64_t a = (abpos / tspace) * tspace;
						int64_t b = bbpos;

						for ( int32_t i = 0; i < tlen2; ++i, tp += 2 )
						{
							int64_t const ac = std::max(a,abpos);

							P.push(po,std::pair<uint64_t,uint64_t>(ac,b));

							a += tspace;
							b += *tp;
						}

						int64_t const ac = std::min(a,static_cast<int64_t>(getAEPos(p)));
						P.push(po,std::pair<uint64_t,uint64_t>(ac,b));

						assert ( b == getBEPos(p) );
					}
					else
					{
						uint8_t const * tp = getTraceData(p);
						// skip error
						tp += sizeof(int16_t);

						int64_t a = (abpos / tspace) * tspace;
						int64_t b = bbpos;

						for ( int32_t i = 0; i < tlen2; ++i, tp += 2*sizeof(int16_t) )
						{
							int64_t const ac = std::max(a,abpos);

							P.push(po,std::pair<uint64_t,uint64_t>(ac,b));

							a += tspace;
							b += libmaus2::util::loadValueLE2(tp);
						}

						int64_t const ac = std::min(a,static_cast<int64_t>(getAEPos(p)));
						P.push(po,std::pair<uint64_t,uint64_t>(ac,b));

						assert ( b == getBEPos(p) );
					}

					return po;
				}

				struct TraceIndex
				{
					int64_t a;
					int64_t b;
					uint64_t i;

					TraceIndex() {}
					TraceIndex(
						int64_t const ra,
						int64_t const rb,
						uint64_t const ri
					) : a(ra), b(rb), i(ri) {}
				};

				static std::pair<TraceIndex,TraceIndex> subIntervalA(
					int64_t const ab,
					int64_t const ae,
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > & P,
					int64_t const tspace
				)
				{
					if ( ab < getABPos(p) || ae > getAEPos(p) )
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "[E] OverlapData::subIntervalB: invalid range [" << ab << "," << ae << ") not inside [" << getABPos(p) << "," << getAEPos(p) << ")" << std::endl;
						lme.finish();
						throw lme;
					}

					uint64_t const ntp = getTracePairs(p,P,tspace);

					uint64_t ib = 0;
					while ( ib+1 < ntp && static_cast<int64_t>(P[ib+1].first) <= ab )
						++ib;
					assert ( static_cast<int64_t>(P[ib].first) <= ab );
					assert ( ib+1 >= ntp || static_cast<int64_t>(P[ib+1].first) > ab );

					uint64_t ie = ntp-1;
					while ( ie && static_cast<int64_t>(P[ie-1].first) >= ae )
						--ie;
					assert ( static_cast<int64_t>(P[ie].first) >= ae );
					assert ( ie == 0 || static_cast<int64_t>(P[ie-1].first) < ae );

					TraceIndex TB(P[ib].first,P[ib].second,ib);
					TraceIndex TE(P[ie].first,P[ie].second,ie);

					return std::pair<TraceIndex,TraceIndex>(TB,TE);
				}

				static void subTrace(
					std::pair<TraceIndex,TraceIndex> const & TI,
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > & P,
					int64_t const tspace,
					uint8_t const * ua,
					uint8_t const * ub,
					libmaus2::lcs::AlignmentTraceContainer & ATC,
					libmaus2::lcs::Aligner & np
				)
				{
					getTracePairs(p,P,tspace);

					uint64_t const numintv = TI.second.i - TI.first.i;
					ATC.reset();

					for ( uint64_t i = 0; i < numintv; ++i )
					{
						uint64_t const i0 = TI.first.i + i;
						uint64_t const i1 = i0 + 1;

						int64_t const abpos = P[i0].first;
						int64_t const bbpos = P[i0].second;
						int64_t const aepos = P[i1].first;
						int64_t const bepos = P[i1].second;

						np.align(
							ua + abpos,
							aepos-abpos,
							ub + bbpos,
							bepos-bbpos
						);

						ATC.push(np.getTraceContainer());
					}
				}

				static std::pair<TraceIndex,TraceIndex> subIntervalB(
					int64_t const bb,
					int64_t const be,
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > & P,
					int64_t const tspace
				)
				{
					if ( bb < getBBPos(p) || be > getBEPos(p) )
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "[E] OverlapData::subIntervalB: invalid range [" << bb << "," << be << ") not inside [" << getBBPos(p) << "," << getBEPos(p) << ")" << std::endl;
						lme.finish();
						throw lme;
					}

					uint64_t const ntp = getTracePairs(p,P,tspace);

					uint64_t ib = 0;
					while ( ib+1 < ntp && static_cast<int64_t>(P[ib+1].second) <= bb )
						++ib;
					assert ( static_cast<int64_t>(P[ib].second) <= bb );
					assert ( ib+1 >= ntp || static_cast<int64_t>(P[ib+1].second) > bb );

					uint64_t ie = ntp-1;
					while ( ie && static_cast<int64_t>(P[ie-1].second) >= be )
						--ie;
					assert ( static_cast<int64_t>(P[ie].second) >= be );
					assert ( ie == 0 || static_cast<int64_t>(P[ie-1].second) < be );

					TraceIndex TB(P[ib].first,P[ib].second,ib);
					TraceIndex TE(P[ie].first,P[ie].second,ie);

					return std::pair<TraceIndex,TraceIndex>(TB,TE);
				}

				static uint64_t getTracePoints(
					uint8_t const * p,
					int64_t const tspace,
					uint64_t const traceid,
					libmaus2::autoarray::AutoArray<TracePoint> & V,
					libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A,
					bool const fullonly,
					uint64_t o = 0
				)
				{
					uint64_t const numt = decodeTraceVector(p,A,libmaus2::dazzler::align::AlignmentFile::tspaceToSmall(tspace));

					// current point on A
					int32_t a_i = ( getABPos(p) / tspace ) * tspace;
					// current point on B
					int32_t b_i = ( getBBPos(p) );

					for ( size_t i = 0; i < numt; ++i )
					{
						// block start point on A
						int32_t const a_i_0 = std::max ( a_i, static_cast<int32_t>(getABPos(p)) );
						// block end point on A
						int32_t const a_i_1 = std::min ( static_cast<int32_t>(a_i + tspace), static_cast<int32_t>(getAEPos(p)) );
						// block end point on B
						int32_t const b_i_1 = b_i + A[i].second;

						if ( (!fullonly) || ((a_i_0%tspace) == 0) )
							V.push(o,TracePoint(a_i_0,b_i,traceid));

						// update start points
						b_i = b_i_1;
						a_i = a_i_1;
					}

					if ( o )
					{
						if ( (!fullonly) || ((a_i%tspace) == 0) )
							V.push(o,TracePoint(a_i,b_i,traceid));
					}
					else
					{
						if ( (!fullonly) || ((getABPos(p)%tspace) == 0) )
							V.push(o,TracePoint(getABPos(p),getBBPos(p),traceid));
					}

					return o;
				}

				static uint64_t putOverlap(
					int64_t const aread,
					int64_t const bread,
					int64_t const flags,
					int64_t const abpos,
					int64_t const aepos,
					int64_t const bbpos,
					int64_t const bepos,
					int64_t const tspace,
					libmaus2::lcs::AlignmentTraceContainer const & ATC,
					libmaus2::autoarray::AutoArray< std::pair<uint16_t,uint16_t> > & A16,
					libmaus2::autoarray::AutoArray< uint8_t > & BS,
					uint64_t const ro = 0
				)
				{
					uint64_t const nt = computeTracePoints(abpos,aepos,bbpos,bepos,tspace,ATC,A16);

					uint64_t o = ro;
					o = putLE(o,BS,2*nt,4); // tlen
					o = putLE(o,BS,ATC.getAlignmentStatistics().getEditDistance(),4); // diffs
					o = putLE(o,BS,abpos,4); // abpos
					o = putLE(o,BS,bbpos,4); // bbpos
					o = putLE(o,BS,aepos,4); // aepos
					o = putLE(o,BS,bepos,4); // bepos
					o = putLE(o,BS,flags,4); // flags
					o = putLE(o,BS,aread,4); // aread
					o = putLE(o,BS,bread,4); // bread
					o = putLE(o,BS,0,4); // pad

					bool const small = libmaus2::dazzler::align::AlignmentFileConstants::tspaceToSmall(tspace);

					if ( small )
					{
						for ( uint64_t i = 0; i < nt; ++i )
						{
							o = putLE(o,BS,A16[i].first,1);
							o = putLE(o,BS,A16[i].second,1);
						}
					}
					else
					{
						for ( uint64_t i = 0; i < nt; ++i )
						{
							o = putLE(o,BS,A16[i].first,2);
							o = putLE(o,BS,A16[i].second,2);
						}
					}
					return o;
				}

				static uint64_t computeTracePoints(
					int64_t const abpos,
					int64_t const aepos,
					int64_t const bbpos,
					int64_t const bepos,
					int64_t const tspace,
					libmaus2::lcs::AlignmentTraceContainer const & ATC,
					libmaus2::autoarray::AutoArray< std::pair<uint16_t,uint16_t> > & A16
				)
				{
					// current point on A
					int64_t a_i = ( abpos / tspace ) * tspace;

					libmaus2::lcs::AlignmentTraceContainer::step_type const * tc = ATC.ta;

					int64_t bsum = 0;
					uint64_t oA16 = 0;

					while ( a_i < aepos )
					{
						assert ( a_i % tspace == 0 );

						int64_t a_c = std::max(abpos,a_i);
						// block end point on A
						int64_t const a_i_1 = std::min ( static_cast<int64_t>(a_i + tspace), static_cast<int64_t>(aepos) );

						assert ( (a_i_1 == aepos) || (a_i_1%tspace == 0) );

						int64_t bforw = 0;
						int64_t err = 0;
						while ( a_c < a_i_1 )
						{
							if ( tc >= ATC.te )
							{
								libmaus2::exception::LibMausException lme;
								lme.getStream() << "OverlapData::computeTracePoints: trace container is inconsistent with coordinates" << std::endl;
								lme.getStream() << "abpos=" << abpos << " aepos=" << aepos << " adiff=" << aepos-abpos << std::endl;
								lme.getStream() << "bbpos=" << bbpos << " bepos=" << bepos << " bdiff=" << bepos-bbpos << std::endl;
								lme.getStream() << "SL.first=" << ATC.getStringLengthUsed().first << std::endl;
								lme.getStream() << "SL.second=" << ATC.getStringLengthUsed().second << std::endl;
								lme.finish();

								throw lme;
							}
							assert ( tc < ATC.te );
							switch ( *(tc++) )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
									++a_c;
									++bforw;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									++a_c;
									++bforw;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									++a_c;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									++bforw;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_RESET:
									break;
							}
						}

						assert ( a_c == a_i_1 );

						// consume rest of operations if we reached end of alignment on A read
						while ( a_c == static_cast<int64_t>(aepos) && tc != ATC.te )
						{
							switch ( *(tc++) )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
									++a_c;
									++bforw;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									++a_c;
									++bforw;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									++a_c;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									++bforw;
									++err;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_RESET:
									break;
							}
						}

						A16.push(oA16,std::pair<uint16_t,uint16_t>(err,bforw));
						bsum += bforw;

						a_i = a_i_1;
					}

					if ( (tc != ATC.te) || (a_i != aepos) || (bsum != bepos-bbpos) )
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "OverlapData::computeTracePoints: trace container is inconsistent with coordinates" << std::endl;
						lme.getStream() << "abpos=" << abpos << " aepos=" << aepos << " adiff=" << aepos-abpos << std::endl;
						lme.getStream() << "bbpos=" << bbpos << " bepos=" << bepos << " bdiff=" << bepos-bbpos << std::endl;
						lme.getStream() << "SL.first=" << ATC.getStringLengthUsed().first << std::endl;
						lme.getStream() << "SL.second=" << ATC.getStringLengthUsed().second << std::endl;
						lme.finish();

						throw lme;
					}

					assert ( tc == ATC.te );
					assert ( a_i == aepos );
					assert ( bsum == bepos-bbpos );

					bool const ok = bsum == (bepos - bbpos);
					if ( ! ok )
					{
						libmaus2::exception::LibMausException lme;
						lme.getStream() << "OverlapData::computeTracePoints: bsum=" << bsum << " != " << (bepos - bbpos) << std::endl;

						std::pair<uint64_t,uint64_t> SL = ATC.getStringLengthUsed(ATC.ta,ATC.te);
						lme.getStream() << "SL = " << SL.first << "," << SL.second << std::endl;
						lme.getStream() << "aepos-abpos=" << aepos-abpos << std::endl;
						lme.getStream() << "bepos-bbpos=" << bepos-bbpos << std::endl;
						lme.getStream() << "ATC.te - ATC.ta=" << (ATC.te-ATC.ta) << std::endl;
						lme.getStream() << "ATC.te - tc=" << (ATC.te-tc) << std::endl;
						lme.finish();

						throw lme;
					}

					return oA16;
				}

				static void computeTrace(
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A,
					int64_t const tspace,
					uint8_t const * aptr,
					uint8_t const * bptr,
					libmaus2::lcs::AlignmentTraceContainer & ATC,
					libmaus2::lcs::Aligner & aligner
				)
				{
					bool const small = libmaus2::dazzler::align::AlignmentFile::tspaceToSmall(tspace);
					uint64_t const Alen = decodeTraceVector(p,A,small);
					libmaus2::dazzler::align::Overlap::computeTrace(
						A.begin(),
						Alen,
						getABPos(p),
						getAEPos(p),
						getBBPos(p),
						getBEPos(p),
						aptr,
						bptr,
						tspace,
						ATC,
						aligner
					);
				}

				static uint64_t putLE(
					uint64_t o,
					libmaus2::autoarray::AutoArray<uint8_t> & A,
					uint64_t v,
					uint64_t n
				)
				{
					for ( uint64_t i = 0; i < n; ++i )
					{
						A.push(o,v&0xFF);
						v >>= 8;
					}

					return o;
				}

				static uint64_t swap(
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A,
					int64_t const tspace,
					uint8_t const * aptr,
					int64_t const la,
					uint8_t const * bptr,
					int64_t const lb,
					libmaus2::lcs::AlignmentTraceContainer & ATC,
					libmaus2::lcs::Aligner & aligner,
					libmaus2::autoarray::AutoArray<uint8_t> & B
				)
				{
					bool const inv = getInverseFlag(p);

					computeTrace(p,A,tspace,aptr,bptr,ATC,aligner);

					// swap roles of A and B
					ATC.swapRoles();

					int64_t abpos = getABPos(p);
					int64_t aepos = getAEPos(p);

					int64_t bbpos = getBBPos(p);
					int64_t bepos = getBEPos(p);

					if ( inv )
					{
						std::reverse(ATC.ta,ATC.te);

						std::swap(abpos,aepos);
						abpos = la - abpos;
						aepos = la - aepos;

						std::swap(bbpos,bepos);
						bbpos = lb - bbpos;
						bepos = lb - bepos;
					}

					std::swap(abpos,bbpos);
					std::swap(aepos,bepos);

					// store trace vector in A
					uint64_t const nt = computeTracePoints(
						abpos,
						aepos,
						bbpos,
						bepos,
						tspace,
						ATC,
						A
					);

					uint64_t diffs = 0;
					for ( uint64_t i = 0; i < nt; ++i )
						diffs += A[i].first;

					// put ovl data structure
					uint64_t o = 0;
					o = putLE(o,B,2*nt,4); // tlen
					o = putLE(o,B,diffs,4); // diffs
					o = putLE(o,B,abpos,4); // abpos
					o = putLE(o,B,bbpos,4); // bbpos
					o = putLE(o,B,aepos,4); // aepos
					o = putLE(o,B,bepos,4); // bepos
					o = putLE(o,B,getFlags(p),4); // flags
					o = putLE(o,B,getBRead(p),4); // aread
					o = putLE(o,B,getARead(p),4); // bread
					o = putLE(o,B,0,4); // pad

					bool const small = libmaus2::dazzler::align::AlignmentFile::tspaceToSmall(tspace);

					if ( small )
					{
						for ( uint64_t i = 0; i < nt; ++i )
						{
							o = putLE(o,B,A[i].first,1);
							o = putLE(o,B,A[i].second,1);
						}
					}
					else
					{
						for ( uint64_t i = 0; i < nt; ++i )
						{
							o = putLE(o,B,A[i].first,2);
							o = putLE(o,B,A[i].second,2);
						}
					}

					return o;
				}

				struct TracePartInfo
				{
					int64_t abpos;
					int64_t aepos;
					int64_t bbpos;
					int64_t bepos;

					TracePartInfo() {}
					TracePartInfo(
						int64_t const rabpos,
						int64_t const raepos,
						int64_t const rbbpos,
						int64_t const rbepos
					) : abpos(rabpos), aepos(raepos), bbpos(rbbpos), bepos(rbepos) {}
				};

				template<typename path_iterator>
				static TracePartInfo computeTracePartByPath(
					int64_t const abpos,
					int64_t const aepos,
					int64_t const bbpos,
					int64_t const bepos,
					int64_t const rastart,
					int64_t const raend,
					path_iterator A,
					uint64_t const Alen,
					int64_t const tspace,
					uint8_t const * aptr,
					uint8_t const * bptr,
					libmaus2::lcs::AlignmentTraceContainer & ATC,
					libmaus2::lcs::Aligner & aligner
				)
				{
					assert ( raend >= rastart );

					int64_t astart = rastart;
					int64_t aend = raend;

					// align to trace point boundaries
					if ( astart % tspace != 0 )
						// round to zero
						astart = (astart / tspace) * tspace;
					if ( aend % tspace != 0 )
						// round to infinity
						aend = ((aend + tspace - 1)/tspace)*tspace;

					// clip to [abpos,aepos)
					astart = std::max(astart,abpos);
					aend = std::max(astart,aend);
					aend = std::min(aend,aepos);
					astart = std::min(astart,aend);

					// std::cerr << "[" << astart << "," << aend << ")" << " [" << abpos << "," << aepos << ")" << std::endl;

					Overlap::OffsetInfo startoff = libmaus2::dazzler::align::Overlap::getBforAOffset(tspace,abpos,aepos,bbpos,bepos,astart,A,Alen);
					Overlap::OffsetInfo endoff = libmaus2::dazzler::align::Overlap::getBforAOffset(tspace,abpos,aepos,bbpos,bepos,aend,A,Alen);

					libmaus2::dazzler::align::Overlap::computeTrace(
						A + startoff.offset,
						endoff.offset - startoff.offset,
						startoff.apos,
						endoff.apos,
						startoff.bpos,
						endoff.bpos,
						aptr,
						bptr,
						tspace,
						ATC,
						aligner
					);

					if ( rastart > astart )
					{
						std::pair<uint64_t,uint64_t> P = ATC.advanceA(rastart - astart);
						std::pair<uint64_t,uint64_t> SL = ATC.getStringLengthUsed(ATC.ta,ATC.ta + P.second);

						startoff.apos += SL.first;
						startoff.bpos += SL.second;
						ATC.ta += P.second;
					}
					if ( raend < aend )
					{
						std::reverse(ATC.ta,ATC.te);

						std::pair<uint64_t,uint64_t> P = ATC.advanceA(aend-raend);
						std::pair<uint64_t,uint64_t> SL = ATC.getStringLengthUsed(ATC.ta,ATC.ta + P.second);
						ATC.ta += P.second;

						std::reverse(ATC.ta,ATC.te);

						endoff.apos -= SL.first;
						endoff.bpos -= SL.second;
					}

					return TracePartInfo(startoff.apos,endoff.apos,startoff.bpos,endoff.bpos);
				}

				static TracePartInfo computeTracePart(
					int64_t const rastart,
					int64_t const raend,
					uint8_t const * p,
					libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A,
					int64_t const tspace,
					uint8_t const * aptr,
					uint8_t const * bptr,
					libmaus2::lcs::AlignmentTraceContainer & ATC,
					libmaus2::lcs::Aligner & aligner
				)
				{
					bool const small = libmaus2::dazzler::align::AlignmentFile::tspaceToSmall(tspace);
					uint64_t const Alen = decodeTraceVector(p,A,small);
					return computeTracePartByPath(
						static_cast<int64_t>(getABPos(p)),
						static_cast<int64_t>(getAEPos(p)),
						static_cast<int64_t>(getBBPos(p)),
						static_cast<int64_t>(getBEPos(p)),
						rastart,raend,A.begin(),Alen,tspace,aptr,bptr,ATC,aligner);
				}
			};
		}
	}
}
#endif
