/*
 * paralleltraversal.cpp
 *
 *  Created on: Sep 11, 2024
 *      Author: au27818
 */


#include <memory>
#include <iostream>
#include <algorithm>
#include <unistd.h>
#include <mutex>
#include <semaphore>
#include <thread>
#include <optional>
#include <string_view>
#include "symmetrictraversal.h"


//includes for application
#include "vektor.h"
#include "printer.h"
#include "parser.h"
#include "gfanapplication.h"
#include "minkowskisum.h"
#include "newtonpolytope.h"
#include "buchberger.h"
#include "wallideal.h"
#include "lp.h"
#include "tropical.h"
#include "division.h"
#include "bergman.h"
#include "tropical2.h"
#include "dimension.h"
#include "timer.h"
#include "log.h"
#include "linalg.h"
#include "tropicaltraverse.h"
#include "traverser_tropical.h"
#include "symmetrictraversal.h"
#include "traverser_stableintersection.h"



namespace gfan{

namespace parallelTraversal{

class EnumerationData //at thread level
{
public:
	IntegerVectorList backVectorStack;
	ConeTraverser* theTraverser;
};

class Vertex
{
public://to do: declare Boundary to be friend
	IntegerVector v;
	bool isFacet;
public:
	void output()const
	{
		if(isFacet)
			debug<<"Facet, unique point:"<<v<<"\n";
		else
			debug<<"Ridge, unique point:"<<v<<"\n";
	}
	std::vector<class Arc> incidentArcs(EnumerationData const &eData)const;
	Vertex(IntegerVector const &v_, bool isFacet_):
		v(v_),
		isFacet(isFacet_)
	{
	}
};

class Arc
{
public://to do: declare Boundary to be friend
	IntegerVector const ridge,ray,facetTag;//facet tag is valid if the arc was produced by facet. In this case it is the unique point of the facet
	bool fromFacetToRidge;
public:
	Arc(IntegerVector const &ridge_, IntegerVector const &ray_, bool fromFacetToRidge_, IntegerVector const &facetTag_):
		ridge(ridge_),
		ray(ray_),
		fromFacetToRidge(fromFacetToRidge_),
		facetTag(facetTag_)
	{

	}
	Vertex followAndReturnHead(EnumerationData &eData)const
	{
		if(fromFacetToRidge)
		{
			debug<<"follow from facet to ridge\n";
			auto &C=eData.theTraverser->refToPolyhedralCone();
			auto temp=C.link(ridge);
			temp.canonicalize();
			eData.backVectorStack.push_back(temp.getUniquePoint());
			return Vertex(ridge,false);
		}
		else
		{
			debug<<"follow from ridge to facet\n";
			eData.theTraverser->changeCone(ridge,ray);
			auto theCone=eData.theTraverser->refToPolyhedralCone();
			return Vertex(theCone.getUniquePoint(),true);
		}
	}
	void goBack(EnumerationData &eData)const
	{
		if(fromFacetToRidge)
		{// we now go from ridge to facet
			IntegerVector backVector=eData.backVectorStack.back();eData.backVectorStack.pop_back();
			eData.theTraverser->changeCone(ridge,backVector);
		}
		else
		{// we now go from facet to ridge
		}
	}
};

std::vector<class Arc> Vertex::incidentArcs(EnumerationData const &eData)const
{
	std::vector<class Arc> ret;
	auto &C=eData.theTraverser->refToPolyhedralCone();
	if(isFacet)
	{
		C.findFacets();
		C.canonicalize();
		auto facetNormals=C.getHalfSpaces();
		for(auto &v:facetNormals)
		{
			IntegerVectorList l;l.push_back(v);
			auto H=PolyhedralCone(IntegerVectorList(),l);
			auto ridge=intersection(C,H);
			ridge.canonicalize();
			auto ridgePoint=ridge.getUniquePoint();
			auto L=C.link(ridgePoint);
			L.canonicalize();
			auto rayPoint=L.getUniquePoint();
			ret.emplace_back(ridgePoint,rayPoint,true,C.getUniquePoint());
		}
	}
	else
	{
		auto L=eData.theTraverser->link(v);
		auto linealitySpaceGenerators=C.generatorsOfLinealitySpace();
		for(auto &r:L)
		{
			IntegerVectorList rlist;rlist.push_back(r);
			auto temp=PolyhedralCone(rlist,linealitySpaceGenerators);
			auto R=temp.dualCone();
			R.canonicalize();
			ret.emplace_back(v,R.getUniquePoint(),false,IntegerVector());
		}
	}
	return ret;
}


class Boundary{ //Shared by all threads in the enumeration
	mutex theMutex;
	class StoredArc{
	public:
		IntegerVector ridge,ray,facetTag;
		bool fromRidgeToFacet;
		bool markedAsBeingFollowed;
		StoredArc(IntegerVector ridge_, IntegerVector ray_, IntegerVector facetTag_, bool fromRidgeToFacet_):
			ridge(ridge_),
			ray(ray_),
			facetTag(facetTag_),
			fromRidgeToFacet(fromRidgeToFacet_),
			markedAsBeingFollowed(false)
		{
		}
	};
//	typedef pair<IntegerVector,IntegerVector> E;//ridge,ray
//	map<E,pair<bool,bool>> theSet;//the first bool marks if the arc needs to be followed from ridge to ray. The second if is used for marking the arc as being followed.

