1 //   Copyright (c)  2006-2009,2013  John Abbott
2 //   Main authors: Laura Torrente (assisted by John Abbott)
3 
4 //   This file is part of the source of CoCoALib, the CoCoA Library.
5 
6 //   CoCoALib is free software: you can redistribute it and/or modify
7 //   it under the terms of the GNU General Public License as published by
8 //   the Free Software Foundation, either version 3 of the License, or
9 //   (at your option) any later version.
10 
11 //   CoCoALib is distributed in the hope that it will be useful,
12 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //   GNU General Public License for more details.
15 
16 //   You should have received a copy of the GNU General Public License
17 //   along with CoCoALib.  If not, see <http://www.gnu.org/licenses/>.
18 
19 
20 #include "CoCoA/ApproxPts.H"
21 
22 #include "CoCoA/BigRat.H"
23 #include "CoCoA/RingTwinFloat.H"
24 #include "CoCoA/assert.H"
25 #include "CoCoA/convert.H"
26 #include "CoCoA/error.H"
27 #include "CoCoA/ring.H"
28 #include "CoCoA/utils.H"
29 
30 #include <algorithm>
31 using std::find_if;
32 #include <cmath>
33 // using std::log; // only in UnitVolBallRadius !! Shadowed by the CoCoALib fn log(BigInt) !!
34 // using std::exp; // only in UnitVolBallRadius
35 #include <list>
36 using std::list;
37 //#include <vector>
38 using std::vector;
39 
40 
41 namespace CoCoA
42 {
43 
44   typedef vector<double> PointDbl;
45 
46   namespace // anonymous namespace for file local auxiliary functions and definitions.
47   {
48 
49     // The square of x -- useful if x is a big expression.
square(double x)50     inline double square(double x)
51     {
52       return x*x;
53     }
54 
55 
56     // This is not really specific to approximate points, but where else should it go?
57     // Compute radius of D-ball having volume 1.
58     // (I got the formula from Wikipedia under "hypersphere")
UnitVolBallRadius(long D)59     double UnitVolBallRadius(long D)
60     {
61       using std::log;
62       using std::exp;
63       CoCoA_ASSERT(D < 10000); // disallow very high dimensions
64       const double pi = 4*atan(1.0);
65       double logfactor = (D/2)*(-log(2*pi)); // D/2 exploits integer division!  NB -(D/2)*log(...) does NOT WORK.
66       if (D & 1)
67         logfactor -= log(2.0);
68       for (long k=D; k > 1; k -= 2)
69         logfactor += log(double(k));
70       return exp(logfactor/D);
71     }
72 
73 
74     // Compute square of 2-norm between P and Q.
L2NormSq(const PointDbl & P,const PointDbl & Q)75     double L2NormSq(const PointDbl& P, const PointDbl& Q)
76     {
77       CoCoA_ASSERT(len(P) == len(Q));
78       double SumSq = 0;
79       const long dim = len(P);
80       for (long i=0; i < dim; ++i)
81         SumSq += square(P[i]-Q[i]);
82       return SumSq;
83     }
84 
85 
86     // Data structure for representing PreProcessed Points.
87     struct PPP
88     {
89     public: // data members
90       PointDbl myCoords;            // the coords of the preprocessed point -- average of myOrigPts
91       long myNumOrigPts;            // length of list myOrigPts (used only in PreprocessSubdivAlgm)
92       list<long> myOrigPts;         // indices into a master table
93       vector<double> myDistanceSq;  // used only in PreprocessSubdivAlgm
94       long myWorstOrigPt;         // the index of the farthest point
95     };
96 
97 
98     // Returns the average of a set of PointDbls; the set is indicated by the first arg
99     // which is a list of indices into the second arg.
SubsetAve(const list<long> & IndexList,const vector<PointDbl> & pts)100     PointDbl SubsetAve(const list<long>& IndexList, const vector<PointDbl>& pts)
101     {
102       CoCoA_ASSERT(!pts.empty());
103       const long dim = len(pts[0]);
104       PointDbl CofG(dim);
105 //      for (list<long>::const_iterator it=IndexList.begin(); it != IndexList.end(); ++it)
106         for(long i: IndexList)
107       {
108         const PointDbl& P = pts[i];
109         for (long j=0; j < dim; ++j)
110           CofG[j] += P[j];
111       }
112 
113       const long n = len(IndexList);
114       for (long j=0; j < dim; ++j)
115         CofG[j] /= n;
116       return CofG;
117     }
118 
119 
SubsetAve(const list<long> & IndexList,const vector<ApproxPts::PointR> & pts)120     ApproxPts::PointR SubsetAve(const list<long>& IndexList, const vector<ApproxPts::PointR>& pts)
121     {
122       CoCoA_ASSERT(!pts.empty());
123       const long dim = len(pts[0]);
124       ApproxPts::PointR ave(dim, zero(owner(pts[0][0])));
125 //      for (list<long>::const_iterator it=IndexList.begin(); it != IndexList.end(); ++it)
126       for (long i: IndexList)
127       {
128         const ApproxPts::PointR& P = pts[i];
129         for (long j=0; j < dim; ++j)
130           ave[j] += P[j];
131       }
132 
133       const long n = len(IndexList);
134       for (long j=0; j < dim; ++j)
135         ave[j] /= n;
136       return ave;
137     }
138 
139 
140     // Choose a suitable positive value for any tolerance component which is zero,
141     // and replace that component by the chosen postive value.
ZeroToleranceHack(const vector<ApproxPts::PointR> & OrigPts,vector<RingElem> & tolerance)142     void ZeroToleranceHack(const vector<ApproxPts::PointR>& OrigPts, vector<RingElem>& tolerance)
143     {
144       const long dim = len(tolerance);
145       const long NumPts = len(OrigPts);
146       for (long i=0; i < dim; ++i)
147       {
148         if (tolerance[i] != 0) continue;
149 
150         RingElem MinDist = tolerance[i]; // really just assignment to zero.
151         for (long j=0; j < NumPts; ++j)
152           for (long k=j+1; k < NumPts; ++k)
153           {
154             const RingElem d = abs(OrigPts[j][i]-OrigPts[k][i]);
155             if (MinDist == 0 || d < MinDist)
156               MinDist = d;
157           }
158         if (MinDist != 0)
159           tolerance[i] = MinDist/4;
160         else
161           tolerance[i] = 1;
162       }
163     }
164 
165 
166     // Rescale coords of pt by factors in array ScaleFactor.
ScalePt(PointDbl & ScaledPt,const ApproxPts::PointR & OrigPt,const vector<RingElem> & ScaleFactor)167     void ScalePt(PointDbl& ScaledPt, const ApproxPts::PointR& OrigPt, const vector<RingElem>& ScaleFactor)
168     {
169       CoCoA_ASSERT(len(OrigPt) > 0);
170       CoCoA_ASSERT(len(OrigPt) == len(ScaleFactor));
171       const long dim = len(OrigPt);
172       ScaledPt.resize(dim);
173       BigRat Q;
174       RingElem coord(owner(OrigPt[0]));
175       for (long i=0; i < dim; ++i)
176       {
177         coord = OrigPt[i] * ScaleFactor[i];
178         if (!IsRational(Q, coord) || !IsConvertible(ScaledPt[i], Q))
179           CoCoA_ERROR("Cannot convert to double", "ScalePt");
180       }
181     }
182 
183     // Rescale the OrigPts and convert them into PointDbl
NormalizePts(vector<PointDbl> & ScaledPts,const vector<ApproxPts::PointR> & OrigPts,const vector<RingElem> & tolerance)184     void NormalizePts(vector<PointDbl>& ScaledPts, const vector<ApproxPts::PointR>& OrigPts, const vector<RingElem>& tolerance)
185     {
186       const long NumPts = len(OrigPts);
187       const long dim = len(tolerance);
188 
189       ring R = owner(tolerance[0]);
190       vector<RingElem> InverseTolerance(dim, zero(R));
191       for (long j=0; j < dim; ++j)
192         InverseTolerance[j] = 1/tolerance[j];
193 
194       ScaledPts.resize(NumPts);
195       for (long i=0; i < NumPts; ++i)
196         ScalePt(ScaledPts[i], OrigPts[i], InverseTolerance);
197     }
198 
199 
200     /////////////////////////////////////////////////////////////////////////////
201     // Auxiliary definitions for the grid algorithm.
202 
GridNearPoint(const PointDbl & P)203     PointDbl GridNearPoint(const PointDbl& P)
204     {
205       const long dim = len(P);
206       PointDbl GNP(dim);
207       for (long i=0; i < dim; ++i)
208         GNP[i] = 2*round(P[i]/2);
209       return GNP;
210     }
211 
212 
213     // Only used in GridAlgm as arg to std::find.
214     // WARNING data member is a reference!!
215     class CoordsEq
216     {
217     public:
CoordsEq(const PointDbl & P)218       CoordsEq(const PointDbl& P): myPointDbl(P) {};
operator()219       bool operator()(const PPP& PPPt) { return myPointDbl == PPPt.myCoords; };
220     private: // data members
221       const PointDbl& myPointDbl;
222     };
223 
224 
225     /////////////////////////////////////////////////////////////////////////////
226     // Functions for aggregative algorithm
227 
228     // ave = (w1*P1 + w2*P2)/(w1+w2);
WeightedAve(PointDbl & ave,double w1,const PointDbl & P1,double w2,const PointDbl & P2)229     void WeightedAve(PointDbl& ave, double w1, const PointDbl& P1, double w2, const PointDbl& P2)
230     {
231       const long dim = len(ave);
232       CoCoA_ASSERT(len(P1) == dim && len(P2) == dim);
233       CoCoA_ASSERT(w1+w2 != 0);
234       for (long i=0; i < dim; ++i)
235       {
236         ave[i] = (w1*P1[i]+w2*P2[i])/(w1+w2);
237       }
238     }
239 
240 
241     struct NearPair
242     {
243     public:
NearPairNearPair244       NearPair(long i, long j, double sepsq): myLowerIndex(i), myHigherIndex(j), mySepSq(sepsq) {};
245     public: // data members
246       const long myLowerIndex, myHigherIndex;
247       double mySepSq;
248     };
249 
250 
251     // Used only as arg to std::sort algorithm
LessNearPair(const NearPair & P1,const NearPair & P2)252     inline bool LessNearPair(const NearPair& P1, const NearPair& P2)
253     {
254       return P1.mySepSq < P2.mySepSq;
255     }
256 
257 
258     // Used in call to algorithm std::remove_if.
259     class NearPairContains
260     {
261     public:
NearPairContains(long j)262       NearPairContains(long j): myIndex(j) {}
operator()263       bool operator()(const NearPair& P) { return P.myLowerIndex == myIndex || P.myHigherIndex == myIndex; }
264     private: // data member
265       const long myIndex;
266     };
267 
268 
269 
270 
271     /////////////////////////////////////////////////////////////////////////////
272     // Below is auxiliary code for the "subdiv" algorithm.
273 
274     // Fill in data member myDistanceSq of X
ComputeDistSq(PPP & X,const vector<PointDbl> & OrigPts)275     void ComputeDistSq(PPP& X, const vector<PointDbl>& OrigPts)
276     {
277       for (long i=0; i < len(OrigPts); ++i)
278         X.myDistanceSq[i] = L2NormSq(X.myCoords, OrigPts[i]);
279 
280       double WorstDist = 0;
281 //      for (list<long>::const_iterator it = X.myOrigPts.begin(); it != X.myOrigPts.end(); ++it)
282       for (long i: X.myOrigPts)
283         if (X.myDistanceSq[i] >= WorstDist)
284         {
285           WorstDist = X.myDistanceSq[i];
286           X.myWorstOrigPt = i;
287         }
288     }
289 
290 
291     // Make Y a copy of X but without the i-th OrigPt.
EraseAndUpdate(PPP & Y,const PPP & X,long i,const vector<PointDbl> & V)292     void EraseAndUpdate(PPP& Y, const PPP& X, long i, const vector<PointDbl>& V)
293     {
294       Y.myOrigPts = X.myOrigPts;
295       Y.myOrigPts.remove(i);
296       Y.myNumOrigPts = X.myNumOrigPts-1;
297       Y.myCoords = SubsetAve(Y.myOrigPts, V);
298       ComputeDistSq(Y, V);
299     }
300 
301 
302     // Make Y a copy of X with the i-th OrigPt added in.
AddAndUpdate(PPP & Y,const PPP & X,long i,const vector<PointDbl> & V)303     void AddAndUpdate(PPP& Y, const PPP& X, long i, const vector<PointDbl>& V)
304     {
305       Y.myOrigPts = X.myOrigPts;
306       Y.myOrigPts.push_back(i);
307       Y.myNumOrigPts = X.myNumOrigPts+1;
308       Y.myCoords = SubsetAve(Y.myOrigPts, V);
309       ComputeDistSq(Y, V);
310     }
311 
312 
313     // Redistribute the assignments of OrigPts to PPPts to minimize the sum of squares of distances
redistribute(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts)314     void redistribute(vector<PPP>& PPPts, const vector<PointDbl>& OrigPts)
315     {
316       CoCoA_ASSERT(len(OrigPts) > 0);
317       const long NumPPP = len(PPPts);
318       bool MadeAnImprovement = true;
319       while (MadeAnImprovement)
320       {
321         MadeAnImprovement = false;
322         double MaxGain = 0.000001; // morally zero but must allow for rounding error
323         long index_from = 0;     // index of PPPt we move from (initialize to keep compiler quiet)
324         long index_to = 0;       // index of PPPt we move to (initialize to keep compiler quiet)
325         long IndexOrigPt = 0;    // index of orig point we want to move (initialize to keep compiler quiet)
326 
327         // For each PPPt i do ...
328         for (long i=0; i < NumPPP; ++i)
329         {
330           const long Ni = PPPts[i].myNumOrigPts;
331           if (Ni == 1) continue;
332           // For each index (of OrigPt associated to PPPt i) it do...
333           for (list<long>::iterator it = PPPts[i].myOrigPts.begin(); it != PPPts[i].myOrigPts.end(); ++it)
334           {
335             const double diffi = (Ni * PPPts[i].myDistanceSq[*it]) / (Ni-1);
336             // For each PPPt j (difft from PPPt i) do...
337             for (long j=0; j < NumPPP; ++j)
338             {
339               if (j == i) continue;
340               const long Nj = PPPts[j].myNumOrigPts;
341               const double diffj = -(Nj * PPPts[j].myDistanceSq[*it]) / (Nj+1); // careful here: Nj is unsigned!
342               const double gain = diffi + diffj;
343               if (gain > MaxGain)
344               {
345                 MadeAnImprovement = true;
346                 MaxGain = gain;
347                 index_from = i;
348                 index_to = j;
349                 IndexOrigPt = *it;
350               }
351             }
352           }
353         }
354 
355         if (MadeAnImprovement)
356         {
357           // Move orig point to new PPPt...
358           AddAndUpdate(PPPts[index_to], PPPts[index_to], IndexOrigPt, OrigPts);
359           EraseAndUpdate(PPPts[index_from], PPPts[index_from], IndexOrigPt, OrigPts);
360         }
361       }
362     }
363 
364 
365     // Initialize the list of preprocessed points for subdivision algm.
PreprocessSubdivInit(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts)366     void PreprocessSubdivInit(vector<PPP>& PPPts, const vector<PointDbl>& OrigPts)
367     {
368       CoCoA_ASSERT(PPPts.empty());
369       PPPts.resize(1);
370       const long NumPts = len(OrigPts);
371       for (long i=0; i < NumPts; ++i)
372         PPPts[0].myOrigPts.push_back(i);
373       PPPts[0].myNumOrigPts = NumPts;
374       PPPts[0].myCoords = SubsetAve(PPPts[0].myOrigPts, OrigPts);
375       PPPts[0].myDistanceSq.resize(NumPts);
376       ComputeDistSq(PPPts[0], OrigPts);
377     }
378 
379 
380     // Determine if some OrigPt is too far from its corresponding PPPt.
381     // If result is true, then WorstOrigIdx and WorstPPPIdx contain the indices
382     // of the most distant pair of orig and preprocessed points.
ExistsBadPoint(long & WorstPPPIdx,const vector<PPP> & V)383     bool ExistsBadPoint(long& WorstPPPIdx, const vector<PPP>& V)
384     {
385       double WorstDist = 0;
386       const long NumPPP = len(V);
387       for (long i=0; i < NumPPP; ++i)
388       {
389         const long BadIndex = V[i].myWorstOrigPt;
390         if (V[i].myDistanceSq[BadIndex] > WorstDist)
391         {
392           WorstDist = V[i].myDistanceSq[BadIndex];
393           WorstPPPIdx = i;
394         }
395       }
396       const long dim = len(V[0].myCoords);
397       const double CriticalRadiusSq = square(2*UnitVolBallRadius(dim)); // inefficient if dim is large and num of pts is small
398       return WorstDist >= CriticalRadiusSq;
399     }
400 
401 
402     // Adjoin a new preprocessed point onto the end of V.
403     // The new point comprises just one OrigPt, the one with index OrigPtIdx.
NewPPP(vector<PPP> & PPPts,const PointDbl & P,long OrigPtIdx)404     void NewPPP(vector<PPP>& PPPts, const PointDbl& P, long OrigPtIdx)
405     {
406       const long NumPPPts = len(PPPts);
407       PPPts.resize(NumPPPts+1);
408       PPP& NewPt(PPPts[NumPPPts]);
409       NewPt.myCoords = P;
410       NewPt.myOrigPts.push_back(OrigPtIdx);
411       NewPt.myNumOrigPts = 1;
412       NewPt.myDistanceSq.push_back(0.0);
413     }
414 
415     // Adjoin a new preprocessed point onto the end of V.
416     // The new point comprises just one OrigPt, the one with index OrigPtIdx.
NewPPPSubdiv(vector<PPP> & PPPts,const vector<PointDbl> & OrigPts,long OrigPtIdx)417     void NewPPPSubdiv(vector<PPP>& PPPts,
418                       const vector<PointDbl>& OrigPts,
419                       long OrigPtIdx)
420     {
421       const long NumPPPts = len(PPPts);
422       PointDbl P = OrigPts[OrigPtIdx];
423       PPPts.resize(NumPPPts+1);
424       PPP& NewPt = PPPts[NumPPPts];
425       NewPt.myCoords = P;
426       NewPt.myOrigPts.push_back(OrigPtIdx);
427       NewPt.myNumOrigPts = 1;
428       NewPt.myDistanceSq.resize(len(OrigPts));
429       ComputeDistSq(NewPt, OrigPts);
430       NewPt.myWorstOrigPt = OrigPtIdx;
431     }
432 
433 
434   } // end of anonymous namespace
435 
436 
PreprocessPts(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)437   void PreprocessPts(std::vector<ApproxPts::PointR>& NewPts,
438                      std::vector<long>& weights,
439                      const std::vector<ApproxPts::PointR>& OrigPts,
440                      std::vector<RingElem> tolerance)
441   {
442     // NOT EXCEPTION CLEAN!!!  But does it matter in this case??
443     PreprocessPtsGrid(NewPts, weights, OrigPts, tolerance);
444     // Crude heuristic for choosing between "aggr" and "subdiv"... needs improving!!!
445     if (len(NewPts) > len(OrigPts)/len(NewPts)) // equiv len(NewPts) > sqrt(len(OrigPts))
446       PreprocessPtsAggr(NewPts, weights, OrigPts, tolerance);
447     else
448       PreprocessPtsSubdiv(NewPts, weights, OrigPts, tolerance);
449   }
450 
451 
PreprocessCheckArgs(const std::vector<ApproxPts::PointR> & OrigPts,const std::vector<RingElem> & tolerance,const char * const FnName)452   void PreprocessCheckArgs(const std::vector<ApproxPts::PointR>& OrigPts,
453                            const std::vector<RingElem>& tolerance,
454                            const char* const FnName)
455   {
456     // Some sanity checks on the inputs.
457     const long NumPts = len(OrigPts);
458     if (NumPts == 0) CoCoA_ERROR(ERR::Empty, FnName);
459     const long dim = len(OrigPts[0]);
460     if (dim == 0) CoCoA_ERROR(ERR::Empty, FnName);
461     if (len(tolerance) != dim) CoCoA_ERROR(ERR::IncompatDims, FnName);
462 
463     // Check that all points lie in the same space over the same ring.
464     ring R = owner(OrigPts[0][0]);
465     if (!IsOrderedDomain(R)) CoCoA_ERROR(ERR::NotOrdDom, FnName);
466     for (long i=0; i < NumPts; ++i)
467     {
468       if (len(OrigPts[i]) != dim) CoCoA_ERROR(ERR::IncompatDims, FnName);
469       for (long j=0; j < dim; ++j)
470         if (owner(OrigPts[i][j]) != R) CoCoA_ERROR(ERR::MixedRings, FnName);
471     }
472     for (long j=0; j < dim; ++j)
473     {
474       if (owner(tolerance[j]) != R) CoCoA_ERROR(ERR::MixedRings, FnName);
475       if (tolerance[j] < 0) CoCoA_ERROR(ERR::NotNonNegative, FnName);
476     }
477   }
478 
479   /////////////////////////////////////////////////////////////////////////////
480   // Below are the three main algorithms.
481   // First the "grid" algorithm.
482 
PreprocessPtsGrid(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)483   void PreprocessPtsGrid(std::vector<ApproxPts::PointR>& NewPts,
484                          std::vector<long>& weights,
485                          const std::vector<ApproxPts::PointR>& OrigPts,
486                          std::vector<RingElem> tolerance)
487   {
488     PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsGrid");
489     const long NumPts = len(OrigPts);
490 
491     // Normalization effectively makes the tolerance in each direction equal to 1.
492     ZeroToleranceHack(OrigPts, tolerance);
493     vector<PointDbl> ScaledPts; // value is set by NormalizePts
494     NormalizePts(ScaledPts, OrigPts, tolerance);
495 
496     vector<PPP> PPPts; PPPts.reserve(NumPts);
497     // For each OrigPts[i] do...
498     for (long i = 0; i < NumPts; ++i)
499     {
500       const PointDbl P = GridNearPoint(ScaledPts[i]);
501       const vector<PPP>::iterator pos = find_if(PPPts.begin(), PPPts.end(), CoordsEq(P));
502       if (pos != PPPts.end())
503       {
504         pos->myOrigPts.push_back(i);  // adjoin to existing class
505         ++(pos->myNumOrigPts);        // increment myNumOrigPts
506       }
507       else
508         NewPPP(PPPts, P, i);          // add a new PPPt
509     }
510 
511     const long NumPPPts = len(PPPts);
512     NewPts.resize(NumPPPts);
513     weights.resize(NumPPPts);
514     for (long k = 0; k < NumPPPts; ++k)
515     {
516       NewPts[k] = SubsetAve(PPPts[k].myOrigPts, OrigPts);
517       weights[k] = PPPts[k].myNumOrigPts;
518     }
519   }
520 
521 
522   /////////////////////////////////////////////////////////////////////////////
523   // The "aggregative" algorithm.
524 
PreprocessPtsAggr(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)525   void PreprocessPtsAggr(std::vector<ApproxPts::PointR>& NewPts,
526                          std::vector<long>& weights,
527                          const std::vector<ApproxPts::PointR>& OrigPts,
528                          std::vector<RingElem> tolerance)
529   {
530     PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsAggr");
531     const long NumPts = len(OrigPts);
532     const long dim = len(tolerance);
533 
534     // Normalization effectively makes the tolerance in each direction equal to 1.
535     ZeroToleranceHack(OrigPts, tolerance);
536     vector<PointDbl> ScaledPts; // value set by NormalizePts
537     NormalizePts(ScaledPts, OrigPts, tolerance);
538 
539     vector<PPP> PPPts(NumPts);
540     for (long i=0; i < NumPts; ++i)
541     {
542       PPPts[i].myCoords = ScaledPts[i];
543       PPPts[i].myNumOrigPts = 1;
544       PPPts[i].myOrigPts.push_back(i);
545     }
546 
547     // nbrs contains all pairs which could conceivably collapse together
548     // (i.e. only pairs separated by at most the CriticalRadius)
549     const double CriticalRadiusSq = square(2*UnitVolBallRadius(dim));
550     list<NearPair> nbrs;
551     for (long i=0; i < NumPts; ++i)
552     {
553       for (long j=i+1; j < NumPts; ++j)
554       {
555         const double d2 = L2NormSq(PPPts[i].myCoords, PPPts[j].myCoords);
556         if (d2 < 4*CriticalRadiusSq)
557           nbrs.push_back(NearPair(i,j,d2));
558       }
559     }
560 
561     nbrs.sort(LessNearPair);
562 
563     PointDbl CofG(dim);
564     while (!nbrs.empty() && nbrs.begin()->mySepSq < 4*CriticalRadiusSq)
565     {
566       const long i = nbrs.front().myLowerIndex;
567       const long j = nbrs.front().myHigherIndex;
568       nbrs.erase(nbrs.begin()); // delete first elem of nbrs
569 
570       const PPP& Pi = PPPts[i];
571       const PPP& Pj = PPPts[j];
572       // Compute CofG of the union of Pi an Pj...
573       WeightedAve(CofG, len(Pi.myOrigPts), Pi.myCoords, len(Pj.myOrigPts), Pj.myCoords);
574 
575       // Check that all original points in the union are close to CofG.
576       bool OK = true;
577 //      for (list<long>::const_iterator it=Pi.myOrigPts.begin(); OK && it != Pi.myOrigPts.end(); ++it)
578       for (long k: Pi.myOrigPts)
579       {
580         OK &= (L2NormSq(CofG, ScaledPts[k]) < CriticalRadiusSq);
581       }
582 //      for (list<long>::const_iterator it=Pj.myOrigPts.begin(); OK && it != Pj.myOrigPts.end(); ++it)
583       for (long k: Pj.myOrigPts)
584       {
585         OK &= (L2NormSq(CofG, ScaledPts[k]) < CriticalRadiusSq);
586       }
587       if (!OK) continue; // We cannot unite these two points, so go on to next pair of points.
588 
589       // The union is fine, so accept it.  We put all data into i-th PPP
590       // and empty the data of the j-th PPP.
591       {
592         list<long>& PPPOrigPts = PPPts[i].myOrigPts;
593         PPPOrigPts.splice(PPPOrigPts.begin(), PPPts[j].myOrigPts);
594         PPPts[i].myNumOrigPts += PPPts[j].myNumOrigPts;
595         PPPts[j].myNumOrigPts = 0;
596       }
597       nbrs.remove_if(NearPairContains(j)); // since Pj has been eliminated.
598       // Update all entries refering to i-th point.
599       for (list<NearPair>::iterator nbr = nbrs.begin(); nbr != nbrs.end(); ++nbr)
600       {
601         if (!NearPairContains(i)(*nbr)) continue;
602         long other = nbr->myLowerIndex;
603         if (other == i) other = nbr->myHigherIndex;
604         nbr->mySepSq = L2NormSq(CofG, PPPts[other].myCoords);
605       }
606       nbrs.sort(LessNearPair); // put the list back into order -- SLUG SLUG SLUG!!!
607       swap(PPPts[i].myCoords, CofG); // really an assignment to PPPts[i].myCoords
608     }
609 
610     // There are no futher possible mergings, so copy answer into NewPts & weights.
611     NewPts.clear();
612     weights.clear();
613     for (long i=0; i < NumPts; ++i)
614     {
615       const long N = PPPts[i].myNumOrigPts;
616       if (N == 0) continue;
617       NewPts.push_back(SubsetAve(PPPts[i].myOrigPts, OrigPts));
618       weights.push_back(N);
619     }
620   }
621 
622 
623   /////////////////////////////////////////////////////////////////////////////
624   // The "subdivision" algorithm.
625 
PreprocessPtsSubdiv(std::vector<ApproxPts::PointR> & NewPts,std::vector<long> & weights,const std::vector<ApproxPts::PointR> & OrigPts,std::vector<RingElem> tolerance)626   void PreprocessPtsSubdiv(std::vector<ApproxPts::PointR>& NewPts,
627                            std::vector<long>& weights,
628                            const std::vector<ApproxPts::PointR>& OrigPts,
629                            std::vector<RingElem> tolerance)
630   {
631     PreprocessCheckArgs(OrigPts, tolerance, "PreprocessPtsSubdiv");
632     const long NumPts = len(OrigPts);
633 
634     // Normalization effectively makes the tolerance in each direction equal to 1.
635     ZeroToleranceHack(OrigPts, tolerance);
636     vector<PointDbl> ScaledPts; // value is set by NormalizePts
637     NormalizePts(ScaledPts, OrigPts, tolerance);
638 
639     // Initially all orig points are associated to a single preprocessed point.
640     vector<PPP> PPPts; PPPts.reserve(NumPts);
641     PreprocessSubdivInit(PPPts, ScaledPts);
642 
643     // Main loop.
644     long WorstPPPIdx = 0; // Init value just to keep compiler quiet.
645     // True value is set in call to ExistsBadPoint (see next line)
646     while (ExistsBadPoint(WorstPPPIdx, PPPts))
647     {
648       const long WorstOrigIdx = PPPts[WorstPPPIdx].myWorstOrigPt;
649       NewPPPSubdiv(PPPts, ScaledPts, WorstOrigIdx);
650       EraseAndUpdate(PPPts[WorstPPPIdx], PPPts[WorstPPPIdx], WorstOrigIdx, ScaledPts);
651       redistribute(PPPts, ScaledPts);
652     }
653 
654     // Copy and denormalize result; add the weight of each PPP.
655     NewPts.clear(); NewPts.reserve(len(PPPts));
656     weights.clear(); weights.reserve(len(PPPts));
657     for (long i=0; i < len(PPPts); ++i)
658     {
659       const long N = PPPts[i].myNumOrigPts;
660       NewPts.push_back(SubsetAve(PPPts[i].myOrigPts, OrigPts));
661       weights.push_back(N);
662     }
663   }
664 
665 
666 } // end of namespace CoCoA
667 
668 
669 // RCS header/log in the next few lines
670 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/ApproxPts.C,v 1.27 2020/02/18 11:25:12 abbott Exp $
671 // $Log: ApproxPts.C,v $
672 // Revision 1.27  2020/02/18 11:25:12  abbott
673 // Summary: Redmine 1346: new for loop syntax
674 //
675 // Revision 1.26  2017/02/15 12:23:33  abbott
676 // Summary: Added square fn (for double)
677 //
678 // Revision 1.25  2014/05/10 20:46:51  abbott
679 // Summary: Corrected arg checking; also now a separate fn
680 // Author: JAA
681 //
682 // Revision 1.24  2014/05/06 13:13:10  abbott
683 // Summary: Replaced assertions by "always" arg checks
684 // Author: JAA
685 //
686 // Revision 1.23  2014/04/30 16:03:53  abbott
687 // Summary: Eliminated use of sqrt (and corrected the program logic!)
688 // Author: JAA
689 //
690 // Revision 1.22  2013/03/27 18:24:33  abbott
691 // Added approx point preprocessing to C5; also changed names of the fns, and updated doc.
692 //
693 // Revision 1.21  2011/08/24 10:24:17  bigatti
694 // -- renamed QQ --> BigRat
695 // -- sorted #include
696 //
697 // Revision 1.20  2011/08/14 15:52:17  abbott
698 // Changed ZZ into BigInt (phase 1: just the library sources).
699 //
700 // Revision 1.19  2011/03/11 12:01:52  bigatti
701 // -- changed size --> len
702 //
703 // Revision 1.18  2011/03/11 11:08:27  bigatti
704 // -- changed size_t --> long
705 // -- changed size --> len
706 //
707 // Revision 1.17  2009/12/11 11:46:32  abbott
708 // Changed fn  convert  into  IsConvertible.
709 // Added template procedure  convert.
710 // New version because change is not backward compatible.
711 //
712 // Revision 1.16  2009/10/29 19:26:20  abbott
713 // Cleaned up include directives: now in alphabetical order, removed unneeded ones.
714 //
715 // Revision 1.15  2009/09/25 13:12:38  abbott
716 // Added initial value for WorstPPPIdx just to keep compiler quiet.
717 //
718 // Revision 1.14  2009/07/02 16:33:59  abbott
719 // Switched to using new IsRational interface (using a QQ value).
720 //
721 // Revision 1.13  2008/11/24 17:12:14  abbott
722 // Final tidying: renamed an internal fn, removed useless includes.
723 //
724 // Revision 1.12  2008/11/23 18:58:32  abbott
725 // Major overhaul to preprocessing and SOI/NBM code.
726 // Split SOI/NBM off into a separate file.
727 // Preprocessing is now "rational" (but internally guided by C++ doubles).
728 // SOI/NBM now each have 3 similar interfaces: one purely rational, one for
729 // input which is represented as doubles, and one which converts the input
730 // to RingTwinFloat values and produces a result which is over some RingTwinFloat
731 // (the precision is increased automatically until an answer is obtained).
732 //
733 // Revision 1.11  2008/11/20 10:47:30  abbott
734 // Now the code actually computes the list of weights.
735 //
736 // Revision 1.10  2008/11/20 10:08:15  abbott
737 // The preprocessing fns now return also the weights associated with the representatives.
738 //
739 // Revision 1.9  2008/11/06 12:50:44  abbott
740 // Moved definitions of square and round to utils.H from ApproxPts.H
741 //
742 // Revision 1.8  2008/10/07 15:45:22  abbott
743 // Changed ErrorInfo objects so they include the name of their own error ID.
744 // Changed catch statements to catch const objects.
745 // Removed calls to the member fn which accessed the error ID member of an
746 // ErrorInfo; now you simply compare directly with the error ID (makes the
747 // code easier to read).
748 //
749 // Revision 1.7  2008/09/12 13:28:43  bigatti
750 // -- new: NBM implementation
751 //
752 // Revision 1.6  2008/06/04 18:27:37  abbott
753 // Modified the server interface for "SOI": it now accepts a 3rd arg (gamma).
754 //
755 // Revision 1.5  2008/05/30 14:20:43  abbott
756 // SOI now returns also the "almost vanishing" polynomials.
757 //
758 // Revision 1.4  2008/05/30 12:51:08  abbott
759 // Added a comment.
760 //
761 // Revision 1.3  2008/05/29 15:46:29  bigatti
762 // -- added Approximate Border Basis (by Abbott,Torrente)
763 //
764 // Revision 1.2  2007/10/30 17:14:08  abbott
765 // Changed licence from GPL-2 only to GPL-3 or later.
766 // New version for such an important change.
767 //
768 // Revision 1.1.1.1  2007/03/09 15:16:11  abbott
769 // Imported files
770 //
771 // Revision 1.8  2007/03/08 18:22:30  cocoa
772 // Just whitespace cleaning.
773 //
774 // Revision 1.7  2006/12/21 13:48:33  cocoa
775 // Made all increment/decrement calls prefix (except where the must be postfix).
776 //
777 // Revision 1.6  2006/11/22 14:43:32  cocoa
778 // -- minor cleaning (indicated by Intel compiler)
779 //
780 // Revision 1.5  2006/11/20 15:53:38  cocoa
781 // Minor cleaning.  Improved comments.
782 //
783 // Revision 1.4  2006/10/31 14:26:03  cocoa
784 // Added some missing consts; should make the code a little easier to comprehend.
785 //
786 // Revision 1.3  2006/10/06 14:04:15  cocoa
787 // Corrected position of #ifndef in header files.
788 // Separated CoCoA_ASSERT into assert.H from config.H;
789 // many minor consequential changes (have to #include assert.H).
790 // A little tidying of #include directives (esp. in Max's code).
791 //
792 // Revision 1.2  2006/06/21 17:05:47  cocoa
793 // Major overhaul of approx point preprocessing algms.
794 //
795 // Revision 1.1.1.1  2006/05/30 11:39:37  cocoa
796 // Imported files
797 //
798 // Revision 1.3  2006/05/29 16:16:47  cocoa
799 // Added "disj" preprocessing algorithm.
800 //
801 // Revision 1.2  2006/05/22 15:52:16  cocoa
802 // Added preprocess-disg algorithm to ApproxPts.
803 // Sundry minor improvements.
804 //
805 // Revision 1.1  2006/05/12 13:16:30  cocoa
806 // Added functions for preprocessing approximate points.
807 //
808 //
809