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