	vector<StoredArc> theSet;
	set<IntegerVector> markings;//vertex
	/**
	 * It seems that there should be two types of markings:
	 *  1) an arc should be marked if it has been taken by a thread. This is to make sure that another thread would not take the same arc.
	 *  2) a vertex should be marked when its identity is known. This is to make sure that no other thread would output the vertex if it reaches it.
	 */
public:
	void print()
	{
		scoped_lock<mutex> lock(theMutex);
		auto &s=std::cerr;
		s<<"Boundary:\n";
		s<<"theSet.size()="<<theSet.size()<<"\n";
		for(auto &ef:theSet)
		{
			debug<<ef.ridge<<ef.ray<<ef.facetTag<<"fromRidgeToFacet:"<<ef.fromRidgeToFacet<<ef.markedAsBeingFollowed<<"\n";
//			debug<<ef.first.first<<ef.first.second<<ef.second.first<<ef.second.second<<"\n";
//			s<<ef.first.first.
		}
	}
	//	Vertex followAndReturnHead()const;
	bool notMarkedAndMark(Vertex const &v)
	{
		scoped_lock<mutex> lock(theMutex);
		bool ret=markings.count(v.v);
		if(!ret)markings.insert(v.v);
		return ret==0;
	}
	void doSymmetricDifference(vector<class Arc> const &L)
	{
		scoped_lock<mutex> lock(theMutex);
		for(auto &a:L)
		{
			auto erased=std::erase_if(theSet,[a](StoredArc &A){return (a.ray==A.ray && a.ridge==A.ridge);});
			if(!erased)
			{
				theSet.push_back(StoredArc(a.ridge,a.ray,a.facetTag,!a.fromFacetToRidge));
			}
		}
	}
	std::optional<Arc> getArcAt(Vertex const &v)//marks arc as being processed
	{
		scoped_lock<mutex> lock(theMutex);
		debug<<"getArcAt with isFacet="<<v.isFacet<<"\n";
		if(v.isFacet)
		{
			debug<<"v.v="<<v.v<<"\n";

			for(auto &a:theSet)
			{
				if(a.facetTag==v.v && !a.markedAsBeingFollowed)
				{
					a.markedAsBeingFollowed=true;
					return Arc(a.ridge,a.ray,!a.fromRidgeToFacet,v.v);
				}
			}
		}
		else
		{
			auto ridgeVector=v.v;
			for(auto &a:theSet)
			{
				if(a.ridge==ridgeVector && !a.markedAsBeingFollowed)
				{
					a.markedAsBeingFollowed=true;
					return Arc(ridgeVector,a.ray,!a.fromRidgeToFacet,IntegerVector());
				}
			}
		}
		return nullopt;
	}
	std::optional<Arc> getArc(Vertex const &v)//marks arc as being processed  //will choose arc at v if possible
	{
		debug<<"getArc called\n";
		auto temp=getArcAt(v);
		scoped_lock<mutex> lock(theMutex);
		debug<<"getArcAt successful:"<<bool(temp)<<"\n";
		if(temp)return temp;
		cerr<<"We look for other arcs\n";
		debug<<"going from facets to ridges in\n";
//		print();//locks!

		{
			for(auto &a:theSet)
				if(!a.fromRidgeToFacet)
			{
				if(!a.markedAsBeingFollowed)
				{
					a.markedAsBeingFollowed=true;
					debug<<"Warp needed.\n";
					assert(0);
					return Arc(a.ridge,a.ray,a.fromRidgeToFacet,a.facetTag);
				}
			}
		}
		debug<<"going from ridges to facets\n";
		{
			for(auto &a:theSet)
				if(a.fromRidgeToFacet)
			{
				if(!a.markedAsBeingFollowed)
				{
					a.markedAsBeingFollowed=true;
					debug<<"Warp needed.\n";
					assert(0);
					return Arc(a.ridge,a.ray,a.fromRidgeToFacet,a.facetTag);
				}
			}
		}
		debug<<"No arc found. Does a semaphore guarantee that this only happens at the end?\n";
		return nullopt;
	}
};

void rek(Boundary &B, EnumerationData &eData, Arc const &a)
{
	debug<<"rek called\n";
	debug<<"with arc"<<a.ridge<<a.ray<<a.facetTag<<a.fromFacetToRidge<<"\n";
	auto v=a.followAndReturnHead(eData);
	debug<<"followed to "<<v.isFacet<<v.v<<"\n";
	if(B.notMarkedAndMark(v))
	{
		v.output();
		auto star=v.incidentArcs(eData);
		B.doSymmetricDifference(star);
		B.print();

//		std::optional<Arc> arc;
		while(auto arc=B.getArcAt(v))
			rek(B,eData,*arc);
	}
	a.goBack(eData);
}


void program(Boundary *B, EnumerationData *eData, Vertex v)
{
//	EnumerationData &eData; Vertex v;//!!!!!!!!!!!s
	while(auto arc=B->getArc(v))
		rek(*B,*eData,*arc);
}

void traverse(Boundary &B, vector<EnumerationData> &eData, Vertex const &v)
{
	assert(eData.size());

	auto l=v.incidentArcs(eData[0]);
	B.doSymmetricDifference(l);
	B.print();

	int N=eData.size();
	std::vector<std::thread> threads;
	threads.reserve(N);
	for(int i=0;i<N;i++)threads.emplace_back(std::thread(program,&B,&(eData[i]),v));
	for(auto &t:threads){t.join();}
}







class ParallelTraverseApplication : public GFanApplication
{
  //  SimpleOption optionPerformanceTimer;
  SimpleOption optionSymmetry;
  //  SimpleOption optionNoIncidencePrinting;
  SimpleOption optionTorusSymmetry;
  SimpleOption optionIgnoreCones;
  SimpleOption optionDisableSymmetryTest;
  //SimpleOption optionStableIntersection;
  //  SimpleOption optionInitialIdealPrinting;
  //  SimpleOption optionLargeDimensionalPrinting;
  InterruptOption optionInterrupt;
public:
  bool includeInDefaultInstallation()
  {
    return true;
  }
  const char *helpText()
  {
    return "This program computes a polyhedral fan representation of the tropical variety of a homogeneous prime ideal $I$ IN PARALLEL??. Let $d$ be the Krull dimension of $I$ and let $\\omega$ be a relative interior point of $d$-dimensional Groebner cone contained in the tropical variety. The input for this program is a pair of marked reduced Groebner bases with respect to the term order represented by $\\omega$, tie-broken in some way. The first one is for the initial ideal $in_\\omega(I)$ the second one for $I$ itself. The pair is the starting point for a traversal of the $d$-dimensional Groebner cones contained in the tropical variety. If the ideal is not prime but with the tropical variety still being pure $d$-dimensional the program will only compute a codimension $1$ connected component of the tropical variety.\n";
  }
  ParallelTraverseApplication():
    //    optionPerformanceTimer("-T","Enable performance timer"),
    optionSymmetry("--symmetry","Do computations up to symmetry and group the output accordingly. If this option is used the program will read in a list of generators for a symmetry group after the pair of Groebner bases have been read. Two advantages of using this option is that the output is nicely grouped and that the computation can be done faster."),
    //    optionNoIncidencePrinting("--noincidence","Disables printing of incidence information. This will avoid extracting the full fan from its orbits and avoid computation of the lower dimensional faces. The output is a list of orbits of maximal cones and a list of orbits of codimension 1 cones."),
    // optionInitialIdealPrinting("--initialideals","Print the initial ideals during incidence printing ONLY without --noincidence."),
    //    optionLargeDimensionalPrinting("--largedimensional","Print the full dimensional cones and the codimension 1 cones computed in the traversal.")
    optionDisableSymmetryTest("--disableSymmetryTest","When using --symmetry this option will disable the check that the group read off from the input actually is a symmetry group with respect to the input ideal.\n"),
    optionTorusSymmetry("--symsigns","Specify for each generator of the symmetry group an element of ${-1,+1}^n$ which by its multiplication on the variables together with the permutation will keep the ideal fixed. The vectors are given as the rows of a matrix."),
    optionIgnoreCones("--nocones","Tells the program not to output the CONES and MAXIMAL_CONES sections, but still output CONES_ORBITS and MAXIMAL_CONES_ORBITS if --symmetry is used.")
    //optionStableIntersection("--stable","Traverse the stable intersection or, equivalently, pretend that the coefficients are genereric.")
  {
    registerOptions();
  }
  const char *name()
  {
    return "_paralleltraverse";
  }
  int main()
  {
    PolynomialSetList tropical;

    //    lpSetSolver("cddgmp");
    FileParser P(Stdin);

    AsciiPrinter p(Stdout);
    PolynomialSet coneGroebnerBasis=P.parsePolynomialSetWithRing();
    PolynomialSet idealGroebnerBasis=P.parsePolynomialSet(coneGroebnerBasis.getRing());
    coneGroebnerBasis.changeNumberOfVariables(idealGroebnerBasis.getRing());

    SymmetryGroup s(idealGroebnerBasis.numberOfVariablesInRing());
    if(optionSymmetry.getValue())
      {
	IntegerVectorList generators=P.parseIntegerVectorList();
	FieldMatrix *torusAction=0;
	FieldMatrix m(idealGroebnerBasis.getRing().getField(),0,0);
	if(optionTorusSymmetry.getValue())
	  {
		IntegerMatrix symSigns=rowsToIntegerMatrix(P.parseIntegerVectorList());
		for(int i=0;i<symSigns.getHeight();i++)
			for(int j=0;j<symSigns.getWidth();j++)
			{
				assert(symSigns[i][j]==1 || symSigns[i][j]==-1);
			}
	    m=integerMatrixToFieldMatrix(symSigns,idealGroebnerBasis.getRing().getField());
	    torusAction=&m;
	  }

	if(/*!optionStableIntersection.getValue()&&*/!optionDisableSymmetryTest.getValue())
    	assertSymmetriesMatch(generators,idealGroebnerBasis,torusAction);
	s.computeClosure(generators);
	  s.createTrie();

	  log2 s.print(Stderr);
	log2 fprintf(Stderr,"\n");
      }
    if(optionTorusSymmetry.getValue() && !optionSymmetry.getValue())
      {
	fprintf(Stderr,"Option --torus can only be used in combination with option --symmetry.\n");
      }

    int n=idealGroebnerBasis.numberOfVariablesInRing();
    PolyhedralCone hspace=homogeneitySpace(idealGroebnerBasis);
    IntegerVectorList hv=hspace.dualCone().getEquations();
    int h=hv.size();
    int d=krullDimension(idealGroebnerBasis);

    log1 fprintf(Stderr,"Ambient dimension: %i\n",n);
    log1 fprintf(Stderr,"Dimension of homogeneity space: %i\n",h);
    log1 fprintf(Stderr,"Dimension of tropical variety: %i\n",d);

    PolyhedralFan p1(0);

    {
    	int N=2;
    	vector<TropicalTraverser> traversers;
    	for(int i=0;i<N;i++)traversers.emplace_back(coneGroebnerBasis,idealGroebnerBasis);
    	{
    		Boundary B;
    		vector<EnumerationData> eData(N);
    		for(int i=0;i<N;i++)eData[i].theTraverser=&(traversers[i]);
    		PolyhedralCone C=traversers[0].refToPolyhedralCone();
    		C.canonicalize();
    		Vertex v(C.getUniquePoint(),true);
    		traverse(B,eData,v);
    		return 0;
    	}
/*    	TropicalTraverser traverser(coneGroebnerBasis,idealGroebnerBasis);

    	{
    		Boundary B;
    		EnumerationData eData;
    		eData.theTraverser=&traverser;
    		PolyhedralCone C=traverser.refToPolyhedralCone();
    		C.canonicalize();
    		Vertex v(C.getUniquePoint(),true);
    		traverse(B,eData,v);
    		return 0;
    	}
*/

		SymmetricTargetFanBuilder target(n,s);
		SymmetricTargetCounterInterrupted target2(target,optionInterrupt.getValue());
//		symmetricTraverse(traverser,target2,&s);
		p1=target.getFanRef();
    }

      {

	p1.removeAllLowerDimensional();

	AsciiPrinter Q(Stdout);

	p1.printWithIndices(&Q,
			    FPF_multiplicities|
			    (optionSymmetry.getValue()?FPF_group|FPF_conesCompressed:0)|
			    (optionIgnoreCones.getValue()?0:FPF_conesExpanded)|
			    FPF_maximalCones|FPF_cones,
			    &s);

      }
    return 0;
  }
};

static ParallelTraverseApplication theApplication;














};

};












