/*
    daccord
    Copyright (C) 2018 German Tischler-Höhle

    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_GETTRANSITIVESTRICT_HPP)
#define LIBMAUS2_DAZZLER_ALIGN_GETTRANSITIVESTRICT_HPP

#include <libmaus2/dazzler/align/Overlap.hpp>
#include <libmaus2/dazzler/align/OverlapData.hpp>
#include <libmaus2/lcs/NP.hpp>
#include <libmaus2/lcs/NPLinMem.hpp>

namespace libmaus2
{
	namespace dazzler
	{
		namespace align
		{
			struct GetTransitiveStrict
			{
				typedef libmaus2::lcs::NPLinMem np_type;

				static uint8_t const * applyInverse(libmaus2::autoarray::AutoArray<uint8_t> & UA, uint8_t const * a, uint64_t const la, bool const inv)
				{
					if ( inv )
					{
						UA.ensureSize(la);
						std::copy(a,a+la,UA.begin());
						std::reverse(UA.begin(),UA.begin()+la);
						for ( uint64_t i = 0; i < la; ++i )
							UA[i] = libmaus2::fastx::invertUnmapped(UA[i]);
						return UA.begin();
					}
					else
					{
						return a;
					}
				}

				struct GetTransitiveStrictMatch
				{
					uint64_t apos;
					uint64_t bpos;

					GetTransitiveStrictMatch() {}
					GetTransitiveStrictMatch(uint64_t const rapos, uint64_t const rbpos) : apos(rapos), bpos(rbpos) {}
				};

				struct GetTransitiveStrictMatchInterval
				{
					uint64_t apos;
					uint64_t epos;

					GetTransitiveStrictMatchInterval() {}
					GetTransitiveStrictMatchInterval(uint64_t const rapos, uint64_t const repos) : apos(rapos), epos(repos) {}

					uint64_t diam() const
					{
						return epos-apos;
					}
				};

				struct GetTransitiveStrictResult
				{
					libmaus2::dazzler::align::OverlapInfo OI;
					libmaus2::dazzler::align::Overlap OVLF;
					libmaus2::dazzler::align::Overlap OVLR;

					GetTransitiveStrictResult()
					{

					}
					GetTransitiveStrictResult(
						libmaus2::dazzler::align::OverlapInfo const & rOI,
						libmaus2::dazzler::align::Overlap const & rOVLF,
						libmaus2::dazzler::align::Overlap const & rOVLR
					)
					: OI(rOI), OVLF(rOVLF), OVLR(rOVLR)
					{}
				};

				struct GetTransitiveStrictResultLowAlloc
				{
					libmaus2::dazzler::align::OverlapInfo OI;
					uint8_t const * BSFa;
					uint8_t const * BSFe;
					uint8_t const * BSRa;
					uint8_t const * BSRe;

					GetTransitiveStrictResultLowAlloc()
					{

					}
					GetTransitiveStrictResultLowAlloc(
						libmaus2::dazzler::align::OverlapInfo const & rOI,
						uint8_t const * rBSFa,
						uint8_t const * rBSFe,
						uint8_t const * rBSRa,
						uint8_t const * rBSRe
					)
					: OI(rOI), BSFa(rBSFa), BSFe(rBSFe), BSRa(rBSRa), BSRe(rBSRe)
					{}
				};

				struct GetTransitiveStrictContext
				{
					np_type np;
					libmaus2::lcs::AlignmentTraceContainer ATCA;
					libmaus2::lcs::AlignmentTraceContainer ATCB;
					libmaus2::lcs::AlignmentTraceContainer ATCC;
					libmaus2::autoarray::AutoArray<uint8_t> UA;
					libmaus2::autoarray::AutoArray<uint8_t> UB;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatch> VM;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatchInterval> VMI;
                                        libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > A16;
                                        libmaus2::autoarray::AutoArray< uint8_t > BSF;
                                        libmaus2::autoarray::AutoArray< uint8_t > BSR;
                                        libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > P;
					libmaus2::lcs::AlignmentTraceContainer ATCsubAB;
					libmaus2::lcs::AlignmentTraceContainer ATCsubBC;
				};

				typedef GetTransitiveStrict this_type;
				typedef std::unique_ptr<this_type> unique_ptr_type;

				GetTransitiveStrictContext context;

				GetTransitiveStrict() {}

				static GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					libmaus2::dazzler::align::Overlap const & OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					libmaus2::dazzler::align::Overlap const & OVLBC,
					uint8_t const * a,
					uint64_t const la,
					uint8_t const * b,
					uint64_t const lb,
					uint8_t const * c,
					uint64_t const lc,
					int64_t const tspace,
					GetTransitiveStrictContext & context,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					np_type & np = context.np;
					libmaus2::lcs::AlignmentTraceContainer & ATCA = context.ATCA;
					libmaus2::lcs::AlignmentTraceContainer & ATCB = context.ATCB;
					libmaus2::lcs::AlignmentTraceContainer & ATCC = context.ATCC;
					libmaus2::autoarray::AutoArray<uint8_t> & UA = context.UA;
					libmaus2::autoarray::AutoArray<uint8_t> & UB = context.UB;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatch> & VM = context.VM;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatchInterval> & VMI = context.VMI;

					// B interval on OAB
					libmaus2::math::IntegerInterval<int64_t> IAB(OAB.bbpos,static_cast<int64_t>(OAB.bepos)-1);
					// A interval on OBC
					libmaus2::math::IntegerInterval<int64_t> IBC(OBC.abpos,static_cast<int64_t>(OBC.aepos)-1);
					libmaus2::math::IntegerInterval<int64_t> I = IAB.intersection(IBC);

					if ( I.isEmpty() )
					{
						return
							GetTransitiveStrictResult(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								libmaus2::dazzler::align::Overlap(),
								libmaus2::dazzler::align::Overlap()
							);
					}

					// decode reads
					uint8_t const * AB_ua = applyInverse(UA,a,la,false            );
					uint8_t const * AB_ub = applyInverse(UB,b,lb,OVLAB.isInverse());

					// compute trace
					OVLAB.computeTrace(AB_ua,AB_ub,tspace,ATCA,np);

					// apply RC if necessary
					if ( (OAB.aread & 1) != 0 )
						std::reverse(ATCA.ta,ATCA.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_a_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta,ATCA.te,I.from - OAB.bbpos);
					std::pair<int64_t,int64_t> sl_a_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta,ATCA.ta + adv_a_0.second);
					std::pair<int64_t,int64_t> adv_a_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta + adv_a_0.second,ATCA.te,I.diameter());
					std::pair<int64_t,int64_t> sl_a_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta + adv_a_0.second, ATCA.ta + adv_a_0.second + adv_a_1.second);

					// trace interval for (A,B)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_ta = ATCA.ta + adv_a_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_te = AB_ta   + adv_a_1.second;

					// interval on A
					int64_t const abpos = OAB.abpos + sl_a_0.first;
					int64_t const aepos = abpos     + sl_a_1.first;

					// decode reads
					uint8_t const * BC_ua = applyInverse(UA,b,lb,false            );
					uint8_t const * BC_ub = applyInverse(UB,c,lc,OVLBC.isInverse());

					// compute trace
					OVLBC.computeTrace(BC_ua,BC_ub,tspace,ATCB,np);

					if ( (OBC.aread & 1) != 0 )
						std::reverse(ATCB.ta,ATCB.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_c_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta,ATCB.te,I.from - OBC.abpos);
					std::pair<int64_t,int64_t> sl_c_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta,ATCB.ta + adv_c_0.second);
					std::pair<int64_t,int64_t> adv_c_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta + adv_c_0.second,ATCB.te,I.diameter());
					std::pair<int64_t,int64_t> sl_c_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta + adv_c_0.second, ATCB.ta + adv_c_0.second + adv_c_1.second);

					// trace interval for (B,C)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_ta = ATCB.ta + adv_c_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_te = BC_ta   + adv_c_1.second;

					// interval on C
					int64_t const cbpos = OBC.bbpos + sl_c_0.second;
					int64_t const cepos = cbpos     + sl_c_1.second;

					// sanity check
					assert (
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(AB_ta,AB_te).second
						==
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(BC_ta,BC_te).first
					);

					// position pair on A,B
					int64_t ab_apos = abpos;
					int64_t ab_bpos = I.from;

					// position pair on B,C
					int64_t bc_bpos = I.from;
					int64_t bc_cpos = cbpos;

					uint64_t VMo = 0;
					if ( padaleft )
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(0,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(0,bc_cpos));
					}
					else
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
					}

					while ( AB_ta != AB_te && BC_ta != BC_te )
					{
						if ( ab_bpos < bc_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									ab_apos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									ab_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									ab_apos++;
									ab_bpos++;
									break;
								default:
									break;
							}
						}
						else if ( bc_bpos < ab_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(BC_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									bc_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									bc_cpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									bc_bpos++;
									bc_cpos++;
									break;
								default:
									break;
							}
						}
						else
						{
							assert ( ab_bpos == bc_bpos );

							libmaus2::lcs::AlignmentTraceContainer::step_type const step_ab = *AB_ta;
							libmaus2::lcs::AlignmentTraceContainer::step_type const step_bc = *BC_ta;

							// first try to keep bpos in sync by not incrementing it
							if ( step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_DEL )
							{
								++AB_ta;
								ab_apos++;
							}
							else if ( step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_INS )
							{
								++BC_ta;
								bc_cpos++;
							}
							// we have to increment bpos in some form
							else
							{
								bool const ab_match =
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;
								bool const bc_match =
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;

								if ( ab_match && bc_match )
								{
									// match

									VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
									// std::cerr << "[V] match* (" << ab_apos << "," << bc_cpos << ")" << std::endl;

									++AB_ta;
									++BC_ta;
									ab_apos++;
									ab_bpos++;
									bc_bpos++;
									bc_cpos++;
								}
								else
								{
									libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

									switch ( step )
									{
										case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
											assert ( false );
											ab_apos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
											ab_bpos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
											ab_apos++;
											ab_bpos++;
											break;
										default:
											break;
									}

								}
							}
						}
					}

					if ( padaright )
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(la,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(la,cepos));
					}
					else
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(aepos,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(aepos,cepos));
					}

					uint64_t VMIo = 0;
					for ( uint64_t i = 1; i < VMo; ++i )
						VMI.push(VMIo,GetTransitiveStrictMatchInterval(VM[i-1].apos,VM[i].apos));

					uint8_t const * za = applyInverse(UA,a,la,OAB.aread&1);
					uint8_t const * zc = applyInverse(UB,c,lc,OBC.bread&1);

					libmaus2::dazzler::align::OverlapInfo const OI(OAB.aread,OBC.bread,
						padaleft  ? 0  : abpos,
						padaright ? la : aepos,
						padbleft  ? 0  : cbpos,
						padbright ? lc : cepos
					);

					if ( (OI.aepos-OI.abpos == 0) || (OI.bepos-OI.bbpos == 0) )
					{
						return
							GetTransitiveStrictResult(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								libmaus2::dazzler::align::Overlap(),
								libmaus2::dazzler::align::Overlap()
							);
					}

					uint64_t low = 0;
					uint64_t const thres = 100;
					ATCC.reset();

					while ( low < VMIo )
					{
						uint64_t high = low+1;
						uint64_t s = VMI[low].diam();

						while (
							high < VMIo
							&&
							s + VMI[high].diam() <= thres
						)
						{
							s += VMI[high++].diam();
						}

						uint64_t const abpos = VM[low].apos;
						uint64_t const aepos = VM[high].apos;
						uint64_t const cbpos = VM[low].bpos;
						uint64_t const cepos = VM[high].bpos;

						np.np(
							za+abpos,
							za+aepos,
							zc+cbpos,
							zc+cepos
						);

						ATCC.push(np);

						// std::cerr << "[V] using window [" << abpos << "," << aepos << ") [" << cbpos << "," << cepos << ")" << " " << np.getAlignmentStatistics() << std::endl;

						low = high;
					}

					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).first)  == static_cast<int64_t>(OI.aepos - OI.abpos) );
					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).second) == static_cast<int64_t>(OI.bepos - OI.bbpos) );
					assert ( libmaus2::lcs::AlignmentTraceContainer::checkAlignment(ATCC.ta,ATCC.te,za+OI.abpos,zc+OI.bbpos) );
					// std::cerr << ATCC.getAlignmentStatistics() << std::endl;

					libmaus2::dazzler::align::OverlapInfo OIF = OI;
					if ( (OIF.aread & 1) != 0 )
					{
						OIF = OIF.inverse(la,lc);
						std::reverse(ATCC.ta,ATCC.te);
					}

					libmaus2::dazzler::align::Overlap const OVLF = libmaus2::dazzler::align::Overlap::computeOverlap(
						(OIF.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIF.aread/2,
						OIF.bread/2,
						OIF.abpos,
						OIF.aepos,
						OIF.bbpos,
						OIF.bepos,
						tspace,
						ATCC
					);

					libmaus2::dazzler::align::OverlapInfo OIR = OIF.swapped();
					ATCC.swapRoles();
					if ( (OIR.aread & 1) != 0 )
					{
						OIR = OIR.inverse(lc,la);
						std::reverse(ATCC.ta,ATCC.te);
					}

					libmaus2::dazzler::align::Overlap const OVLR = libmaus2::dazzler::align::Overlap::computeOverlap(
						(OIR.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIR.aread/2,
						OIR.bread/2,
						OIR.abpos,
						OIR.aepos,
						OIR.bbpos,
						OIR.bepos,
						tspace,
						ATCC
					);

					return GetTransitiveStrictResult(OI,OVLF,OVLR);
				}

				static GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					uint8_t const * OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					uint8_t const * OVLBC,
					uint8_t const * a,
					uint64_t const la,
					uint8_t const * b,
					uint64_t const lb,
					uint8_t const * c,
					uint64_t const lc,
					int64_t const tspace,
					GetTransitiveStrictContext & context,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					np_type & np = context.np;
					libmaus2::lcs::AlignmentTraceContainer & ATCA = context.ATCA;
					libmaus2::lcs::AlignmentTraceContainer & ATCB = context.ATCB;
					libmaus2::lcs::AlignmentTraceContainer & ATCC = context.ATCC;
					libmaus2::autoarray::AutoArray<uint8_t> & UA = context.UA;
					libmaus2::autoarray::AutoArray<uint8_t> & UB = context.UB;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatch> & VM = context.VM;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatchInterval> & VMI = context.VMI;
                                        libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A16 = context.A16;

					// B interval on OAB
					libmaus2::math::IntegerInterval<int64_t> IAB(OAB.bbpos,static_cast<int64_t>(OAB.bepos)-1);
					// A interval on OBC
					libmaus2::math::IntegerInterval<int64_t> IBC(OBC.abpos,static_cast<int64_t>(OBC.aepos)-1);
					libmaus2::math::IntegerInterval<int64_t> I = IAB.intersection(IBC);

					if ( I.isEmpty() )
					{
						return
							GetTransitiveStrictResult(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								libmaus2::dazzler::align::Overlap(),
								libmaus2::dazzler::align::Overlap()
							);
					}

					// decode reads
					uint8_t const * AB_ua = applyInverse(UA,a,la,false            );
					uint8_t const * AB_ub = applyInverse(UB,b,lb,libmaus2::dazzler::align::OverlapData::getInverseFlag(OVLAB));

					// compute trace
					libmaus2::dazzler::align::OverlapData::computeTrace(OVLAB,A16,tspace,AB_ua,AB_ub,ATCA,np);
					// OVLAB.computeTrace(AB_ua,AB_ub,tspace,ATCA,np);

					// apply RC if necessary
					if ( (OAB.aread & 1) != 0 )
						std::reverse(ATCA.ta,ATCA.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_a_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta,ATCA.te,I.from - OAB.bbpos);
					std::pair<int64_t,int64_t> sl_a_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta,ATCA.ta + adv_a_0.second);
					std::pair<int64_t,int64_t> adv_a_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta + adv_a_0.second,ATCA.te,I.diameter());
					std::pair<int64_t,int64_t> sl_a_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta + adv_a_0.second, ATCA.ta + adv_a_0.second + adv_a_1.second);

					// trace interval for (A,B)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_ta = ATCA.ta + adv_a_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_te = AB_ta   + adv_a_1.second;

					// interval on A
					int64_t const abpos = OAB.abpos + sl_a_0.first;
					int64_t const aepos = abpos     + sl_a_1.first;

					// decode reads
					uint8_t const * BC_ua = applyInverse(UA,b,lb,false            );
					uint8_t const * BC_ub = applyInverse(UB,c,lc,libmaus2::dazzler::align::OverlapData::getInverseFlag(OVLBC));

					// compute trace
					// OVLBC.computeTrace(BC_ua,BC_ub,tspace,ATCB,np);
					libmaus2::dazzler::align::OverlapData::computeTrace(OVLBC,A16,tspace,BC_ua,BC_ub,ATCB,np);

					if ( (OBC.aread & 1) != 0 )
						std::reverse(ATCB.ta,ATCB.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_c_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta,ATCB.te,I.from - OBC.abpos);
					std::pair<int64_t,int64_t> sl_c_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta,ATCB.ta + adv_c_0.second);
					std::pair<int64_t,int64_t> adv_c_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta + adv_c_0.second,ATCB.te,I.diameter());
					std::pair<int64_t,int64_t> sl_c_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta + adv_c_0.second, ATCB.ta + adv_c_0.second + adv_c_1.second);

					// trace interval for (B,C)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_ta = ATCB.ta + adv_c_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_te = BC_ta   + adv_c_1.second;

					// interval on C
					int64_t const cbpos = OBC.bbpos + sl_c_0.second;
					int64_t const cepos = cbpos     + sl_c_1.second;

					// sanity check
					assert (
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(AB_ta,AB_te).second
						==
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(BC_ta,BC_te).first
					);

					// position pair on A,B
					int64_t ab_apos = abpos;
					int64_t ab_bpos = I.from;

					// position pair on B,C
					int64_t bc_bpos = I.from;
					int64_t bc_cpos = cbpos;

					uint64_t VMo = 0;
					if ( padaleft )
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(0,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(0,bc_cpos));
					}
					else
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
					}

					while ( AB_ta != AB_te && BC_ta != BC_te )
					{
						if ( ab_bpos < bc_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									ab_apos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									ab_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									ab_apos++;
									ab_bpos++;
									break;
								default:
									break;
							}
						}
						else if ( bc_bpos < ab_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(BC_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									bc_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									bc_cpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									bc_bpos++;
									bc_cpos++;
									break;
								default:
									break;
							}
						}
						else
						{
							assert ( ab_bpos == bc_bpos );

							libmaus2::lcs::AlignmentTraceContainer::step_type const step_ab = *AB_ta;
							libmaus2::lcs::AlignmentTraceContainer::step_type const step_bc = *BC_ta;

							// first try to keep bpos in sync by not incrementing it
							if ( step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_DEL )
							{
								++AB_ta;
								ab_apos++;
							}
							else if ( step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_INS )
							{
								++BC_ta;
								bc_cpos++;
							}
							// we have to increment bpos in some form
							else
							{
								bool const ab_match =
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;
								bool const bc_match =
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;

								if ( ab_match && bc_match )
								{
									// match

									VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
									// std::cerr << "[V] match* (" << ab_apos << "," << bc_cpos << ")" << std::endl;

									++AB_ta;
									++BC_ta;
									ab_apos++;
									ab_bpos++;
									bc_bpos++;
									bc_cpos++;
								}
								else
								{
									libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

									switch ( step )
									{
										case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
											assert ( false );
											ab_apos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
											ab_bpos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
											ab_apos++;
											ab_bpos++;
											break;
										default:
											break;
									}

								}
							}
						}
					}

					if ( padaright )
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(la,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(la,cepos));
					}
					else
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(aepos,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(aepos,cepos));
					}

					uint64_t VMIo = 0;
					for ( uint64_t i = 1; i < VMo; ++i )
						VMI.push(VMIo,GetTransitiveStrictMatchInterval(VM[i-1].apos,VM[i].apos));

					uint8_t const * za = applyInverse(UA,a,la,OAB.aread&1);
					uint8_t const * zc = applyInverse(UB,c,lc,OBC.bread&1);

					libmaus2::dazzler::align::OverlapInfo const OI(OAB.aread,OBC.bread,
						padaleft  ? 0  : abpos,
						padaright ? la : aepos,
						padbleft  ? 0  : cbpos,
						padbright ? lc : cepos
					);

					if ( (OI.aepos-OI.abpos == 0) || (OI.bepos-OI.bbpos == 0) )
					{
						return
							GetTransitiveStrictResult(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								libmaus2::dazzler::align::Overlap(),
								libmaus2::dazzler::align::Overlap()
							);
					}

					uint64_t low = 0;
					uint64_t const thres = 100;
					ATCC.reset();

					while ( low < VMIo )
					{
						uint64_t high = low+1;
						uint64_t s = VMI[low].diam();

						while (
							high < VMIo
							&&
							s + VMI[high].diam() <= thres
						)
						{
							s += VMI[high++].diam();
						}

						uint64_t const abpos = VM[low].apos;
						uint64_t const aepos = VM[high].apos;
						uint64_t const cbpos = VM[low].bpos;
						uint64_t const cepos = VM[high].bpos;

						np.np(
							za+abpos,
							za+aepos,
							zc+cbpos,
							zc+cepos
						);

						ATCC.push(np);

						// std::cerr << "[V] using window [" << abpos << "," << aepos << ") [" << cbpos << "," << cepos << ")" << " " << np.getAlignmentStatistics() << std::endl;

						low = high;
					}

					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).first)  == static_cast<int64_t>(OI.aepos - OI.abpos) );
					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).second) == static_cast<int64_t>(OI.bepos - OI.bbpos) );
					assert ( libmaus2::lcs::AlignmentTraceContainer::checkAlignment(ATCC.ta,ATCC.te,za+OI.abpos,zc+OI.bbpos) );
					// std::cerr << ATCC.getAlignmentStatistics() << std::endl;

					libmaus2::dazzler::align::OverlapInfo OIF = OI;
					if ( (OIF.aread & 1) != 0 )
					{
						OIF = OIF.inverse(la,lc);
						std::reverse(ATCC.ta,ATCC.te);
					}

					libmaus2::dazzler::align::Overlap const OVLF = libmaus2::dazzler::align::Overlap::computeOverlap(
						(OIF.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIF.aread/2,
						OIF.bread/2,
						OIF.abpos,
						OIF.aepos,
						OIF.bbpos,
						OIF.bepos,
						tspace,
						ATCC
					);

					libmaus2::dazzler::align::OverlapInfo OIR = OIF.swapped();
					ATCC.swapRoles();
					if ( (OIR.aread & 1) != 0 )
					{
						OIR = OIR.inverse(lc,la);
						std::reverse(ATCC.ta,ATCC.te);
					}

					libmaus2::dazzler::align::Overlap const OVLR = libmaus2::dazzler::align::Overlap::computeOverlap(
						(OIR.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIR.aread/2,
						OIR.bread/2,
						OIR.abpos,
						OIR.aepos,
						OIR.bbpos,
						OIR.bepos,
						tspace,
						ATCC
					);

					return GetTransitiveStrictResult(OI,OVLF,OVLR);
				}

				static GetTransitiveStrictResultLowAlloc getTransitiveStrictLowAlloc(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					uint8_t const * OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					uint8_t const * OVLBC,
					uint8_t const * a,
					uint64_t const la,
					uint8_t const * b,
					uint64_t const lb,
					uint8_t const * c,
					uint64_t const lc,
					int64_t const tspace,
					GetTransitiveStrictContext & context,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					np_type & np = context.np;
					#if defined(GETTRANSITIVESTRICTLOWALLOC_DEBUG)
					libmaus2::lcs::AlignmentTraceContainer & ATCA = context.ATCA;
					libmaus2::lcs::AlignmentTraceContainer & ATCB = context.ATCB;
					#endif
					libmaus2::lcs::AlignmentTraceContainer & ATCC = context.ATCC;
					libmaus2::autoarray::AutoArray<uint8_t> & UA = context.UA;
					libmaus2::autoarray::AutoArray<uint8_t> & UB = context.UB;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatch> & VM = context.VM;
					libmaus2::autoarray::AutoArray<GetTransitiveStrictMatchInterval> & VMI = context.VMI;
                                        libmaus2::autoarray::AutoArray<std::pair<uint16_t,uint16_t> > & A16 = context.A16;
                                        libmaus2::autoarray::AutoArray< uint8_t > & BSF = context.BSF;
                                        libmaus2::autoarray::AutoArray< uint8_t > & BSR = context.BSR;
                                        libmaus2::autoarray::AutoArray<std::pair<uint64_t,uint64_t> > & P = context.P;
					libmaus2::lcs::AlignmentTraceContainer & ATCsubAB = context.ATCsubAB;
					libmaus2::lcs::AlignmentTraceContainer & ATCsubBC = context.ATCsubBC;

					// B interval on OAB
					libmaus2::math::IntegerInterval<int64_t> IAB(OAB.bbpos,static_cast<int64_t>(OAB.bepos)-1);
					// A interval on OBC
					libmaus2::math::IntegerInterval<int64_t> IBC(OBC.abpos,static_cast<int64_t>(OBC.aepos)-1);
					libmaus2::math::IntegerInterval<int64_t> I = IAB.intersection(IBC);

					if ( I.isEmpty() )
					{
						return
							GetTransitiveStrictResultLowAlloc(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								BSF.begin(),
								BSF.begin(),
								BSR.begin(),
								BSR.begin()
							);
					}

					// decode reads
					uint8_t const * AB_ua = applyInverse(UA,a,la,false            );
					uint8_t const * AB_ub = applyInverse(UB,b,lb,libmaus2::dazzler::align::OverlapData::getInverseFlag(OVLAB));

					int64_t left_b_start = I.from;
					int64_t left_b_end = I.from + I.diameter();
					if ( (OAB.aread & 1) != 0 )
					{
						std::swap(left_b_start,left_b_end);
						left_b_start = lb - left_b_start;
						left_b_end = lb - left_b_end;
					}

					typedef libmaus2::dazzler::align::OverlapData::TraceIndex TraceIndex;
					std::pair<TraceIndex,TraceIndex> const OAB_straight_A_sub_B = libmaus2::dazzler::align::OverlapData::subIntervalB(left_b_start,left_b_end,OVLAB,P,tspace);
					libmaus2::dazzler::align::OverlapInfo OAB_straight_A_sub_B_OI(
						(OAB.aread & 1) ? (OAB.aread^1) : OAB.aread,
						(OAB.aread & 1) ? (OAB.bread^1) : OAB.bread,
						OAB_straight_A_sub_B.first.a,
						OAB_straight_A_sub_B.second.a,
						OAB_straight_A_sub_B.first.b,
						OAB_straight_A_sub_B.second.b
					);
					libmaus2::dazzler::align::OverlapData::subTrace(OAB_straight_A_sub_B,OVLAB,P,tspace,AB_ua,AB_ub,ATCsubAB,np);
					if ( (OAB.aread & 1) != 0 )
					{
						std::reverse(ATCsubAB.ta,ATCsubAB.te);
						OAB_straight_A_sub_B_OI = OAB_straight_A_sub_B_OI.inverse(la,lb);
					}

					assert ( OAB_straight_A_sub_B_OI.aread == OAB.aread );
					assert ( OAB_straight_A_sub_B_OI.bread == OAB.bread );

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_a_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCsubAB.ta,ATCsubAB.te,I.from - OAB_straight_A_sub_B_OI.bbpos);
					assert ( adv_a_0.first == I.from - OAB_straight_A_sub_B_OI.bbpos );
					std::pair<int64_t,int64_t> sl_a_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCsubAB.ta,ATCsubAB.ta + adv_a_0.second);
					std::pair<int64_t,int64_t> adv_a_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCsubAB.ta + adv_a_0.second,ATCsubAB.te,I.diameter());
					assert ( adv_a_1.first == I.diameter() );
					std::pair<int64_t,int64_t> sl_a_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCsubAB.ta + adv_a_0.second, ATCsubAB.ta + adv_a_0.second + adv_a_1.second);

					int64_t const abpos = OAB_straight_A_sub_B_OI.abpos + sl_a_0.first;
					int64_t const aepos = abpos                         + sl_a_1.first;

					// trace interval for (A,B)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_ta = ATCsubAB.ta + adv_a_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * AB_te = AB_ta       + adv_a_1.second;

					#if defined(GETTRANSITIVESTRICTLOWALLOC_DEBUG)
					// compute trace
					libmaus2::dazzler::align::OverlapData::computeTrace(OVLAB,A16,tspace,AB_ua,AB_ub,ATCA,np);
					// OVLAB.computeTrace(AB_ua,AB_ub,tspace,ATCA,np);

					// apply RC if necessary
					if ( (OAB.aread & 1) != 0 )
						std::reverse(ATCA.ta,ATCA.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> full_adv_a_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta,ATCA.te,I.from - OAB.bbpos);
					std::pair<int64_t,int64_t> full_sl_a_0  = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta,ATCA.ta + full_adv_a_0.second);
					std::pair<int64_t,int64_t> full_adv_a_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxB(ATCA.ta + full_adv_a_0.second,ATCA.te,I.diameter());
					std::pair<int64_t,int64_t> full_sl_a_1  = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCA.ta + full_adv_a_0.second, ATCA.ta + full_adv_a_0.second + full_adv_a_1.second);

					// trace interval for (A,B)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * full_AB_ta = ATCA.ta    + full_adv_a_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * full_AB_te = full_AB_ta + full_adv_a_1.second;

					// interval on A
					int64_t const full_abpos = OAB.abpos + full_sl_a_0.first;
					int64_t const full_aepos = abpos     + full_sl_a_1.first;

					std::cerr << "[abpos,aepos)          =[" << abpos << "," << aepos << ")" << std::endl;
					std::cerr << "[full_abpos,full_aepos)=[" << full_abpos << "," << full_aepos << ")" << std::endl;

					std::cerr << "AB_te-AB_ta = " << (AB_te-AB_ta) << " full_AB_te-full_AB_ta=" << (full_AB_te-full_AB_ta) << std::endl;
					for ( int64_t i = 0; i < (AB_te-AB_ta); ++i )
						std::cerr << AB_ta[i];
					std::cerr << std::endl;
					for ( int64_t i = 0; i < (full_AB_te-full_AB_ta); ++i )
						std::cerr << full_AB_ta[i];
					std::cerr << std::endl;

					assert ( (AB_te-AB_ta) == (full_AB_te-full_AB_ta) );
					for ( int64_t i = 0; i < (AB_te-AB_ta); ++i )
						assert ( AB_ta[i] == full_AB_ta[i] );
					assert ( abpos == full_abpos );
					assert ( aepos == full_aepos );
					#endif

					// decode reads
					uint8_t const * BC_ua = applyInverse(UA,b,lb,false            );
					uint8_t const * BC_ub = applyInverse(UB,c,lc,libmaus2::dazzler::align::OverlapData::getInverseFlag(OVLBC));

					int64_t right_a_start = I.from;
					int64_t right_a_end   = I.from + I.diameter();
					if ( (OBC.aread & 1) != 0 )
					{
						std::swap(right_a_start,right_a_end);
						right_a_start = lb - right_a_start;
						right_a_end   = lb - right_a_end;
					}
					std::pair<TraceIndex,TraceIndex> const OBC_straight_B_sub_A = libmaus2::dazzler::align::OverlapData::subIntervalA(right_a_start,right_a_end,OVLBC,P,tspace);

					//
					libmaus2::dazzler::align::OverlapInfo OBC_straight_B_sub_A_OI(
						(OBC.aread & 1) ? (OBC.aread^1) : OBC.aread,
						(OBC.aread & 1) ? (OBC.bread^1) : OBC.bread,
						OBC_straight_B_sub_A.first.a,
						OBC_straight_B_sub_A.second.a,
						OBC_straight_B_sub_A.first.b,
						OBC_straight_B_sub_A.second.b
					);
					libmaus2::dazzler::align::OverlapData::subTrace(OBC_straight_B_sub_A,OVLBC,P,tspace,BC_ua,BC_ub,ATCsubBC,np);
					if ( (OBC.aread & 1) != 0 )
					{
						std::reverse(ATCsubBC.ta,ATCsubBC.te);
						OBC_straight_B_sub_A_OI = OBC_straight_B_sub_A_OI.inverse(lb,lc);
					}

					assert ( OBC_straight_B_sub_A_OI.aread == OBC.aread );
					assert ( OBC_straight_B_sub_A_OI.bread == OBC.bread );

					// compute interval for intersection
					std::pair<int64_t,int64_t> adv_c_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCsubBC.ta,ATCsubBC.te,I.from - OBC_straight_B_sub_A_OI.abpos);
					assert ( adv_c_0.first == I.from - OBC_straight_B_sub_A_OI.abpos );
					std::pair<int64_t,int64_t> sl_c_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCsubBC.ta,ATCsubBC.ta + adv_c_0.second);
					std::pair<int64_t,int64_t> adv_c_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCsubBC.ta + adv_c_0.second,ATCsubBC.te,I.diameter());
					assert ( adv_c_1.first == I.diameter() );
					std::pair<int64_t,int64_t> sl_c_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCsubBC.ta + adv_c_0.second, ATCsubBC.ta + adv_c_0.second + adv_c_1.second);

					int64_t const cbpos = OBC_straight_B_sub_A_OI.bbpos + sl_c_0.second;
					int64_t const cepos = cbpos                         + sl_c_1.second;

					// trace interval for (A,B)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_ta = ATCsubBC.ta + adv_c_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * BC_te = BC_ta       + adv_c_1.second;

					#if defined(GETTRANSITIVESTRICTLOWALLOC_DEBUG)
					// compute trace
					// OVLBC.computeTrace(BC_ua,BC_ub,tspace,ATCB,np);
					libmaus2::dazzler::align::OverlapData::computeTrace(OVLBC,A16,tspace,BC_ua,BC_ub,ATCB,np);

					if ( (OBC.aread & 1) != 0 )
						std::reverse(ATCB.ta,ATCB.te);

					// compute interval for intersection
					std::pair<int64_t,int64_t> full_adv_c_0 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta,ATCB.te,I.from - OBC.abpos);
					std::pair<int64_t,int64_t> full_sl_c_0 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta,ATCB.ta + full_adv_c_0.second);
					std::pair<int64_t,int64_t> full_adv_c_1 = libmaus2::lcs::AlignmentTraceContainer::advanceMaxA(ATCB.ta + full_adv_c_0.second,ATCB.te,I.diameter());
					std::pair<int64_t,int64_t> full_sl_c_1 = libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCB.ta + full_adv_c_0.second, ATCB.ta + full_adv_c_0.second + full_adv_c_1.second);

					// trace interval for (B,C)
					libmaus2::lcs::AlignmentTraceContainer::step_type const * full_BC_ta = ATCB.ta    + full_adv_c_0.second;
					libmaus2::lcs::AlignmentTraceContainer::step_type const * full_BC_te = full_BC_ta + full_adv_c_1.second;

					// interval on C
					int64_t const full_cbpos = OBC.bbpos  + full_sl_c_0.second;
					int64_t const full_cepos = full_cbpos + full_sl_c_1.second;

					std::cerr << "[cbpos,cepos)          =[" << cbpos << "," << cepos << ")" << std::endl;
					std::cerr << "[full_cbpos,full_cepos)=[" << full_cbpos << "," << full_cepos << ")" << std::endl;

					std::cerr << "BC_te-BC_ta = " << (BC_te-BC_ta) << " full_BC_te-full_BC_ta=" << (full_BC_te-full_BC_ta) << std::endl;
					for ( int64_t i = 0; i < (BC_te-BC_ta); ++i )
						std::cerr << BC_ta[i];
					std::cerr << std::endl;
					for ( int64_t i = 0; i < (full_BC_te-full_BC_ta); ++i )
						std::cerr << full_BC_ta[i];
					std::cerr << std::endl;

					assert ( (BC_te-BC_ta) == (full_BC_te-full_BC_ta) );
					for ( int64_t i = 0; i < (BC_te-BC_ta); ++i )
						assert ( BC_ta[i] == full_BC_ta[i] );
					assert ( cbpos == full_cbpos );
					assert ( cepos == full_cepos );
					#endif

					// sanity check
					assert (
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(AB_ta,AB_te).second
						==
						libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(BC_ta,BC_te).first
					);

					// position pair on A,B
					int64_t ab_apos = abpos;
					int64_t ab_bpos = I.from;

					// position pair on B,C
					int64_t bc_bpos = I.from;
					int64_t bc_cpos = cbpos;

					uint64_t VMo = 0;
					if ( padaleft )
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(0,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(0,bc_cpos));
					}
					else
					{
						if ( padbleft )
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,0));
						else
							VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
					}

					while ( AB_ta != AB_te && BC_ta != BC_te )
					{
						if ( ab_bpos < bc_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									ab_apos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									ab_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									ab_apos++;
									ab_bpos++;
									break;
								default:
									break;
							}
						}
						else if ( bc_bpos < ab_bpos )
						{
							libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(BC_ta++);

							switch ( step )
							{
								case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
									bc_bpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
									bc_cpos++;
									break;
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
								case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
									bc_bpos++;
									bc_cpos++;
									break;
								default:
									break;
							}
						}
						else
						{
							assert ( ab_bpos == bc_bpos );

							libmaus2::lcs::AlignmentTraceContainer::step_type const step_ab = *AB_ta;
							libmaus2::lcs::AlignmentTraceContainer::step_type const step_bc = *BC_ta;

							// first try to keep bpos in sync by not incrementing it
							if ( step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_DEL )
							{
								++AB_ta;
								ab_apos++;
							}
							else if ( step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_INS )
							{
								++BC_ta;
								bc_cpos++;
							}
							// we have to increment bpos in some form
							else
							{
								bool const ab_match =
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_ab == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;
								bool const bc_match =
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH
									||
									step_bc == libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH;

								if ( ab_match && bc_match )
								{
									// match

									VM.push(VMo,GetTransitiveStrictMatch(ab_apos,bc_cpos));
									// std::cerr << "[V] match* (" << ab_apos << "," << bc_cpos << ")" << std::endl;

									++AB_ta;
									++BC_ta;
									ab_apos++;
									ab_bpos++;
									bc_bpos++;
									bc_cpos++;
								}
								else
								{
									libmaus2::lcs::AlignmentTraceContainer::step_type const step = *(AB_ta++);

									switch ( step )
									{
										case libmaus2::lcs::AlignmentTraceContainer::STEP_DEL:
											assert ( false );
											ab_apos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_INS:
											ab_bpos++;
											break;
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MATCH:
										case libmaus2::lcs::AlignmentTraceContainer::STEP_MISMATCH:
											ab_apos++;
											ab_bpos++;
											break;
										default:
											break;
									}

								}
							}
						}
					}

					if ( padaright )
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(la,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(la,cepos));
					}
					else
					{
						if ( padbright )
							VM.push(VMo,GetTransitiveStrictMatch(aepos,lc));
						else
							VM.push(VMo,GetTransitiveStrictMatch(aepos,cepos));
					}

					uint64_t VMIo = 0;
					for ( uint64_t i = 1; i < VMo; ++i )
						VMI.push(VMIo,GetTransitiveStrictMatchInterval(VM[i-1].apos,VM[i].apos));

					libmaus2::dazzler::align::OverlapInfo const OI(OAB.aread,OBC.bread,
						padaleft  ? 0  : abpos,
						padaright ? la : aepos,
						padbleft  ? 0  : cbpos,
						padbright ? lc : cepos
					);

					if ( (OI.aepos-OI.abpos == 0) || (OI.bepos-OI.bbpos == 0) )
					{
						return
							GetTransitiveStrictResultLowAlloc(
								libmaus2::dazzler::align::OverlapInfo(
									OAB.aread,
									OBC.bread,
									0,0,0,0
								),
								BSF.begin(),
								BSF.begin(),
								BSR.begin(),
								BSR.begin()
							);
					}

					uint64_t low = 0;
					uint64_t const thres = 100;
					ATCC.reset();

					uint8_t const * za = applyInverse(UA,a,la,OAB.aread&1);
					uint8_t const * zc = applyInverse(UB,c,lc,OBC.bread&1);

					while ( low < VMIo )
					{
						uint64_t high = low+1;
						uint64_t s = VMI[low].diam();

						while (
							high < VMIo
							&&
							s + VMI[high].diam() <= thres
						)
						{
							s += VMI[high++].diam();
						}

						uint64_t const abpos = VM[low].apos;
						uint64_t const aepos = VM[high].apos;
						uint64_t const cbpos = VM[low].bpos;
						uint64_t const cepos = VM[high].bpos;

						np.np(
							za+abpos,
							za+aepos,
							zc+cbpos,
							zc+cepos
						);

						ATCC.push(np);

						// std::cerr << "[V] using window [" << abpos << "," << aepos << ") [" << cbpos << "," << cepos << ")" << " " << np.getAlignmentStatistics() << std::endl;

						low = high;
					}

					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).first)  == static_cast<int64_t>(OI.aepos - OI.abpos) );
					assert ( static_cast<int64_t>(libmaus2::lcs::AlignmentTraceContainer::getStringLengthUsed(ATCC.ta,ATCC.te).second) == static_cast<int64_t>(OI.bepos - OI.bbpos) );
					assert ( libmaus2::lcs::AlignmentTraceContainer::checkAlignment(ATCC.ta,ATCC.te,za+OI.abpos,zc+OI.bbpos) );
					// std::cerr << ATCC.getAlignmentStatistics() << std::endl;

					libmaus2::dazzler::align::OverlapInfo OIF = OI;
					if ( (OIF.aread & 1) != 0 )
					{
						OIF = OIF.inverse(la,lc);
						std::reverse(ATCC.ta,ATCC.te);
					}

					uint64_t const BSFo = libmaus2::dazzler::align::OverlapData::putOverlap(
						OIF.aread/2,
						OIF.bread/2,
						(OIF.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIF.abpos,
						OIF.aepos,
						OIF.bbpos,
						OIF.bepos,
						tspace,
						ATCC,
						A16,
						BSF
					);

					libmaus2::dazzler::align::OverlapInfo OIR = OIF.swapped();
					ATCC.swapRoles();
					if ( (OIR.aread & 1) != 0 )
					{
						OIR = OIR.inverse(lc,la);
						std::reverse(ATCC.ta,ATCC.te);
					}

					uint64_t const BSRo = libmaus2::dazzler::align::OverlapData::putOverlap(
						OIR.aread/2,
						OIR.bread/2,
						(OIR.bread&1) ? libmaus2::dazzler::align::Overlap::getInverseFlag() : 0,
						OIR.abpos,
						OIR.aepos,
						OIR.bbpos,
						OIR.bepos,
						tspace,
						ATCC,
						A16,
						BSR
					);

					return GetTransitiveStrictResultLowAlloc(
						OI,
						BSF.begin(),BSF.begin()+BSFo,
						BSR.begin(),BSR.begin()+BSRo
					);
				}


				static GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					libmaus2::dazzler::align::Overlap const & OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					libmaus2::dazzler::align::Overlap const & OVLBC,
					uint8_t const * a,
					uint8_t const * b,
					uint8_t const * c,
					int64_t const tspace,
					GetTransitiveStrictContext & context,
					std::vector<uint64_t> const & RL,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					uint64_t const la = RL[OVLAB.aread];
					uint64_t const lb = RL[OVLAB.bread];
					uint64_t const lc = RL[OVLBC.bread];

					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,la,b,lb,c,lc,tspace,context,padaleft,padaright,padbleft,padbright);

				}

				static GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					uint8_t const * OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					uint8_t const * OVLBC,
					uint8_t const * a,
					uint8_t const * b,
					uint8_t const * c,
					int64_t const tspace,
					GetTransitiveStrictContext & context,
					std::vector<uint64_t> const & RL,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					uint64_t const la = RL[libmaus2::dazzler::align::OverlapData::getARead(OVLAB)];
					uint64_t const lb = RL[libmaus2::dazzler::align::OverlapData::getBRead(OVLAB)];
					uint64_t const lc = RL[libmaus2::dazzler::align::OverlapData::getBRead(OVLBC)];

					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,la,b,lb,c,lc,tspace,context,padaleft,padaright,padbleft,padbright);

				}

				/**
				 * given alignments (A,B) and (B,C) as designated by OAB and OBC compute (A,C)
				 * using OVLAB and OVLBC to speed up trace computation
				 **/
				GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					libmaus2::dazzler::align::Overlap const & OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					libmaus2::dazzler::align::Overlap const & OVLBC,
					uint8_t const * a,
					uint8_t const * b,
					uint8_t const * c,
					int64_t const tspace,
					std::vector<uint64_t> const & RL,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,b,c,tspace,context,RL,padaleft,padaright,padbleft,padbright);
				}

				/**
				 * given alignments (A,B) and (B,C) as designated by OAB and OBC compute (A,C)
				 * using OVLAB and OVLBC to speed up trace computation
				 **/
				GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					uint8_t const * OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					uint8_t const * OVLBC,
					uint8_t const * a,
					uint8_t const * b,
					uint8_t const * c,
					int64_t const tspace,
					std::vector<uint64_t> const & RL,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,b,c,tspace,context,RL,padaleft,padaright,padbleft,padbright);
				}

				/**
				 * given alignments (A,B) and (B,C) as designated by OAB and OBC compute (A,C)
				 * using OVLAB and OVLBC to speed up trace computation
				 **/
				GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					libmaus2::dazzler::align::Overlap const & OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					libmaus2::dazzler::align::Overlap const & OVLBC,
					uint8_t const * a,
					uint64_t const la,
					uint8_t const * b,
					uint64_t const lb,
					uint8_t const * c,
					uint64_t const lc,
					int64_t const tspace,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,la,b,lb,c,lc,tspace,context,padaleft,padaright,padbleft,padbright);
				}

				/**
				 * given alignments (A,B) and (B,C) as designated by OAB and OBC compute (A,C)
				 * using OVLAB and OVLBC to speed up trace computation
				 **/
				GetTransitiveStrictResult getTransitiveStrict(
					libmaus2::dazzler::align::OverlapInfo const & OAB,
					uint8_t const * OVLAB,
					libmaus2::dazzler::align::OverlapInfo const & OBC,
					uint8_t const * OVLBC,
					uint8_t const * a,
					uint64_t const la,
					uint8_t const * b,
					uint64_t const lb,
					uint8_t const * c,
					uint64_t const lc,
					int64_t const tspace,
					bool const padaleft,
					bool const padaright,
					bool const padbleft,
					bool const padbright
				)
				{
					return getTransitiveStrict(OAB,OVLAB,OBC,OVLBC,a,la,b,lb,c,lc,tspace,context,padaleft,padaright,padbleft,padbright);
				}
			};
		}
	}
}
#endif
