1 /* Frobby: Software for monomial ideal computations.
2    Copyright (C) 2011 Bjarke Hammersholt Roune (www.broune.com)
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see http://www.gnu.org/licenses/.
16 */
17 #include "stdinc.h"
18 #include "LatticeAlgs.h"
19 
20 #include <stack>
21 
getThinPlanes(vector<TriPlane> & planes,const GrobLat & lat)22 void getThinPlanes(vector<TriPlane>& planes, const GrobLat& lat) {
23   planes.clear();
24   for (size_t a = 0; a <= lat.getNeighborCount(); ++a) {
25 	Neighbor an(lat);
26 	if (a < lat.getNeighborCount())
27 	  an = lat.getNeighbor(a);
28 	for (size_t b = 0; b < lat.getNeighborCount(); ++b) {
29 	  for (size_t c = 0; c < lat.getNeighborCount(); ++c) {
30 		TriPlane plane(an, lat.getNeighbor(b), lat.getNeighbor(c));
31 		if (plane.isLine())
32 		  continue; // points are collinear
33 		ASSERT(plane.isParallel(plane));
34 		bool parallel = false;
35 		for (size_t p = 0; p < planes.size(); ++p) {
36 		  if (plane.isParallel(planes[p])) {
37 			parallel = true;
38 			break;
39 		  }
40 		}
41 		if (parallel)
42 		  continue; // already got this plane
43 
44 		bool isThin = true;
45 		for (size_t n = 0; n < lat.getNeighborCount(); ++n) {
46 		  if (!plane.closeToPlane(lat.getNeighbor(n))) {
47 			isThin = false;
48 			break;
49 		  }
50 		}
51 		if (isThin)
52 		  planes.push_back(plane);
53 	  }
54 	}
55   }
56 }
57 
58 /*
59 void checkParallelFaces(const vector<Mlfb>& mlfbs,
60 						const vector<Plane> planes) {
61   vector<TriPlane> facePlanes;
62   for (size_t m = 0; m < mlfbs.size(); ++m) {
63 	const Mlfb& mlfb = mlfbs[m];
64 	cout << mlfb.getName() << facePlanes.size() << endl;
65 	Neighbor a = mlfb.getPoint(0);
66 	Neighbor b = mlfb.getPoint(1);
67 	Neighbor c = mlfb.getPoint(2);
68 	Neighbor d = mlfb.getPoint(3);
69 	facePlanes.push_back(TriPlane(b, c, d));
70 	facePlanes.push_back(TriPlane(a, c, d));
71 	facePlanes.push_back(TriPlane(a, b, d));
72 	facePlanes.push_back(TriPlane(a, b, c));
73   }
74 
75   for (size_t i = 0; i < facePlanes.size(); ++i) {
76 	const TriPlane& plane = facePlanes[i];
77 
78 	bool thin = false;
79 	for (size_t p = 0; p < planes.size(); ++p) {
80 	  if (plane.isParallel(planes[p])) {
81 		thin = true;
82 		break;
83 	  }
84 	}
85 	if (thin)
86 	  continue;
87 
88 	size_t parallelCount = 0;
89 	for (size_t j = 0; j < facePlanes.size(); ++j)
90 	  if (plane.isParallel(facePlanes[j])) {cout << ' ' << j << endl;
91 		++parallelCount;}
92 	cout << parallelCount << ' ' << plane.getNormal() << endl;
93 	CHECK(parallelCount == 2); // not satisfied
94   }
95 }
96 */
97 
98 
checkPlanes(const vector<TriPlane> & thinPlanes,const vector<Plane> & dtPlanes)99 void checkPlanes(const vector<TriPlane>& thinPlanes,
100 				 const vector<Plane>& dtPlanes) {
101   CHECK(thinPlanes.size() == dtPlanes.size());
102 
103   for (size_t thin = 0; thin < thinPlanes.size(); ++thin) {
104 	bool parallel = false;
105 	for (size_t dt = 0; dt < dtPlanes.size(); ++dt) {
106 	  if (thinPlanes[thin].isParallel(dtPlanes[dt])) {
107 		parallel = true;
108 		break;
109 	  }
110 	}
111 	CHECK(parallel);
112   }
113 
114   bool found = false;
115   for (size_t dt = 0; dt < dtPlanes.size(); ++dt) {
116 	const Plane& plane = dtPlanes[dt];
117 	size_t sum = plane.tri.getASideMlfbs().size() +
118 	  plane.tri.getBSideMlfbs().size();
119 
120     if (sum == 3)
121       found = true;
122 	/*
123 	if (plane.flatSeq.empty()) {
124 	  if (sum == 4)
125 		found = true;
126 	} else {
127 	  CHECK(sum == 3);
128 	  found = true;
129 	}
130 	*/
131   }
132   CHECK(dtPlanes.size() == 6 || found);
133 }
134 
getPlaceCode(NeighborPlace place)135 char getPlaceCode(NeighborPlace place) {
136   switch (place) {
137   case InPlane: return 'P';
138   case UnderPlane: return 'U';
139   case OverPlane: return 'O';
140   case NoPlace: return ' ';
141   default: return 'E';
142   }
143 }
144 
pushOutFacetPositive(size_t facetPushOut,const vector<mpz_class> & rhs,const GrobLat & lat)145 size_t pushOutFacetPositive(size_t facetPushOut,
146 							const vector<mpz_class>& rhs,
147 							const GrobLat& lat) {
148   size_t onFacet = numeric_limits<size_t>::max();
149   mpq_class leastEntry;
150 
151   for (size_t n = 0; n < lat.getNeighborCount(); ++n) {
152 	for (size_t i = 0; i < lat.getYDim(); ++i) {
153 	  if (i == facetPushOut)
154 		continue;
155 	  if (lat.getYMatrix()(n, i) >= rhs[i])
156 		goto notOnFacet;
157 	}
158 
159 	if (onFacet == numeric_limits<size_t>::max() ||
160 		leastEntry > lat.getYMatrix()(n, facetPushOut)) {
161 	  leastEntry = lat.getYMatrix()(n, facetPushOut);
162 	  onFacet = n;
163 	}
164 
165   notOnFacet:;
166   }
167   return onFacet;
168 }
169 
pushOutFacetZero(const vector<mpz_class> & rhs,const GrobLat & lat)170 size_t pushOutFacetZero(const vector<mpz_class>& rhs, const GrobLat& lat) {
171   size_t onFacet = numeric_limits<size_t>::max();
172   mpq_class leastEntry;
173 
174   for (size_t n = 0; n < lat.getNeighborCount(); ++n) {
175 	for (size_t i = 1; i < lat.getYDim(); ++i)
176 	  if (-lat.getYMatrix()(n, i) >= rhs[i])
177 		goto notOnFacet;
178 
179 	if (onFacet == numeric_limits<size_t>::max() ||
180 		leastEntry > -lat.getYMatrix()(n, 0)) {
181 	  leastEntry = -lat.getYMatrix()(n, 0);
182 	  onFacet = n;
183 	}
184 
185   notOnFacet:;
186   }
187   return onFacet;
188 }
189 
computeRhs(vector<mpz_class> & rhs,const vector<Neighbor> points)190 void computeRhs(vector<mpz_class>& rhs, const vector<Neighbor> points) {
191   ASSERT(!points.empty());
192   const GrobLat& lat = points[0].getGrobLat();
193   rhs.resize(lat.getYDim());
194   for (size_t var = 0; var < lat.getYDim(); ++var) {
195 	rhs[var] = points[0].getY(var);
196 	for (size_t p = 1; p < points.size(); ++p)
197 	  if (rhs[var] < points[p].getY(var))
198 		rhs[var] = points[p].getY(var);
199   }
200 }
201 
reset(size_t offset,const vector<Neighbor> & points)202 void Mlfb::reset(size_t offset, const vector<Neighbor>& points) {
203   ASSERT(!points.empty());
204   _points = points;
205   _offset = offset;
206 
207   const GrobLat& lat = points[0].getGrobLat();
208 
209   computeRhs(_rhs, points);
210 
211   // order to have maxima along diagonal if possible.
212   if (getPointCount() == lat.getYDim()) {
213 	for (size_t i = 0; i < lat.getYDim(); ++i)
214 	  for (size_t p = 0; p < getPointCount(); ++p)
215 		if (getPoint(p).getY(i) == getRhs()[i])
216 		  swap(_points[i], _points[p]);
217   }
218 
219   // Compute MLFB index.
220   if (getPointCount() - 1 == lat.getHDim())
221   {
222 	Matrix mat(getPointCount() - 1, lat.getHDim());
223 	for (size_t point = 1; point < getPointCount(); ++point)
224 	  for (size_t var = 0; var < lat.getHDim(); ++var)
225 		mat(point - 1, var) = getPoint(point).getH(var);
226 	index = determinant(mat);
227   }
228 
229   if (getPointCount() == 4) {
230 	Matrix mat(4, lat.getHDim());
231 	for (size_t point = 0; point < getPointCount(); ++point)
232 	  for (size_t var = 0; var < lat.getHDim(); ++var)
233 		mat(point, var) = getPoint(point).getH(var);
234 	_isParallelogram = ::isParallelogram(mat);
235   } else
236 	_isParallelogram = false;
237 }
238 
computeMlfbs(vector<Mlfb> & mlfbs,const GrobLat & lat)239 void computeMlfbs(vector<Mlfb>& mlfbs, const GrobLat& lat) {
240   BigIdeal initialIdeal;
241   lat.getInitialIdeal(initialIdeal);
242 
243   BigTermRecorder recorder;
244   SliceParams params;
245   SliceFacade facade(params, initialIdeal, recorder);
246   facade.computeIrreducibleDecomposition(true);
247   auto_ptr<BigIdeal> rhsesOwner = recorder.releaseIdeal();
248   BigIdeal& rhses = *rhsesOwner;
249   ASSERT(recorder.empty());
250 
251   mlfbs.clear();
252   mlfbs.resize(rhses.getGeneratorCount());
253 
254   vector<Neighbor> points;
255   for (size_t i = 0; i < mlfbs.size(); ++i) {
256 	Mlfb& mlfb = mlfbs[i];
257 	points.clear();
258 
259 	points.push_back(Neighbor(lat));
260 	for (size_t gen = 0; gen < initialIdeal.getGeneratorCount(); ++gen) {
261 	  for (size_t var = 0; var < initialIdeal.getVarCount(); ++var)
262 		if (initialIdeal[gen][var] > rhses[i][var])
263 		  goto skipIt;
264 	  points.push_back(Neighbor(lat, gen));
265 	skipIt:;
266 	}
267 
268 	mlfb.reset(i, points);
269 	CHECK(rhses[i] == mlfb.getRhs());
270   }
271 
272   Matrix nullSpaceBasis;
273   nullSpace(nullSpaceBasis, lat.getMatrix());
274   transpose(nullSpaceBasis, nullSpaceBasis);
275   // the basis is the rows of NullSpaceBasis at this point.
276 
277   for (size_t m = 0; m < mlfbs.size(); ++m) {
278 	Mlfb& mlfb = mlfbs[m];
279 
280 	if (mlfb.getPointCount() != lat.getYDim())
281 	  continue;
282 
283 	// Compute minInitialFacet.
284 	mlfb.minInitialFacet = 0;
285 	mpq_class minInitial = 0;
286 	for (size_t i = 0; i < mlfb.getPointCount(); ++i) {
287 	  mpq_class initial = mlfb.getPoint(i).getY(0);
288 	  if (minInitial > initial) {
289 		minInitial = initial;
290 		mlfb.minInitialFacet = i;
291 	  }
292 	}
293 
294 	// Compute dot degree.
295 	if (nullSpaceBasis.getRowCount() == 1 &&
296 		lat.getYDim() == nullSpaceBasis.getColCount()) {
297 	  mlfb.dotDegree = 0;
298 	  for (size_t r = 0; r < nullSpaceBasis.getRowCount(); ++r)
299 		for (size_t c = 0; c < nullSpaceBasis.getColCount(); ++c)
300 		  mlfb.dotDegree += nullSpaceBasis(r, c) * mlfb.getRhs()[c];
301 	}
302 
303 	// Compute Scarf edges.
304 	mlfb.edges.resize(lat.getYDim());
305 	mlfb.edgeHitsFacet.resize(lat.getYDim());
306 	for (size_t facetPushIn = 0; facetPushIn < lat.getYDim(); ++facetPushIn) {
307 	  mpq_class secondLargest;
308 	  size_t facetPushOut = numeric_limits<size_t>::max();
309 
310 	  for (size_t neigh = 0; neigh < lat.getYDim(); ++neigh) {
311 		mpq_class entry = 0; // neigh == 0 represents zero.
312 		if (neigh > 0)
313 		  entry = mlfb.getPoint(neigh).getY(facetPushIn);
314 
315 		if (neigh == facetPushIn) {
316 		  if (entry != mlfb.getRhs()[facetPushIn])
317 			goto skipBecauseNotGeneric;
318 		} else {
319 		  if (entry == secondLargest &&
320 			  facetPushOut != numeric_limits<size_t>::max())
321 			goto skipBecauseNotGeneric;
322 
323 		  if (entry > secondLargest ||
324 			  facetPushOut == numeric_limits<size_t>::max()) {
325 			secondLargest = entry;
326 			facetPushOut = neigh;
327 		  }
328 		}
329 	  }
330 
331 	  // ----------------------------------------------
332 	  // ** Case 1: facetPushIn > 0 and facetPushOut > 0
333 	  //
334 	  // We push in facetPushIn (discarding the non-zero neighbor
335 	  // previously on that facet) and hit a non-zero neighbor
336 	  // that is already on the MLFB. That neighbor now instead
337 	  // lies on facetPushIn and we push out facetPushOut in the
338 	  // straight forward way until it hits what will be the new
339 	  // neighbor on facetPushOut.
340 	  //
341 	  // ----------------------------------------------
342 	  // ** Case  2: facetPushIn > 0 and facetPushOut == 0
343 	  //
344 	  // We push in facetPushIn (discarding the non-zero neighbor
345 	  // previously on that facet) and hit zero. Zero now instead
346 	  // lies on facetPushIn and we push out facetPushOut. It will
347 	  // be pushing into the area on the opposite side from the
348 	  // half-set of neighbors that we are looking at, so to find
349 	  // the replacement neighbor to put on facetPushOut
350 	  // (i.e. facet zero) we need to consider the negative of the
351 	  // neighbors we have. When that neighbor -v has been found,
352 	  // we need to translate the whole body by +v so that zero
353 	  // will once again lie on facet zero.
354 	  //
355 	  // ----------------------------------------------
356 	  // ** case 3: facetPushIn == 0 and facetPushOut > 0
357 	  //
358 	  // We push in facetPushIn (discarding the zero previously on
359 	  // that facet) and hit a non-zero neighbor v on
360 	  // facetPushOut. Then v lies on facetPushIn (i.e. facet
361 	  // zero), so for the MLFB to have zero on facet 0 we need to
362 	  // translate it by -v. We then relax facetPushOut to find a
363 	  // replacement neighbor as in Case 1.
364 
365 	  ASSERT(facetPushIn == 0 || secondLargest >= 0);
366 	  // In all cases the neighbor we hit moves to facetPushIn.
367 	  vector<mpz_class> rhs(mlfb.getRhs());
368 
369 	  rhs[facetPushIn] = secondLargest;
370 
371 	  if (facetPushIn == 0) {
372 		// Case 3: the neighbor hit moves to facet 0 so translate
373 		// the body to make that neighbor zero.
374 		for (size_t i = 0; i < lat.getYDim(); ++i)
375 		  rhs[i] -= mlfb.getPoint(facetPushOut).getY(i);
376 	  }
377 
378 	  if (facetPushOut > 0) {
379 		// Case 1 or 3: push out in the usual way.
380 		size_t newNeighbor = pushOutFacetPositive(facetPushOut, rhs, lat);
381 		if (newNeighbor == numeric_limits<size_t>::max())
382 		  goto skipBecauseNotGeneric;
383 		rhs[facetPushOut] = lat.getYMatrix()(newNeighbor, facetPushOut);
384 	  }
385 
386 	  if (facetPushOut == 0) {
387 		// Case 2: push out into negative neighbors
388 		size_t newNeighbor = pushOutFacetZero(rhs, lat);
389 		if (newNeighbor == numeric_limits<size_t>::max())
390 		  goto skipBecauseNotGeneric;
391 		for (size_t i = 0; i < lat.getYDim(); ++i)
392 		  rhs[i] += lat.getYMatrix()(newNeighbor, i);
393 		rhs[0] = 0;
394 	  }
395 
396 	  // Find the MLFB with the right hand side that we have
397 	  // computed.
398 	  for (size_t mi = 0; mi < mlfbs.size(); ++mi) {
399 		if (mlfbs[mi].getRhs() == rhs) {
400 		  mlfb.edges[facetPushIn] = &(mlfbs[mi]);
401 		  mlfb.edgeHitsFacet[facetPushIn] = facetPushOut;
402 		  goto foundMatch;
403 		}
404 	  }
405 	  goto skipBecauseNotGeneric;
406 	foundMatch:;
407 	}
408 	continue;
409   skipBecauseNotGeneric:
410 	mlfb.edges.clear();
411 	mlfb.edgeHitsFacet.clear();
412   }
413 }
414 
nextInSeq(SeqPos pos)415 SeqPos nextInSeq(SeqPos pos) {
416   size_t pushIn;
417   for (pushIn = 0;; ++pushIn) {
418 	ASSERT(pushIn < 4);
419 	if (pushIn != pos.fixFacet1 &&
420 		pushIn != pos.fixFacet2 &&
421 		pushIn != pos.comingFromFacet)
422 	  break;
423   }
424 
425   size_t hits = pos.mlfb->getHitsFacet(pushIn);
426   ASSERT(hits != pushIn);
427 
428   SeqPos next = pos;
429   next.mlfb = pos.mlfb->getEdge(pushIn);
430   next.comingFromFacet = hits;
431 
432   if (pos.fixFacet1 == hits)
433 	next.fixFacet1 = pushIn;
434   else if (pos.fixFacet2 == hits)
435 	next.fixFacet2 = pushIn;
436 
437   next.order();
438   return next;
439 }
440 
prevInSeq(SeqPos pos)441 SeqPos prevInSeq(SeqPos pos) {
442   return nextInSeq(pos.getReverse()).getReverse();
443 }
444 
computeFlatIntervalCount(const vector<SeqPos> & flatSeq)445 size_t computeFlatIntervalCount(const vector<SeqPos>& flatSeq) {
446   if (flatSeq.empty())
447 	return 0u;
448 
449   const Mlfb& leftFlat = *(flatSeq.front().mlfb);
450   size_t sumFacet = leftFlat.getMinInitialFacet();
451 
452   size_t aFacet = 4;
453   for (size_t j = 0; j < 4; ++j) {
454 	if (j != 0 && j != sumFacet) {
455 	  aFacet = j;
456 	  break;
457 	}
458   }
459   CHECK(aFacet != 4);
460 
461   size_t subSeqCount = 1;
462   for (size_t i = 1; i < flatSeq.size() - 1; ++i) {
463 	const Mlfb& prev = *(flatSeq[i - 1].mlfb);
464 	const Mlfb& flat = *(flatSeq[i].mlfb);
465 	if (flat.getHitsFacet(aFacet) != prev.getHitsFacet(aFacet))
466 	  ++subSeqCount;
467   }
468   return subSeqCount;
469 }
470 
computeFlatSeq(vector<SeqPos> & seq,const vector<Mlfb> & mlfbs,const Plane & plane)471 void computeFlatSeq(vector<SeqPos>& seq,
472 					const vector<Mlfb>& mlfbs,
473 					const Plane& plane) {
474   // ** compute left most flat
475   const Mlfb* leftFlat = 0;
476   for (size_t m = 0; m < mlfbs.size(); ++m) {
477 	if (!plane.isFlat(mlfbs[m]))
478 	  continue;
479 	const Mlfb* toLeft = mlfbs[m].getEdge(0);
480 	if (!plane.isFlat(*toLeft)) {
481 	  CHECK(leftFlat == 0 || leftFlat == toLeft); // left flat unique
482 	  leftFlat = &(mlfbs[m]);
483 	}
484   }
485 
486   seq.clear();
487   if (leftFlat == 0) {
488 	ASSERT(!plane.hasFlat());
489 	return;
490   }
491 
492   // ** go right as long as there is a flat there
493   SeqPos pos;
494   pos.mlfb = leftFlat;
495   while (plane.isFlat(*pos.mlfb)) {
496 	ASSERT(seq.empty() || seq.back().mlfb != pos.mlfb);
497 	seq.push_back(pos);
498 	bool moved = false;
499 	for (size_t facet = 1; facet < 4; ++facet) {
500 	  if (pos.mlfb->getEdge(facet)->getEdge(0) == pos.mlfb) {
501 		pos.mlfb = pos.mlfb->getEdge(facet);
502 		moved = true;
503 		break;
504 	  }
505 	}
506 	if (!moved)
507 	  break;
508   }
509 }
510 
computePlanes(vector<Plane> & planes,const GrobLat & lat,vector<Mlfb> & mlfbs)511 void computePlanes(vector<Plane>& planes,
512 				   const GrobLat& lat,
513 				   vector<Mlfb>& mlfbs) {
514   const size_t neighborCount = lat.getNeighborCount();
515   for (size_t gen1 = 0; gen1 < neighborCount; ++gen1) {
516 	for (size_t gen2 = gen1 + 1; gen2 < neighborCount; ++gen2) {
517 	  Neighbor a = lat.getNeighbor(gen1);
518 	  Neighbor b = lat.getNeighbor(gen2);
519 	  Neighbor sum = lat.getSum(a, b);
520 
521 	  if (!sum.isValid())
522 		continue;
523 	  if (!lat.isPointFreeBody(a, sum))
524 		continue;
525 	  if (!lat.isPointFreeBody(b, sum))
526 		continue;
527 	  if (lat.isPointFreeBody(a, b, sum))
528 		continue; // only looking for non-flat double triangles right now
529 
530 	  Matrix rowAB(2, lat.getHDim());
531 	  copyRow(rowAB, 0, lat.getHMatrix(), gen1);
532 	  copyRow(rowAB, 1, lat.getHMatrix(), gen2);
533 
534 	  planes.push_back(Plane(a, b, sum, mlfbs, lat));
535 	  Plane& plane = planes.back();
536 	  plane.rowAB = rowAB;
537 	  nullSpace(plane.nullSpaceBasis, rowAB);
538 
539 	  Matrix prod;
540 	  product(prod, lat.getHMatrix(), plane.nullSpaceBasis);
541 	  plane.neighborPlace.resize(lat.getNeighborCount());
542 	  for (size_t gen = 0; gen < lat.getNeighborCount(); ++gen) {
543 		mpq_class& value = prod(gen, 0);
544 		NeighborPlace place = InPlane;
545 		if (value < 0)
546 		  place = UnderPlane;
547 		else if (value > 0)
548 		  place = OverPlane;
549 		plane.neighborPlace[gen] = place;
550 	  }
551 
552 	  transpose(plane.nullSpaceBasis);
553 	}
554   }
555 
556   for (size_t p = 0; p < planes.size(); ++p) {
557 	Plane& plane = planes[p];
558 	for (size_t i = 0; i < mlfbs.size(); ++i)
559 	  plane.typeCounts[plane.getType(mlfbs[i])] += 1;
560 
561 	computeFlatSeq(plane.flatSeq, mlfbs, plane);
562 	computePivots(plane.pivots, mlfbs, plane, plane.flatSeq);
563 	plane.flatIntervalCount = computeFlatIntervalCount(plane.flatSeq);
564   }
565 }
566 
Tri(Neighbor a,Neighbor b,Neighbor sum,const vector<Mlfb> & mlfbs,const GrobLat & lat)567 Tri::Tri(Neighbor a, Neighbor b, Neighbor sum,
568 		 const vector<Mlfb>& mlfbs, const GrobLat& lat):
569   _a(a), _b(b), _sum(sum) {
570 
571   // find MLFBs containing {0,a,a+b}
572   for (size_t m = 0; m < mlfbs.size(); ++m)
573 	if (mlfbs[m].hasPoint(a) && mlfbs[m].hasPoint(sum))
574 	  _aSideMlfbs.push_back(&(mlfbs[m]));
575 
576   // find MLFBs containing {0,b,a+b}
577   for (size_t m = 0; m < mlfbs.size(); ++m)
578 	if (mlfbs[m].hasPoint(b) && mlfbs[m].hasPoint(sum))
579 	  _bSideMlfbs.push_back(&(mlfbs[m]));
580 
581   // find additional neighbors in the body defined by {0,a,b,a+b}
582   vector<Neighbor> points;
583   points.push_back(Neighbor(lat)); // add zero;
584   points.push_back(a);
585   points.push_back(b);
586   points.push_back(sum);
587   vector<mpz_class> rhs;
588   computeRhs(rhs, points);
589   _boundary.push_back(Neighbor(lat)); // add zero
590   for (size_t n = 0; n < lat.getNeighborCount(); ++n) {
591 	Neighbor neighbor = lat.getNeighbor(n);
592 	bool boundary = true;
593 	bool interior = true;
594 	for (size_t i = 0; i < rhs.size(); ++i) {
595 	  if (neighbor.getY(i) == rhs[i])
596 		interior = false;
597 	  else if (neighbor.getY(i) > rhs[i]) {
598 		interior = false;
599 		boundary = false;
600 		break;
601 	  }
602 	}
603 	if (interior)
604 	  _interior.push_back(neighbor);
605 	else if (boundary)
606 	  _boundary.push_back(neighbor);
607   }
608 }
609 
610 
check0Graph(const vector<Mlfb> & mlfbs)611 void check0Graph(const vector<Mlfb>& mlfbs) {
612   vector<bool> ok(mlfbs.size());
613   bool sawFlat = false;
614   for (size_t i =0 ; i < mlfbs.size(); ++i) {
615 	ok[i] = (mlfbs[i].index == 0);
616 	if (ok[i])
617 	  sawFlat = true;
618   }
619   if (!sawFlat)
620 	return;
621 
622   while (true) {
623 	bool done = true;
624 	for (size_t i = 0; i < mlfbs.size(); ++i) {
625 	  if (!ok[i]) {
626 		size_t to = mlfbs[i].getEdge(0)->getOffset();
627 		if (ok[to]) {
628 		  done = false;
629 		  ok[i] = true;
630 		}
631 	  }
632 	}
633 	if (done)
634 	  break;
635   }
636 
637   for (size_t i = 0; i < mlfbs.size(); ++i) {
638 	CHECK(ok[i]);
639   }
640 }
641 
checkMlfbs(const vector<Mlfb> & mlfbs,const GrobLat & lat)642 void checkMlfbs(const vector<Mlfb>& mlfbs, const GrobLat& lat) {
643   CHECK(mlfbs.size() == lat.getNeighborCount() - 1);
644   for (size_t m = 0; m < mlfbs.size(); ++m) {
645 	CHECK(mlfbs[m].isParallelogram() == (mlfbs[m].index == 0));
646   }
647 }
648 
checkDoubleTrianglePlanes(const vector<Plane> & planes,const GrobLat & lat,const vector<Mlfb> & mlfbs)649 void checkDoubleTrianglePlanes(const vector<Plane>& planes,
650 							   const GrobLat& lat,
651 							   const vector<Mlfb>& mlfbs) {
652   // Check no two planes are parallel. Otherwise there would a plane
653   // with two non-flat double triangles in it.
654   for (size_t p1 = 0; p1 < planes.size(); ++p1) {
655 	for (size_t p2 = 0; p2 < p1; ++p2) {
656 	  CHECK(!hasSameRowSpace(planes[p1].rowAB, planes[p2].rowAB));
657 	}
658   }
659 
660   // Check that all parallelograms lie in a plane. Otherwise there
661   // would be a plane defined by a flat that does not have a double
662   // triangle in it.
663   for (size_t m = 0; m < mlfbs.size(); ++m) {
664 	if (mlfbs[m].isParallelogram()) {
665 	  bool liesInSomePlane = false;
666 	  for (size_t p = 0; p < planes.size(); ++p) {
667 		if (planes[p].isFlat(mlfbs[m])) {
668 		  liesInSomePlane = true;
669 		  break;
670 		}
671 	  }
672 	  CHECK(liesInSomePlane);
673 	}
674   }
675 
676 
677 
678 
679   bool multipleIntervals = false;
680   bool anyFlat = false;
681   bool flatWith4Pivots = false;
682   for (size_t p = 0; p < planes.size(); ++p) {
683 	if (planes[p].flatIntervalCount > 1)
684 	  multipleIntervals = true;
685 	if (planes[p].hasFlat()) {
686 	  anyFlat = true;
687 	  if (planes[p].pivots.size() == 4)
688 		flatWith4Pivots = true;
689 	}
690   }
691   if (multipleIntervals) {
692 	ASSERT(anyFlat);
693 	//CHECK(flatWith4Pivots);
694 	CHECK(planes.size() == 1);
695   }
696 
697   if (planes.size() == 6) {
698 	CHECK(!anyFlat);
699 	CHECK(planes.size() == 6);
700 	for (size_t p = 0; p < planes.size(); ++p) {
701 	  CHECK(planes[p].pivots.size() == 4);
702 	}
703 	CHECK(lat.getNeighborCount() == 7);
704 	CHECK(mlfbs.size() == 6);
705   }
706 
707   if (anyFlat) {
708 	//check0Graph(mlfbs);
709 	//CHECK(flatWith4Pivots);
710 	CHECK(planes.size() < 6);
711   }
712 }
713 
checkPlane(const Plane & plane,const vector<Mlfb> & mlfbs)714 void checkPlane(const Plane& plane, const vector<Mlfb>& mlfbs) {
715   for (size_t i = 0; i < mlfbs.size(); ++i) {
716 	if (plane.isPivot(mlfbs[i])) {
717 	  CHECK(mlfbs[i].index == -1 || mlfbs[i].index == 1);
718 	} else if (plane.isFlat(mlfbs[i])) {
719 	  CHECK(mlfbs[i].index == 0);
720 	}
721   }
722 }
723 
724 /** Returns the facet to push in of pivot to get to a flat. Pivot must
725 	be a pivot. */
pivotToFlatFacet(const Mlfb & pivot,const Plane & plane)726 size_t pivotToFlatFacet(const Mlfb& pivot, const Plane& plane) {
727   size_t facet = 4;
728   for (size_t push = 0; push < 4; ++push) {
729 	if (plane.isFlat(*pivot.getEdge(push))) {
730 	  CHECK(facet == 4); // adjacent to no more than one flat
731 	  facet = push;
732 	}
733   }
734   CHECK(facet != 4); // adjacent to at least one flat
735   return facet;
736 }
737 
disjointSeqs(const vector<SeqPos> & a,const vector<SeqPos> & b)738 bool disjointSeqs(const vector<SeqPos>& a, const vector<SeqPos>& b) {
739   for (size_t i = 0; i < a.size(); ++i)
740 	for (size_t j = 0; j < b.size(); ++j)
741 	  if (a[i].mlfb == b[j].mlfb)
742 		return false;
743   return true;
744 }
745 
computePivots(vector<const Mlfb * > & pivots,const vector<Mlfb> & mlfbs,const Plane & plane,const vector<SeqPos> & flatSeq)746 void computePivots(vector<const Mlfb*>& pivots,
747 				   const vector<Mlfb>& mlfbs,
748 				   const Plane& plane,
749 				   const vector<SeqPos>& flatSeq) {
750   pivots.clear();
751   for (size_t m = 0; m < mlfbs.size(); ++m)
752 	if (plane.isPivot(mlfbs[m]))
753 	  pivots.push_back(&(mlfbs[m]));
754   if (pivots.size() != 4 || flatSeq.empty())
755 	return; // no idea about proper order in this case
756 
757   pivots.clear();
758   // use flat sequence to impose correct order
759   size_t sumFacet = flatSeq.front().mlfb->getMinInitialFacet();
760   pivots.push_back(flatSeq.front().mlfb->getEdge(0));
761   pivots.push_back(flatSeq.front().mlfb->getEdge(sumFacet));
762 
763   sumFacet = flatSeq.back().mlfb->getMinInitialFacet();
764   for (size_t i = 0; i < 4; ++i)
765 	if (i != 0 && i != sumFacet)
766 	  pivots.push_back(flatSeq.back().mlfb->getEdge(i));
767 }
768 
computeSeqs(vector<vector<SeqPos>> & left,vector<vector<SeqPos>> & right,const vector<Mlfb> & mlfbs,const Plane & plane)769 void computeSeqs(vector<vector<SeqPos> >& left,
770 				 vector<vector<SeqPos> >& right,
771 				 const vector<Mlfb>& mlfbs,
772 				 const Plane& plane) {
773   vector<vector<SeqPos> > seqs;
774 
775   for (size_t m = 0; m < mlfbs.size(); ++m) {
776 	if (!plane.isPivot(mlfbs[m]))
777 	  continue;
778 	const Mlfb& p = mlfbs[m];
779 	for (size_t i = 0; i < 4; ++i) {
780 	  const Mlfb& e = *(p.getEdge(i));
781 	  if (plane.isPivot(e) || plane.isFlat(e))
782 		continue;
783 
784 	  bool doneBefore = false;
785 	  for (size_t s = 0; s < seqs.size(); ++s) {
786 		if (*(seqs[s][seqs[s].size() - 1].mlfb) == p &&
787 			*(seqs[s][seqs[s].size() - 2].mlfb) == e) {
788 		  doneBefore = true;
789 		  break;
790 		}
791 	  }
792 	  if (doneBefore)
793 		continue;
794 
795 	  size_t prevFacet;
796 	  for (prevFacet = 0; prevFacet < 4; ++prevFacet)
797 		if (*(e.getEdge(prevFacet)) == p)
798 		  break;
799 	  ASSERT(prevFacet < 4);
800 
801 	  NeighborPlace place = plane.getPlace(e.getPoint(prevFacet));
802 	  size_t nextFacet;
803 	  for (nextFacet = 0; nextFacet < 4; ++nextFacet) {
804 		if (nextFacet != prevFacet &&
805 			place == plane.getPlace(e.getPoint(nextFacet)))
806 		  break;
807 	  }
808 	  SeqPos pos = prevInSeq(SeqPos(&e, nextFacet, prevFacet));
809 
810 	  seqs.resize(seqs.size() + 1);
811 	  vector<SeqPos>& seq = seqs.back();
812 	  seq.push_back(pos);
813 	  do {
814 		pos = nextInSeq(pos);
815 		seq.push_back(pos);
816 	  } while (!(plane.isPivot(*pos.mlfb)));
817 	}
818   }
819 
820   CHECK(!seqs.empty());
821   ASSERT(!seqs.front().empty());
822 
823   // now we've got all the sequences. Time to look at the sides.
824   stack<const Mlfb*> pending;
825 
826   // put a side pivot on the left arbitrarily and explore the
827   // connected component
828   vector<bool> leftSeen(mlfbs.size());
829   pending.push(seqs.front().front().mlfb);
830   while (!pending.empty()) {
831 	const Mlfb& m = *pending.top();
832 	pending.pop();
833 	if (leftSeen[m.getOffset()])
834 	  continue;
835 	leftSeen[m.getOffset()] = true;
836 	for (size_t s = 0; s < seqs.size(); ++s) {
837 	  if (*(seqs[s].front().mlfb) == m)
838 		pending.push(seqs[s].back().mlfb);
839 	  if (*(seqs[s].back().mlfb) == m)
840 		pending.push(seqs[s].front().mlfb);
841 	}
842   }
843 
844   // find a non-left side pivot
845   size_t m;
846   for (m = 0; m < mlfbs.size(); ++m)
847 	if (plane.isSidePivot(mlfbs[m]) && !leftSeen[m])
848 	  break;
849   CHECK(m < mlfbs.size());
850 
851   // put the non-left pivot on the right and explore the connected
852   // component
853   vector<bool> rightSeen(mlfbs.size());
854   pending.push(&(mlfbs[m]));
855   while (!pending.empty()) {
856 	const Mlfb& pm = *pending.top();
857 	pending.pop();
858 	if (rightSeen[pm.getOffset()])
859 	  continue;
860 	rightSeen[pm.getOffset()] = true;
861 	for (size_t s = 0; s < seqs.size(); ++s) {
862 	  if (*(seqs[s].front().mlfb) == pm)
863 		pending.push(seqs[s].back().mlfb);
864 	  if (*(seqs[s].back().mlfb) == pm)
865 		pending.push(seqs[s].front().mlfb);
866 	}
867   }
868 
869   left.clear();
870   right.clear();
871   for (size_t s = 0; s < seqs.size(); ++s) {
872 	const size_t offset = seqs[s].front().mlfb->getOffset();
873 	if (leftSeen[offset]) {
874 	  CHECK(!rightSeen[offset]);
875 	  left.push_back(seqs[s]);
876 	} else if (rightSeen[offset])
877 	  right.push_back(seqs[s]);
878   }
879 }
880 
computePivotSeqs(vector<vector<SeqPos>> & seqs,const Mlfb & pivot,const Plane & plane)881 void computePivotSeqs(vector<vector<SeqPos> >& seqs,
882 					  const Mlfb& pivot,
883 					  const Plane& plane) {
884   ASSERT(plane.pivots.size() == 4);
885   ASSERT(plane.isPivot(pivot));
886   size_t flatFacet = pivotToFlatFacet(pivot, plane);
887 
888   seqs.clear();
889   for (size_t facet = 0; facet < 4; ++facet) {
890 	if (facet == flatFacet)
891 	  continue;
892 	seqs.resize(seqs.size() + 1);
893 	vector<SeqPos>& seq = seqs.back();
894 
895 	SeqPos pos(&pivot, facet, flatFacet);
896 	seq.push_back(pos);
897 	do {
898 	  pos = nextInSeq(pos);
899 	  seq.push_back(pos);
900 	} while (!(plane.isPivot(*pos.mlfb)));
901   }
902 }
903 
checkSeq(vector<bool> & seenOnSide,const vector<SeqPos> & seq,const Plane & plane)904 void checkSeq(vector<bool>& seenOnSide,
905 			  const vector<SeqPos>& seq,
906 			  const Plane& plane) {
907   CHECK(seq.size() >= 3); // each seq must have at least one 2-2.
908   CHECK(plane.isSidePivot(*(seq.front().mlfb))); // start is a pivot
909   CHECK(plane.isSidePivot(*(seq.back().mlfb))); // end is a pivot
910   CHECK(seq.front().mlfb != seq.back().mlfb); // no loops
911 
912   for (size_t m = 1; m < seq.size() - 1 ; ++m) {
913 	const Mlfb* prev = seq[m - 1].mlfb;
914 	const Mlfb* current = seq[m].mlfb;
915 	const Mlfb* next = seq[m + 1].mlfb;
916 	const SeqPos& pos = seq[m];
917 
918 	// ** Check on 2-2 appears twice and update seenOnSide
919 	CHECK(!seenOnSide[current->getOffset()]);
920 	seenOnSide[current->getOffset()] = true;
921 
922 	// ** middle elements are 2-2s.
923 	CHECK(plane.is22(*current));
924 
925 	// ** SeqPos fields agrees with sequence order
926 	size_t prevFacet = pos.getBackFacet();
927 	size_t nextFacet = pos.getForwardFacet();
928 	CHECK(current->getEdge(prevFacet) == prev);
929 	CHECK(current->getEdge(nextFacet) == next);
930 
931 	// ** in-coming and out-going facets are on same plane
932 	CHECK(plane.getPlace(current->getPoint(prevFacet)) ==
933 		  plane.getPlace(current->getPoint(nextFacet)));
934   }
935 }
936 
checkSide(vector<bool> & pivotOnSide,const vector<vector<SeqPos>> & side,const Plane & plane,const vector<Mlfb> & mlfbs)937 void checkSide(vector<bool>& pivotOnSide,
938 			   const vector<vector<SeqPos> >& side,
939 			   const Plane& plane,
940 			   const vector<Mlfb>& mlfbs) {
941   CHECK(side.size() == 2 || side.size() == 3);
942 
943   vector<bool> seenOnSide(mlfbs.size());
944   for (size_t s = 0; s < side.size(); ++s){
945 	// check sequence local properties and update seen
946 	checkSeq(seenOnSide, side[s], plane);
947 
948 	// compute onSide
949 	pivotOnSide[side[s].front().mlfb->getOffset()] = true;
950 	pivotOnSide[side[s].back().mlfb->getOffset()] = true;
951   }
952 
953   /* // must have seen all 2-2s on this side.
954   for (size_t m = 0; m < mlfbs.size(); ++m)
955 	if (plane.is22(mlfbs[m]))
956 	CHECK(seenOnSide[m]);*/
957 
958   // 2,3 or 4 sidepivots
959   size_t sidePivots = 0;
960   for (size_t m = 0; m < mlfbs.size(); ++m)
961 	if (pivotOnSide[m])
962 	  ++sidePivots;
963   CHECK(sidePivots == 2 || sidePivots == 3 || sidePivots == 4);
964 }
965 
checkSeqs(const vector<vector<SeqPos>> & left,const vector<vector<SeqPos>> & right,const Plane & plane,const vector<Mlfb> & mlfbs)966 void checkSeqs(const vector<vector<SeqPos> >& left,
967 			   const vector<vector<SeqPos> >& right,
968 			   const Plane& plane,
969 			   const vector<Mlfb>& mlfbs) {
970   vector<bool> isLeftPivot(mlfbs.size());
971   checkSide(isLeftPivot, left, plane, mlfbs);
972 
973   vector<bool> isRightPivot(mlfbs.size());
974   checkSide(isRightPivot, right, plane, mlfbs);
975 
976   // all side pivots are on one and only one side
977   for (size_t m = 0; m < mlfbs.size(); ++m) {
978 	if (plane.isSidePivot(mlfbs[m]))
979 	  CHECK((isLeftPivot[m] + isRightPivot[m]) == 1);
980 	else
981 	  CHECK((isLeftPivot[m] + isRightPivot[m]) == 0);
982   }
983 }
984 
checkMiddle(const Plane & plane,const vector<Mlfb> & mlfbs)985 void checkMiddle(const Plane& plane,
986 				 const vector<Mlfb>& mlfbs) {
987   // ** check that the subgraph of pivots and flat is connected.
988   vector<bool> seen(mlfbs.size());
989   stack<const Mlfb*> pending;
990 
991   // find a pivot or flat
992   size_t m;
993   for (m = 0; m < mlfbs.size(); ++m)
994 	if (plane.isFlat(mlfbs[m]) || plane.isPivot(mlfbs[m]))
995 	  break;
996   ASSERT(m < mlfbs.size());
997 
998   // explore the graph of pivots and flats that is connected to the
999   // one we found.
1000   pending.push(&(mlfbs[m]));
1001   while (!pending.empty()) {
1002 	const Mlfb& mlfb = *(pending.top());
1003 	pending.pop();
1004 	if (seen[mlfb.getOffset()])
1005 	  continue;
1006 	seen[mlfb.getOffset()] = true;
1007 	for (size_t i = 0; i < 4; ++i)
1008 	  pending.push(mlfb.getEdge(i));
1009   }
1010 
1011   // check that we have reached all pivots and flats
1012   for (m = 0; m < mlfbs.size(); ++m)
1013 	if (plane.isFlat(mlfbs[m]) || plane.isPivot(mlfbs[m]))
1014 	  CHECK(seen[m]);
1015 }
1016 
checkGraphOnPlane(const Plane & plane,const vector<Mlfb> & mlfbs)1017 void checkGraphOnPlane(const Plane& plane,
1018 					   const vector<Mlfb>& mlfbs) {
1019   // no flat is adjacent to a 2-2
1020   for (size_t m = 0; m < mlfbs.size(); ++m) {
1021 	const Mlfb& mlfb = mlfbs[m];
1022 	if (plane.isFlat(mlfb))
1023 	  for (size_t i = 0; i < 4; ++i)
1024 		CHECK(!plane.is22(*(mlfb.getEdge(i))));
1025   }
1026 
1027   // parallelograms can't be flats and non-flat parallelograms are not
1028   // adjacent to a flat
1029   for (size_t m = 0; m < mlfbs.size(); ++m) {
1030 	const Mlfb& mlfb = mlfbs[m];
1031 	if (mlfb.isParallelogram()) {
1032 	  CHECK(!plane.isPivot(mlfb));
1033 	  if (!plane.isFlat(mlfb)) {
1034 		for (size_t i = 0; i < 4; ++i) {
1035 		  const Mlfb& adj = *(mlfb.getEdge(i));
1036 		  CHECK(!plane.isFlat(adj));
1037 		}
1038 	  }
1039 	}
1040   }
1041 }
1042 
checkDoubleTriangle(const Plane & plane,const vector<Mlfb> & mlfbs)1043 void checkDoubleTriangle(const Plane& plane,
1044 						 const vector<Mlfb>& mlfbs) {
1045   size_t aSideCount = plane.tri.getASideMlfbs().size();
1046   size_t bSideCount = plane.tri.getBSideMlfbs().size();
1047   CHECK(aSideCount == 1 || aSideCount == 2);
1048   CHECK(bSideCount == 1 || bSideCount == 2);
1049 
1050   for (size_t m = 0; m < aSideCount; ++m) {
1051 	const Mlfb& mlfb = *plane.tri.getASideMlfbs()[m];
1052 	CHECK(plane.isFlat(mlfb) || plane.isPivot(mlfb));
1053   }
1054   for (size_t m = 0; m < bSideCount; ++m) {
1055 	const Mlfb& mlfb = *plane.tri.getBSideMlfbs()[m];
1056 	CHECK(plane.isFlat(mlfb) || plane.isPivot(mlfb));
1057   }
1058 }
1059 
checkGraph(const vector<Mlfb> & mlfbs)1060 void checkGraph(const vector<Mlfb>& mlfbs) {
1061   // All non-parallelograms have out-degree 2. Parallelograms have
1062   // out-degree equal to 4 minus the number of other adjacent
1063   // parallelograms.
1064   for (size_t m = 0; m < mlfbs.size(); ++m) {
1065 	const Mlfb& mlfb = mlfbs[m];
1066 	set<size_t> adjParas;
1067 	set<size_t> adjNodes;
1068 	for (size_t i = 0; i < 4; ++i) {
1069 	  const Mlfb& adj = *(mlfb.getEdge(i));
1070 	  adjNodes.insert(adj.getOffset());
1071 	  if (adj.isParallelogram())
1072 		adjParas.insert(adj.getOffset());
1073 	}
1074 	const size_t outDegree = adjNodes.size();
1075 	if (!mlfb.isParallelogram())
1076 	  CHECK(outDegree == 4);
1077 	else
1078 	  CHECK(outDegree == 4 - adjParas.size());
1079   }
1080 
1081   // if there is an edge (a,b) then there is also an edge (b,a).
1082   for (size_t m = 0; m < mlfbs.size(); ++m) {
1083 	const Mlfb& mlfb = mlfbs[m];
1084 	for (size_t i = 0; i < 4; ++i) {
1085 	  size_t hitsFacet = mlfb.getHitsFacet(i);
1086 	  const Mlfb& adj = *(mlfb.getEdge(i));
1087 	  CHECK(mlfb == *(adj.getEdge(hitsFacet)));
1088 	}
1089   }
1090 }
1091 
checkPivotSeqs(vector<vector<SeqPos>> & pivotSeqs,const Plane & plane,const vector<Mlfb> & mlfbs,const vector<SeqPos> & flatSeq)1092 void checkPivotSeqs(vector<vector<SeqPos> >& pivotSeqs,
1093 					const Plane& plane,
1094 					const vector<Mlfb>& mlfbs,
1095 					const vector<SeqPos>& flatSeq) {
1096   CHECK(pivotSeqs.size() == 3);
1097   CHECK(pivotSeqs[0].size() >= 2);
1098   const Mlfb* pivot1 = pivotSeqs[0].front().mlfb;
1099   const Mlfb* pivot2 = pivotSeqs[0].back().mlfb;
1100 
1101   CHECK(plane.isPivot(*pivot1));
1102   CHECK(plane.isPivot(*pivot2));
1103 
1104 
1105   bool foundPlace = false;
1106   NeighborPlace place;
1107   for (size_t i = 0; i < 3; ++i) {
1108 	CHECK(pivotSeqs[i].size() >= 2);
1109 	// ** all sequences on same side between same pivots
1110 	CHECK((pivotSeqs[i].front().mlfb == pivot1 &&
1111 		   pivotSeqs[i].back().mlfb == pivot2) ||
1112 		  (pivotSeqs[i].front().mlfb == pivot2 &&
1113 		   pivotSeqs[i].back().mlfb == pivot1));
1114 
1115 	for (size_t j = 1; j < pivotSeqs[i].size() - 1; ++j) {
1116 	  const Mlfb* prev = pivotSeqs[i][j - 1].mlfb;
1117 	  const Mlfb* current = pivotSeqs[i][j].mlfb;
1118 	  const Mlfb* next = pivotSeqs[i][j + 1].mlfb;
1119 
1120 	  // ** middle mlfbs are 2-2's
1121 	  CHECK(plane.getType(*current) == 2);
1122 
1123 	  // ** SeqPos agrees with sequence order
1124 	  const SeqPos& pos = pivotSeqs[i][j];
1125 	  size_t prevFacet = pos.getBackFacet();
1126 	  size_t nextFacet = pos.getForwardFacet();
1127 	  CHECK(current->getEdge(prevFacet) == prev);
1128 	  CHECK(current->getEdge(nextFacet) == next);
1129 
1130 	  // ** in-coming and out-going facets are on same plane
1131 	  CHECK(plane.getPlace(current->getPoint(prevFacet)) ==
1132 			plane.getPlace(current->getPoint(nextFacet)));
1133 
1134 	  // ** active facets are the same for all sequences on same side
1135 	  if (!foundPlace) {
1136 		place = plane.getPlace(current->getPoint(prevFacet));
1137 	  }
1138 	  CHECK(place == plane.getPlace(current->getPoint(prevFacet)));
1139 	}
1140   }
1141 
1142   // ** Check that the sequences are a partition of the set of 2-2 mlfbs
1143   vector<bool> seen(mlfbs.size());
1144   for (size_t i = 0; i < 3; ++i) {
1145 	for (size_t j = 1; j < pivotSeqs[i].size() - 1; ++j) {
1146 	  CHECK(!seen[pivotSeqs[i][j].mlfb->getOffset()]); // sequences must be disjoint
1147 	  seen[pivotSeqs[i][j].mlfb->getOffset()] = true;
1148 	}
1149   }
1150   /*  for (size_t i = 0; i < mlfbs.size(); ++i) {
1151 	if (plane.isPivot(mlfbs[i]) || plane.isFlat(mlfbs[i]))
1152 	  continue;
1153 	  CHECK(seen[i]); // all 2-2s in some sequence
1154   }*/
1155 }
1156 
checkNonSums(const GrobLat & lat)1157 void checkNonSums(const GrobLat& lat) {
1158   const vector<Neighbor>& nonSums = lat.getNonSums();
1159   CHECK(nonSums.size() == 3 || nonSums.size() == 4);
1160   if (nonSums.size() == 3) {
1161 	Matrix mat(3, 3);
1162 	for (size_t ns = 0; ns < 3; ++ns)
1163 	  for (size_t var = 0; var < 3; ++var)
1164 		mat(ns, var) = nonSums[ns].getH(var);
1165 	mpq_class det = determinant(mat);
1166 	CHECK(det == 1 || det == -1);
1167   } else {
1168 	Matrix mat(4, 3);
1169 	for (size_t ns = 0; ns < 4; ++ns)
1170 	  for (size_t var = 0; var < 3; ++var)
1171 		mat(ns, var) = nonSums[ns].getY(var);
1172 	CHECK(isParallelogram(mat));
1173 
1174 	return; // not checking this as it seems to not be true
1175 	mpq_class areaSq = getParallelogramAreaSq(mat);
1176 	CHECK(areaSq == 1);
1177   }
1178 }
1179 
checkFlatSeq(const vector<SeqPos> & flatSeq,const GrobLat & lat,const Plane & plane)1180 void checkFlatSeq(const vector<SeqPos>& flatSeq,
1181 				  const GrobLat& lat,
1182 				  const Plane& plane) {
1183   if (flatSeq.empty())
1184 	return;
1185   size_t sumf = flatSeq.front().mlfb->getMinInitialFacet();
1186   CHECK(sumf != 0);
1187 
1188   size_t af = 4;
1189   for (size_t j = 0; j < 4; ++j) {
1190 	if (j != 0 && j != sumf) {
1191 	  af = j;
1192 	  break;
1193 	}
1194   }
1195 
1196   size_t bf = 4;
1197   for (size_t j = 0; j < 4; ++j) {
1198 	if (j != 0 && j != sumf && j != af) {
1199 	  bf = j;
1200 	  break;
1201 	}
1202   }
1203 
1204   for (size_t i = 0; i < flatSeq.size(); ++i) {
1205 	const Mlfb& flat = *(flatSeq[i].mlfb);
1206 	Neighbor sum = flat.getPoint(sumf);
1207 	Neighbor a = flat.getPoint(af);
1208 	Neighbor b = flat.getPoint(bf);
1209 
1210 	CHECK(sumf == flat.getMinInitialFacet());
1211 	CHECK(lat.getSum(a, b) == sum);
1212 
1213 	// right-going edges
1214 	if (i < flatSeq.size() - 1) {
1215 	  // not at right end
1216 	  CHECK(flat.getEdge(af) == flat.getEdge(bf));
1217 	  const Mlfb& next = *(flatSeq[i + 1].mlfb);
1218 	  CHECK(flat.getEdge(af) == &next);
1219 	  if (flat.getHitsFacet(af) == sumf) {
1220 		CHECK(flat.getHitsFacet(bf) == 0);
1221 		CHECK(next.hasPoint(b));
1222 		CHECK(next.hasPoint(sum));
1223 		CHECK(next.hasPoint(lat.getSum(sum, b)));
1224 	  } else {
1225 		CHECK(flat.getHitsFacet(af) == 0);
1226 		CHECK(flat.getHitsFacet(bf) == sumf);
1227 		CHECK(next.hasPoint(a));
1228 		CHECK(next.hasPoint(sum));
1229 		CHECK(next.hasPoint(lat.getSum(sum, a)));
1230 	  }
1231 	} else {
1232 	  // at right end
1233 	  CHECK(plane.isPivot(*flat.getEdge(af)));
1234 	  CHECK(plane.isPivot(*flat.getEdge(bf)));
1235 	  CHECK(flat.getEdge(af) != flat.getEdge(bf));
1236 	}
1237 
1238 	// left-going edges
1239 	if (i > 0) {
1240 	  // not at left end
1241 	  CHECK(flat.getEdge(0) == flat.getEdge(sumf));
1242 	  const Mlfb& prev = *(flatSeq[i - 1].mlfb);
1243 	  CHECK(flat.getEdge(0) == &prev);
1244 	  if (flat.getHitsFacet(0) == af) {
1245 		CHECK(flat.getHitsFacet(sumf) == bf);
1246 	  } else {
1247 		CHECK(flat.getHitsFacet(0) == bf);
1248 		CHECK(flat.getHitsFacet(sumf) == af);
1249 	  }
1250 	} else {
1251 	  // at left end
1252 	  CHECK(plane.isPivot(*flat.getEdge(0)));
1253 	  CHECK(plane.isPivot(*flat.getEdge(sumf)));
1254 	  CHECK(flat.getEdge(0) != flat.getEdge(sumf));
1255 	}
1256   }
1257 }
1258 
checkPlaneTri(const GrobLat & lat,const vector<Mlfb> & mlfbs,const vector<const Mlfb * > & pivots,const Plane & plane)1259 void checkPlaneTri(const GrobLat& lat,
1260 				   const vector<Mlfb>& mlfbs,
1261 				   const vector<const Mlfb*>& pivots,
1262 				   const Plane& plane) {
1263   const Tri& tri = plane.tri;
1264   Neighbor a = tri.getA();
1265   Neighbor b = tri.getB();
1266   Neighbor sum = tri.getSum();
1267 
1268   // ** tri is not a flat
1269   for (size_t i = 0; i < mlfbs.size(); ++i) {
1270 	if (plane.isFlat(mlfbs[i])) {
1271 	  CHECK(!mlfbs[i].hasPoint(a) ||
1272 			!mlfbs[i].hasPoint(b) ||
1273 			!mlfbs[i].hasPoint(sum));
1274 	}
1275   }
1276 
1277   // ** find unique non-flat MLFB with {0,a,a+b}
1278   const Mlfb* mlfbA = 0;
1279   for (size_t i = 0; i < mlfbs.size(); ++i) {
1280 	if (!plane.isFlat(mlfbs[i]) &&
1281 		mlfbs[i].hasPoint(a) &&
1282 		mlfbs[i].hasPoint(sum)) {
1283 	  CHECK(mlfbA == 0);
1284 	  mlfbA = &(mlfbs[i]);
1285 	}
1286   }
1287   CHECK(mlfbA != 0);
1288 
1289   // ** find unique non-flat MLFB with {0,b,a+b}
1290   const Mlfb* mlfbB = 0;
1291   for (size_t i = 0; i < mlfbs.size(); ++i) {
1292 	if (!plane.isFlat(mlfbs[i]) &&
1293 		mlfbs[i].hasPoint(b) &&
1294 		mlfbs[i].hasPoint(sum)) {
1295 	  CHECK(mlfbB == 0);
1296 	  mlfbB = &(mlfbs[i]);
1297 	}
1298   }
1299   CHECK(mlfbB != 0);
1300 
1301   // ** both parts must be pivots
1302   CHECK(plane.isPivot(*mlfbA));
1303   CHECK(plane.isPivot(*mlfbB));
1304 
1305   // ** the two pivots must be on the left
1306   CHECK((mlfbA == pivots[0] && mlfbB == pivots[1]) ||
1307 		(mlfbA == pivots[1] && mlfbB == pivots[0]));
1308 }
1309 
getEdgePos(size_t index)1310 const char* getEdgePos(size_t index) {
1311   switch (index) {
1312   case 0: return "sw";
1313   case 1: return "se";
1314   case 2: return "ne";
1315   case 3: return "nw";
1316   default: ASSERT(false);
1317 	return "ERROR";
1318   }
1319 }
1320 
getIndexSum(const vector<Mlfb> & mlfbs)1321 mpq_class getIndexSum(const vector<Mlfb>& mlfbs) {
1322   mpq_class sum;
1323   for (size_t m = 0; m < mlfbs.size(); ++m)
1324 	sum += mlfbs[m].index;
1325   return sum;
1326 }
1327 
SeqPos()1328 SeqPos::SeqPos() {}
SeqPos(const Mlfb * mlfbParam,size_t nextFacet,size_t previousFacet)1329 SeqPos::SeqPos(const Mlfb* mlfbParam, size_t nextFacet, size_t previousFacet) {
1330   ASSERT(mlfbParam != 0);
1331   ASSERT(nextFacet != previousFacet);
1332   ASSERT(nextFacet < 4);
1333   ASSERT(previousFacet < 4);
1334   mlfb = mlfbParam;
1335 
1336   comingFromFacet = previousFacet;
1337   for (size_t f = 0; f < 4; ++f)
1338 	if (f != previousFacet && f != nextFacet)
1339 	  fixFacet1 = f;
1340   for (size_t f = 0; f < 4; ++f)
1341 	if (f != previousFacet && f != nextFacet && f != fixFacet1)
1342 	  fixFacet2 = f;
1343 }
1344 
getForwardFacet() const1345 size_t SeqPos::getForwardFacet() const {
1346   for (size_t i = 0; ; ++i) {
1347 	ASSERT(i < 4);
1348 	if (i != fixFacet1 && i != fixFacet2 && i != comingFromFacet)
1349 	  return i;
1350   }
1351 }
1352 
getBackFacet() const1353 size_t SeqPos::getBackFacet() const {
1354   return comingFromFacet;
1355 }
1356 
getReverse() const1357 SeqPos SeqPos::getReverse() const {
1358   size_t to;
1359   for (to = 0; to < 4; ++to) {
1360 	if (to != fixFacet1 && to != fixFacet2 && to != comingFromFacet)
1361 	  break;
1362   }
1363   ASSERT(to != 4);
1364   SeqPos reverse = *this;
1365   reverse.comingFromFacet = to;
1366   return reverse;
1367 }
1368 
order()1369 void SeqPos::order() {
1370   if (fixFacet1 > fixFacet2)
1371 	swap(fixFacet1, fixFacet2);
1372 }
1373 
operator <(const SeqPos & pos) const1374 bool SeqPos::operator<(const SeqPos& pos) const {
1375   if (mlfb->getOffset() < pos.mlfb->getOffset())
1376 	return true;
1377   if (mlfb->getOffset() > pos.mlfb->getOffset())
1378 	return false;
1379 
1380   if (fixFacet1 < pos.fixFacet1)
1381 	return true;
1382   if (fixFacet1 > pos.fixFacet1)
1383 	return false;
1384 
1385   if (fixFacet2 < pos.fixFacet2)
1386 	return true;
1387   if (fixFacet2 > pos.fixFacet2)
1388 	return false;
1389 
1390   if (comingFromFacet < pos.comingFromFacet)
1391 	return true;
1392   if (comingFromFacet > pos.comingFromFacet)
1393 	return false;
1394 
1395   return false;
1396 }
1397 
Neighbor()1398 Neighbor::Neighbor():
1399   _lat(0), _row(0) {
1400 }
1401 
Neighbor(const GrobLat & lat)1402 Neighbor::Neighbor(const GrobLat& lat):
1403   _lat(&lat), _row(lat.getNeighborCount() + 1) {
1404 }
1405 
Neighbor(const GrobLat & lat,const size_t row)1406 Neighbor::Neighbor(const GrobLat& lat, const size_t row):
1407   _lat(&lat), _row(row) {
1408 }
1409 
getH(size_t i) const1410 const mpq_class& Neighbor::getH(size_t i) const {
1411   ASSERT(isValid());
1412   ASSERT(i < getHDim());
1413   if (isZero())
1414 	return _lat->getZero();
1415   else
1416 	return _lat->getHMatrix()(_row, i);
1417 }
1418 
getHDim() const1419 size_t Neighbor::getHDim() const {
1420   ASSERT(isValid());
1421   return _lat->getHDim();
1422 }
1423 
getY(size_t i) const1424 const mpq_class& Neighbor::getY(size_t i) const {
1425   ASSERT(isValid());
1426   ASSERT(i < getYDim());
1427   if (isZero())
1428 	return _lat->getZero();
1429   else
1430 	return _lat->getYMatrix()(_row, i);
1431 }
1432 
getYDim() const1433 size_t Neighbor::getYDim() const {
1434   ASSERT(isValid());
1435   return _lat->getYDim();
1436 }
1437 
isZero() const1438 bool Neighbor::isZero() const {
1439   ASSERT(isValid());
1440   return _row == _lat->getNeighborCount() + 1;
1441 }
1442 
isValid() const1443 bool Neighbor::isValid() const {
1444   return _lat != 0;
1445 }
1446 
getTypeCount(size_t type) const1447 size_t Plane::getTypeCount(size_t type) const {
1448   map<size_t, size_t>::const_iterator it = typeCounts.find(type);
1449   if (it == typeCounts.end())
1450 	return 0u;
1451   else
1452 	return it->second;
1453 }
1454 
getMaxType() const1455 size_t Plane::getMaxType() const {
1456   if (typeCounts.empty())
1457 	return 0u;
1458   else
1459 	return typeCounts.rbegin()->first;
1460 }
1461 
getPlace(Neighbor neighbor) const1462 NeighborPlace Plane::getPlace(Neighbor neighbor) const {
1463   if (neighbor.isZero())
1464 	return InPlane;
1465   ASSERT(neighbor.getRow() < neighborPlace.size());
1466   return neighborPlace[neighbor.getRow()];
1467 }
1468 
inPlane(Neighbor neighbor) const1469 bool Plane::inPlane(Neighbor neighbor) const {
1470   return getPlace(neighbor) == InPlane;
1471 }
1472 
isPivot(const Mlfb & mlfb) const1473 bool Plane::isPivot(const Mlfb& mlfb) const {
1474   const size_t type = getType(mlfb);
1475   return type == 1 || type == 3;
1476 }
1477 
isSidePivot(const Mlfb & mlfb) const1478 bool Plane::isSidePivot(const Mlfb& mlfb) const {
1479   if (!isPivot(mlfb))
1480 	return false;
1481   for (size_t i = 0; i < 4; ++i)
1482 	if (is22(*(mlfb.getEdge(i))))
1483 	  return true;
1484   return false;
1485 }
1486 
is22(const Mlfb & mlfb) const1487 bool Plane::is22(const Mlfb& mlfb) const {
1488   const size_t type = getType(mlfb);
1489   return type == 2;
1490 }
1491 
isFlat(const Mlfb & mlfb) const1492 bool Plane::isFlat(const Mlfb& mlfb) const {
1493   return getType(mlfb) == 4;
1494 }
1495 
isSpecial() const1496 bool Neighbor::isSpecial() const {
1497   ASSERT(isValid());
1498   for (size_t i = 1; i < _lat->getYDim(); ++i)
1499 	if (getY(i) <= 0)
1500 	  return false;
1501   return true;
1502 }
1503 
isGenerator() const1504 bool Neighbor::isGenerator() const {
1505   if (isZero())
1506 	return false;
1507   else
1508 	return !_lat->isSum(*this);
1509 }
1510 
getType(const Mlfb & mlfb) const1511 size_t Plane::getType(const Mlfb& mlfb) const {
1512   size_t type = 0;
1513   for (size_t i = 0; i < mlfb.getPointCount(); ++i)
1514 	if (inPlane(mlfb.getPoint(i)))
1515 	  ++type;
1516   return type;
1517 }
1518 
getName() const1519 string Neighbor::getName() const {
1520   if (isZero())
1521 	return "zero";
1522   if (!isValid())
1523 	return "none";
1524   ostringstream name;
1525   name << 'n' << (getRow() + 1);
1526   if (isSpecial())
1527 	name << 's';
1528   if (isGenerator())
1529 	name << 'g';
1530   return name.str();
1531 }
1532 
GrobLat(const Matrix & matrix,const SatBinomIdeal & ideal)1533 GrobLat::GrobLat(const Matrix& matrix, const SatBinomIdeal& ideal) {
1534   _ideal = ideal;
1535   _ideal.getMatrix(_y);
1536 
1537   // transpose in preparation for solve
1538   transpose(_y);
1539   transpose(_mat, matrix);
1540 
1541   solve(_h, _mat, _y);
1542 
1543   // un-transpose
1544   transpose(_mat);
1545   transpose(_y);
1546   transpose(_h);
1547 
1548   _isSumRow.resize(getNeighborCount());
1549   for (size_t i = 0; i < getNeighborCount(); ++i) {
1550 	for (size_t j = 0; j < i; ++j) {
1551 	  Neighbor sum = getSum(getNeighbor(i), getNeighbor(j));
1552 	  if (sum.isValid())
1553 		_isSumRow[sum.getRow()] = true;
1554 	}
1555   }
1556 
1557   _nonSums.clear();
1558   for (size_t i = 0; i < _isSumRow.size(); ++i)
1559 	if (!_isSumRow[i])
1560 	  _nonSums.push_back(getNeighbor(i));
1561 }
1562