std::counting_semaphore<0> S(0);

namespace parallelplayground{
using VertexName=string;

list<VertexName> computeNeighbours(VertexName v)
{
	list<VertexName> ret;
	for(int i=1;i<v.size();i++)
	{
		swap(v[i-1],v[i]);
		ret.push_back(v);
		swap(v[i-1],v[i]);
	}
	return ret;
}

class Boundary
	{
		class BoundaryEntry
		{
		public:
			VertexName name;
			list<VertexName> neighbours;//parents
			bool beingProcessed;
			BoundaryEntry(VertexName name_):
				name(name_),
				beingProcessed(false)
			{

			}
		};
		vector<BoundaryEntry> b;
		mutex M;
		int numberBeingProcessed;
		bool terminate;
public:
		Boundary():
			numberBeingProcessed(1),
			terminate(false)
		{

		}
		int boundarySize()
		{
			int ret=0;
			for(auto &v:b)
				ret+=v.neighbours.size();
			return ret;
		}
		void print()
		{
			cout<<"Size"<<b.size()<<"\n";
			for(int i=0;i<b.size();i++)
			{
				cout<<i<<":"<<b[i].name<<"\n";
				for(auto n:b[i].neighbours)
					cout<<n;
				cout<<"\n";
			}
		}
		unique_ptr<VertexName> getJob()
		{
			S.acquire();
//			std::cerr<<"\tACQUIRED\n";
			{
				scoped_lock<mutex> s(M);

				if(terminate)return 0;

				//HER skal vi lede efter ledig kant, markere, free mutex og returnere
				if(b.size())
				{
					numberBeingProcessed++;
					for(auto &a:b)
						if(!a.beingProcessed)
						{
							a.beingProcessed=true;
							return make_unique<VertexName>(a.name);
						}
				}
				assert(0);
			}
			return 0;
			// If there are jobs available, then return a job
			// If there are no more jobs and no entries being processed, then return false
			// If there are no more jobs and entries being processed, then block
		}
		void markStar(VertexName name, list<VertexName> neighbours)
		{
			int increase=0;
			{
			scoped_lock<mutex> s(M);
//			cout<<"markStar"<<name<<"\n";
			int j=-1;
			for(int i=0;i<b.size();i++)
				if(name==b[i].name)j=i;
			if(j==-1)
				b.emplace_back(name);
			for(int i=0;i<b.size();i++)
				if(name==b[i].name)j=i;
			assert(j!=-1);

//			cout<<"J:"<<j<<"\n";
			for(auto &neighbour:neighbours)
			{
				bool found=false;
		//		cout<<"nsize"<<b[j].neighbours.size()<<"\n";
				for(auto &a:b[j].neighbours)
					if(neighbour==a)
						found=true;
				if(!found)
				{
//					cout<<"NOT FOUND:"<<neighbour<<"\n";
					bool found2=false;
					for(auto &c:b)
						if(c.name==neighbour)
							found2=true;
					if(!found2)
					{
						b.emplace_back(neighbour);
						increase++;
					}
					for(auto &c:b)
						if(c.name==neighbour)
							c.neighbours.push_back(name);
//					print();
				}
				else
				{
//					cout<<"FOUND:"<<neighbour<<"\n";
			//		cout<<"looking up"<<neighbour<<"\n";
					for(auto &a:b)
						if(neighbour==a.name)
						{
							remove_if(a.neighbours.begin(),a.neighbours.end(),[name](auto x){return name==x;});
							increase--;
						}
//					print();

				}
			}

//			print();
			vector<BoundaryEntry> b2;
			for(auto &a:b)if(a.name!=name)b2.push_back(a);
			b=b2;


			numberBeingProcessed--;//We are done with this vertex
//			std::cerr<<"NumberBeingProcessed"<<numberBeingProcessed<<"boundarySize"<<boundarySize()<<"\n";
			if(numberBeingProcessed==0)
			{
				if(boundarySize()==0)
				{
					std::cerr<<"TERMINATE\n";
					terminate=true;
					S.release(10000); //this should be enough for everyone to terminate
				}
			}

			}//scoped lock
//			std::cerr<<"\tINCREASE:"<<increase<<"\n";
			S.release(increase);
		}
	};
void program(Boundary *B)
{
	unique_ptr<VertexName> job;
	int count=0;
	while((job=B->getJob()))
		{
			sleep(1);
//			b.print();
//			std::cout<<*job<<"\n";
			count++;
			B->markStar(*job,computeNeighbours(*job));
		}
	{
	std::cerr<<count<<"\n";
	}
}
	void computeComponent(VertexName root)
	{
		Boundary b;
		std::cout<<root<<"\n";
//		b.numberBeingProcessed=1;
		b.markStar(root,computeNeighbours(root));
//		b.print();
//		pardo

		if(1)
		{
			int N=16;
			std::vector<std::thread> threads;
			threads.reserve(N);
			for(int i=0;i<N;i++)threads.emplace_back(std::thread(program,&b));
			for(auto &t:threads){t.join();}
		}
		else
			program(&b);
	}
}

/*int main()
{
	std::cerr<<"test\n";
	parallelplayground::computeComponent("ABCDE");
	return 0;
}*/

#if 0
namespace gfan{


/** This class is almost the same as Boundary from symmetrictraversal.cpp, but this version need to be
 * protected by a mutex. The classes should maybe be merged together to one.
 */
class Boundary
{
  typedef pair<IntegerVector,IntegerVector> EFirst;
#if 0
  typedef pair<IntegerVectorList*,IntegerVectorList::iterator> ESecond;
#else
  struct ESecond{// L2 maybe zero, in that case i1==i2
    IntegerVectorList* L1;
    IntegerVectorList::iterator i1;
    IntegerVectorList *L2;
    IntegerVectorList::iterator i2;
    ESecond():L1(0),L2(0){};

      ESecond(IntegerVectorList* L1_,IntegerVectorList::iterator i1_,IntegerVectorList* L2_,IntegerVectorList::iterator i2_):
      L1(L1_),
      i1(i1_),
      L2(L2_),
      i2(i2_)
    {
    }
  };
#endif
  SymmetryGroup const &sym;
  map<EFirst,ESecond > theSet;
  int theSetSize;
public:
  Boundary(SymmetryGroup const &sym_):
    sym(sym_),
    theSetSize(0)
  {
  }
  int size()const
  {
    return theSetSize;
  }
  pair<IntegerVector,IntegerVector> normalForm(IntegerVector const &ridge, IntegerVector const &ray)const
  {
    pair<IntegerVector,IntegerVector> ret;
    IntegerVector perm;
    ret.first=sym.orbitRepresentative(ridge,&perm);
    ret.second=sym.orbitRepresentativeFixing(SymmetryGroup::compose(perm,ray),ret.first);
    return ret;
  }
  bool containsFlip(IntegerVector const &ridge, IntegerVector const &ray, IntegerVectorList *storedInList_, IntegerVectorList::iterator listIterator_, IntegerVectorList *storedInList2_, IntegerVectorList::iterator listIterator2_)
  {
//	    debug<<"F\n";
    assert(ridge.size()==ray.size());
    EFirst p=normalForm(ridge,ray);
//    debug<<p.first<<p.second<<"\n";
//    debug<<"F2\n";
    if(theSet.count(p)==1)
      {
#if 0
        theSet[p].first->erase(theSet[p].second);
#else
//	    debug<<"F3\n";
        theSet[p].L1->erase(theSet[p].i1);
        if(theSet[p].L2)theSet[p].L2->erase(theSet[p].i2);
#endif
        theSet.erase(p);
	theSetSize--;
//    debug<<"G\n";
	return true;
      }
//    debug<<"P\n";
#if 0
    theSet[p]=ESecond(storedInList_,listIterator_);
#else
//    fprintf(stderr,"%x\n",storedInList_);
//    fprintf(stderr,"%x\n",storedInList2_);
//    debug<<*listIterator_<<*listIterator2_<<"\n";
    ESecond a(storedInList_,listIterator_,storedInList2_,listIterator2_);
//    debug<<"TEST\n";
//    debug<<*listIterator_<<*listIterator2_<<"\n";
//    debug<<p.first<<p.second<<"\n";
//    debug<<(int)a.i1._M_singular()<<"\n";
//    debug<<(int)a.i2._M_singular()<<"\n";
//    theSet[p]=a;//ESecond(storedInList_,listIterator_,storedInList2_,listIterator2_);
    theSet.insert(std::make_pair(p,a));
#endif
    theSetSize++;
//    debug<<"H\n";
    return false;
  }
  /**
   * This routine remove rays from rays, such that only one ridge-ray pair is left for each orbit.
   * The routine allows an additional list of vectors with the same number of elements as rays to be passed.
   * The routine will remove those vectors from this set which correspond to rays removed from rays.
   *
   * To do this it must know the symmetry group.
   */
  void removeDuplicates(IntegerVector const &ridge, IntegerVectorList &rays, IntegerVectorList *normals=0)const
  {
//    debug<<"ENTER\n";
//    debug<<rays;
    IntegerVectorList ret;
    IntegerVectorList normalsRet;
    set<IntegerVector> representatives;
    IntegerVectorList::const_iterator I;
    if(normals)I=normals->begin();
    for(IntegerVectorList::const_iterator i=rays.begin();i!=rays.end();i++)
      {
	IntegerVector rep=sym.orbitRepresentativeFixing(*i,ridge);
	if(representatives.count(rep)==0)
	  {
	    representatives.insert(rep);
	    ret.push_back(*i);
	    if(normals)normalsRet.push_back(*I);
	  }
	if(normals)I++;
      }
    rays=ret;
    if(normals)*normals=normalsRet;
  }
  void print()const
  {
    cerr<< "Boundary" <<endl;
    for(map<EFirst,ESecond>::const_iterator i=theSet.begin();i!=theSet.end();i++)
      {
	AsciiPrinter P(Stderr);
	P << i->first.first << i->first.second;
	cerr << endl;
      }
    cerr<<endl<<endl;
  }
};

class ParallelTraverser{
	ParallelTraverser(Boundary &theBoundary, ConeTraverser &theTraverser);
	void run()
	{

		while(theBoundary.getJob())
		{

		}
	}
};

void ParallelTraverse(Boundary &theBoundary, vector<ParallelTraverser> &traversers, SymmetricTarget &target)
{
	assert(traversers.size());


	traversers.begin().mark();

	std::for_each(std::execution::par,traversers.begin(),traversers.end(),
			[](auto&& item)
		    {
		    	item.run();
		    });
}

}
#endif



#if 0
//Sketch of parallel traversing in bipartite graph
//Facets on the left, Ridges on the right
Input: vertex v

Boundary B;
B.symmetricDiff(neighbours(v))

THREAD:

loop:if(no left edge)
  gobackLeft
  if(no right edge)
   gobackRight
goto loop

hvis kant fundet
     go
     
     parwhile(!boundary.pickLeftEdge)
{
  
}

#endif
