1 //   Copyright (c)  1997-2007,2016,2018  John Abbott
2 
3 //   This file is part of the source of CoCoALib, the CoCoA Library.
4 
5 //   CoCoALib is free software: you can redistribute it and/or modify
6 //   it under the terms of the GNU General Public License as published by
7 //   the Free Software Foundation, either version 3 of the License, or
8 //   (at your option) any later version.
9 
10 //   CoCoALib is distributed in the hope that it will be useful,
11 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //   GNU General Public License for more details.
14 
15 //   You should have received a copy of the GNU General Public License
16 //   along with CoCoALib.  If not, see <http://www.gnu.org/licenses/>.
17 
18 #include "CoCoA/MatrixFp.H"
19 #include "CoCoA/matrix.H"
20 #include "CoCoA/DenseMatrix.H"
21 #include "CoCoA/convert.H"
22 #include "CoCoA/RingFp.H"
23 #include "CoCoA/utils.H"
24 #include "CoCoA/VectorOps.H"
25 #include "CoCoA/verbose.H"
26 
27 #include <iostream>
28 using std::endl;
29 #include <vector>
30 using std::vector;
31 
32 // /* Two bits used in the return value */
33 // const int FFsolve_new_pivot = 2;
34 // const int FFsolve_invalid = 1;
35 
36 
37 
38 /* Find a solution to a system of linear equations (in place).          */
39 /* Several right hand side vectors may be specified.                    */
40 /* Method is just direct Gaussian elimination -- fine for finite fields */
41 /* The value returned is the rank of M.                                 */
42 /* The result is placed in soln; if there is no solution for some rhs   */
43 /* vector then its solution has first coordinate equal to p, the modulus.*/
44 
45 namespace CoCoA
46 {
47 
48 
MatrixFp(const SmallFpImpl & ModP,int nrows,int ncols)49   MatrixFp::MatrixFp(const SmallFpImpl& ModP, int nrows, int ncols):
50       myRows(nrows),
51       myCols(ncols),
52       myArith(ModP),
53       myM(nrows, std::vector<SmallFpImpl::value>(ncols))
54   {}
55 
56 
MatrixFp(const ConstMatrixView & M)57   MatrixFp::MatrixFp(const ConstMatrixView& M):
58       myRows(NumRows(M)),
59       myCols(NumCols(M)),
60       myArith(ModularArith(RingOf(M))), // ASSUME small prime!!!
61       myM(myRows, std::vector<SmallFpImpl::value>(myCols))
62   {
63     for (int i=0; i < myRows; ++i)
64       for (int j=0; j < myCols; ++j)
65         myM[i][j] = myArith.myReduce(ConvertTo<long>(M(i,j)));
66   }
67 
68 
69 
70 
71 
72 class MatrixFpNonRed
73 {
74 public:
75   MatrixFpNonRed(long nrows, long ncols);
76 //  MatrixFpNonRed();
mySwapRows(long r1,long r2)77   void mySwapRows(long r1, long r2) {CoCoA_ASSERT(0 <= r1 && r1 < myRows && 0 <= r2 && r2 < myRows); if (r1 != r2) std::swap(myM[r1],myM[r2]);} ;
78   SmallFpImpl::NonRedValue& operator()(long r, long c);
79   const SmallFpImpl::NonRedValue& operator()(long r, long c) const;
80 //  friend void SetEntry(MatrixFp& M, long r, long c, SmallFpImpl::value x);
81 
82 private:
83   long myRows;
84   long myCols;
85   std::vector< std::vector< SmallFpImpl::NonRedValue > > myM;
86 };
87 
MatrixFpNonRed(long nrows,long ncols)88 MatrixFpNonRed::MatrixFpNonRed(long nrows, long ncols):
89     myRows(nrows),  // clang says unused???
90     myCols(ncols),  // clang says unused???
91     myM(nrows, std::vector<SmallFpImpl::NonRedValue>(ncols))
92 {}
93 
operator()94   inline SmallFpImpl::NonRedValue& MatrixFpNonRed::operator()(long r, long c) { return myM[r][c]; }
operator()95   inline const SmallFpImpl::NonRedValue& MatrixFpNonRed::operator()(long r, long c) const { return myM[r][c]; }
96 
97 
MulByTr(const MatrixFp & M1,const MatrixFp & trM2)98   MatrixFp MulByTr(const MatrixFp& M1, const MatrixFp& trM2)
99   {
100     CoCoA_ASSERT(ModPArith(M1) == ModPArith(trM2));
101     CoCoA_ASSERT(NumCols(M1) == NumCols(trM2)); // YES!!! I do mean NumCols(trM2)
102     const SmallFpImpl& ModP = ModPArith(M1);
103     MatrixFp ans(ModP, NumRows(M1), NumRows(trM2));
104     for (int i=0; i < NumRows(M1); ++i)
105       for (int j=0; j < NumRows(trM2); ++j)
106       {
107         SmallFpImpl::NonRedValue sum = zero(SmallFp);
108         for (int k=0; k < NumCols(M1); ++k)
109           sum += M1(i,k)*trM2(j,k); // BUG add overflow check!
110         ans(i,j) = ModP.myNormalize(sum);
111       }
112     return ans;
113   }
114 
115 
116 class LinSolver
117 {
118 public:
119   LinSolver(/*const SmallFpImpl& Modp,*/ const MatrixFp& M, const MatrixFp& rhs);
120 //??  LinSolver(const matrix& M, const matrix& rhs);
121   friend long rk(LinSolver& LS);
122   friend const MatrixFp& soln(/*const*/ LinSolver& LS);
123 private:
124   void SolveByGauss();
125 private: // data members
126   long myNrows;
127   long myNcols;
128   long myRHScols;
129   long myRank; // -1 means not yet solved, non-neg is true value
130   const SmallFpImpl& myModP;
131   MatrixFpNonRed myM; // matrix and rhs concatenated
132   MatrixFp mySoln;
133   std::vector<bool> mySolnIsValid;
134   std::vector<long> myPivotRow;
135 };
136 
rk(LinSolver & LS)137  long rk(LinSolver& LS) { if (LS.myRank < 0) LS.SolveByGauss(); return LS.myRank; }
soln(LinSolver & LS)138 const MatrixFp& soln(/*const*/ LinSolver& LS) { if (LS.myRank < 0) LS.SolveByGauss(); return LS.mySoln;}
139 
LinSolver(const MatrixFp & M,const MatrixFp & rhs)140   LinSolver::LinSolver(/*const SmallFpImpl& Modp,*/ const MatrixFp& M, const MatrixFp& rhs):
141     myNrows(NumRows(M)),
142     myNcols(NumCols(M)),
143     myRHScols(NumCols(rhs)),
144     myRank(-1),
145     myModP(ModPArith(M)),
146     myM(myNrows, myNcols+myRHScols),
147     mySoln(myModP, myNrows, myRHScols),
148     mySolnIsValid(myRHScols), /// ??? init val
149     myPivotRow(myNcols)  ///??? init val?
150 {
151   CoCoA_ASSERT(NumRows(rhs) == myNrows);
152 //  CoCoA_ASSERT(NumRows(rhs) == myNrows);
153   // Fill myM
154   for (int i=0; i < myNrows; ++i)
155   {
156     for (int j=0; j < myNcols; ++j)
157     {
158       myM(i,j) = M(i,j);
159     }
160     for (int j=0; j < myRHScols; ++j)
161     {
162       myM(i, j+myNcols) = rhs(i,j);
163     }
164   }
165 }
166 
167 //int FFsolve(FFmat soln, int *shape, FFmat matrix, FFmat RHS)
168 //int LinSolveInPlace(vector<vector< SmallFpImpl::NonRedValue> >& M, int ncols)
SolveByGauss()169 void LinSolver::SolveByGauss()//const SmallFpImpl& Modp, MatrixFp& soln, const MatrixFp& M, const MatrixFp& rhs)
170 {
171   int ReducedRows = 0;
172   const int AllCols = myNcols+myRHScols;
173   const SmallFpImpl& Modp = myModP;
174   const long TimeToReduce = Modp.myMaxIters();
175   long count = 0;
176 //  vector<int> PivotRow(ncols);
177 //  pivot_row = (int*)MALLOC(ncols*sizeof(int));
178   // Do Gauss redn col-by-col from lhs
179   for (int i=0; i < myNcols; ++i)
180   {
181     bool ZeroCol = true;
182     SmallFpImpl::value pivot;
183     for (int j=ReducedRows; j < myNrows; ++j)
184     {
185       pivot = Modp.myNormalize(myM(j,i));
186       if (!IsZero(pivot)) { ZeroCol = false; break;}
187     }
188     if (ZeroCol)
189     {
190 ///      if (shape[i]) { FREE(pivot_row); return FFsolve_invalid; }
191       continue;
192     }
193 // What does next block do????
194     // if (!shape[i])
195     // {
196     //   shape[i] = 1;
197     //   result |= FFsolve_new_pivot;
198     //   for (k=i+1; k < ncols; ++k) shape[k] = 0;
199     // }
200     myPivotRow[ReducedRows] = i;
201     myM.mySwapRows(ReducedRows, i);
202     // swap = M[j]; M[j] = M[trows]; M[trows] = swap;
203     // swap = rhs[j]; rhs[j] = rhs[trows]; rhs[trows] = swap;
204 //    const SmallFpImpl::value invMii = Modp.myRecip(myM(ReducedRows,i));
205     const SmallFpImpl::value inv_pivot = Modp.myRecip(pivot);
206     for (int j=i; j < AllCols; ++j)
207       myM(ReducedRows,j) = Modp.myMul(inv_pivot, Modp.myNormalize(myM(ReducedRows,j)));
208 //    for (j=i; j < ncols; j++) M[trows][j] = FFmul(M[trows][j]%p, invMii);
209 //    for (j=0; j < r; j++) rhs[trows][j] = FFmul(rhs[trows][j]%p, invMii);
210 
211     // Now zero the whole column (except the pivot, of course!)
212     for (int j=0; j < myNrows; j++)
213     {
214       if (j == ReducedRows) continue;
215       SmallFpImpl::value Mji = Modp.myNormalize(myM(j,i));
216       if (IsZero(Mji)) continue;
217       Mji = Modp.myNegate(Mji);
218       for (int k=i+1; k < AllCols; ++k)
219         myM(j,k) += Mji*myM(ReducedRows,k);
220       // for (int k=0; k < r; k++) rhs[j][k] += Mji*rhs[trows][k];
221       // for (k=i+1; k < ncols; k++) M[j][k] += Mji*M[trows][k];
222       myM(j,i) = zero(SmallFp);
223     }
224     ++ReducedRows;
225     if (++count < TimeToReduce) continue;
226     count = 0;
227     for (int j=0; j < myNrows; ++j)
228     {
229       for (int k=i+1; k < AllCols; ++k)
230         myM(j,k) = Modp.myHalfNormalize(myM(j,k));
231       // for (k=0; k < r; k++)
232       //   if (rhs[j][k] >= shift) rhs[j][k] -= shift;
233       // for (k=i+1; k < ncols; k++)
234       //   if (M[j][k] >= shift) M[j][k] -= shift;
235     }
236   }
237 
238   for (int j=0; j < myRHScols; ++j)
239   {
240     /* If any coord beyond ReducedRows is non-zero, no solution exists. */
241     for (int i=ReducedRows; i < myNrows; ++i)
242     {
243       myM(i, j+myNcols) = Modp.myNormalize(myM(i,j));
244       if (!IsZero(Modp.myNormalize(myM(i,j)))) { mySolnIsValid[j] = false; break; }
245     }
246     // There is a soln, so put it into mySoln:
247     int k = ReducedRows-1;
248     for (int i=myNcols-1; i >= 0; --i)
249     {
250       if (k >= 0 && i == myPivotRow[k])
251       { mySoln(i,j) = Modp.myNormalize(myM(k, j + myNcols)); --k; }
252 //default value!!      else mySoln(i,j) = 0;
253     }
254   }
255 
256 }
257 
258 
LinSolveFp(ConstMatrixView M,ConstMatrixView rhs)259   matrix LinSolveFp(ConstMatrixView M, ConstMatrixView rhs)
260   {
261     ring Fp = RingOf(M);
262     // assume RingOf(rhs) == Fp
263 //    const int p = ConvertTo<long>(characteristic(Fp));
264 //    SmallFpImpl Modp(p);
265     const SmallFpImpl& ModP = ModularArith(Fp);
266 
267     const long rows = NumRows(M);
268     const long cols = NumCols(M);
269     const long rhs_cols  = NumCols(rhs);
270     MatrixFp Mcopy(ModP, rows, cols);
271     MatrixFp RHScopy(ModP, rows, rhs_cols);
272     for (int i=0; i < rows; ++i)
273       for (int j=0; j < cols; ++j)
274         Mcopy(i,j) = ModP.myReduce(ConvertTo<long>(M(i,j)));
275     for (int i=0; i < rows; ++i)
276       for (int j=0; j < rhs_cols; ++j)
277         RHScopy(i,j) = ModP.myReduce(ConvertTo<long>(rhs(i,j)));
278     LinSolver LS(/*Modp,*/ Mcopy, RHScopy);
279     const MatrixFp& Msoln = soln(LS);
280     matrix ans = NewDenseMat(Fp, rows, rhs_cols);
281     for (int i=0; i < rows; ++i)
282       for (int j=0; j < rhs_cols; ++j)
283         SetEntry(ans,i,j,  ModP.myExportNonNeg(Msoln(i,j)));
284     return ans;
285   }
286 
287 
LinKerFp(ConstMatrixView M_orig)288   matrix LinKerFp(ConstMatrixView M_orig)
289   {
290     ring Fp = RingOf(M_orig);
291     // assume RingOf(rhs) == Fp
292 //    const int p = ConvertTo<long>(characteristic(Fp));
293     const SmallFpImpl& ModP = ModularArith(Fp);
294 //    SmallFpImpl Modp(p);
295 
296     const long nrows = NumRows(M_orig);
297     const long ncols = NumCols(M_orig);
298     MatrixFpNonRed M(nrows, ncols);
299     for (int i=0; i < nrows; ++i)
300       for (int j=0; j < ncols; ++j)
301         M(i,j) = ModP.myReduce(ConvertTo<long>(M_orig(i,j)));
302 
303     vector<bool> c(nrows); // initially all false
304     vector<int> d(ncols);  // initially all zero
305     vector<SmallFpImpl::value> ThisRow(ncols);
306 
307     int KerDim = 0;
308     int IterCount = 0;
309 
310     for (int k=0; k < ncols; ++k)
311     {
312       for (int j=0; j < nrows; ++j)
313       {
314         ThisRow[j] = ModP.myNormalize(M(j,k));
315         M(j,k) = ThisRow[j];
316       }
317 
318       int j;
319       for (j=0; j < nrows; ++j)
320         if (!c[j] && !IsZero(ThisRow[j])) break;
321 
322       if (j == nrows) { ++KerDim; d[k] = -1; continue; }
323       // Mj = M+(ncols*j);
324       const SmallFpImpl::value q = ModP.myNegate(ModP.myRecip(ThisRow[j])); // surely non-zero
325       for (int i=k+1; i < ncols; i++)
326         M(j,i) = ModP.myMul(q, ModP.myNormalize(M(j,i)));
327 
328       for (int i=0; i < nrows; i++)
329       {
330         if (i == j) continue;
331         //Mi = M+(i*ncols);
332         const SmallFpImpl::value Mik = ModP.myNormalize(M(i,k));
333         if (IsZero(Mik)) continue;
334         M(i,k) = zero(SmallFp);
335         for (int s=k+1; s < ncols; s++)
336           M(i,s) += Mik*M(j,s);
337       /* for (s=k+1; s < ncols; s++) Mi[s] += Mik*Mj[s]; */
338       // {
339       //   FFelem *Mis = &Mi[k+1],
340       //          *Mjs = &Mj[k+1],
341       //          *last = &Mi[ncols-1];
342       //   while (Mis <= last) *(Mis++) += Mik * *(Mjs++);
343       // }
344       }
345       c[j] = true;
346       d[k] = j;
347       if (++IterCount < ModP.myMaxIters()) continue;
348       IterCount = 0;
349       for (int i=0; i < nrows; i++)
350       {
351 //        Mi = M+(i*ncols);
352         for (int j=k+1; j < ncols; j++)
353           M(i,j) = ModP.myHalfNormalize(M(i,j));
354 //        if (Mi[j] >= shift) Mi[j] -= shift;
355       }
356   }
357 
358 
359 //    MatrixFp ans(KerDim, ncols);
360     matrix ans = NewDenseMat(Fp, KerDim, ncols);
361 //  ans = (FFelem*)MALLOC(KerDim*ncols*sizeof(FFelem));
362   int s = 0;
363 //  x = ans;
364   for (int k=0; k < ncols; ++k)
365   {
366     if (d[k] != -1) continue;
367     for (int i=0; i < ncols; ++i)
368     {
369       if (d[i] == -1) SetEntry(ans,s,i, (i == k));//ans(k,i) = ModP.myReduce(i == k);
370       else SetEntry(ans,s,i, ModP.myExport(ModP.myNormalize(M(d[i],k))));// ans(k,i) = ModP.myNormalize(M(d[i],k));
371     }
372 //    x += ncols;
373     ++s;
374   }
375 //  FREE(c);
376 //  FREE(d);
377 
378 //  *B = ans;
379   return ans;
380 
381   }
382 
383 //   class LinDepFp
384 //   {
385 //   public:
386 //     LinDepFp(const SmallFpImpl& ModP, long dim);
387 //     bool myAppendVec(std::vector<SmallFpImpl::value>& v);
388 // //    bool IsLinDep() const { return };
389 //     const std::vector<SmallFpImpl::value>& myLinReln() { CoCoA_ASSERT(); return myLinRelnVec;}
390 
391 //   private:
392 //     const SmallFpImpl& myArith;
393 //     long myDim;
394 //     int myNumVecs;
395 //     std::vector< std::vector<SmallFpImpl::value> > myM;
396 //     std::vector<SmallFpImpl::NonRedValue> myRow;
397 // //    std::vector<std::vector<RingElem> > myRowRepr;
398 //     std::vector<int> myColIndices;
399 //     std::vector<SmallFpImpl::value> myLinRelnVec;
400 
401 //   };
402 
LinDepFp(const SmallFpImpl & ModP,long dim)403   LinDepFp::LinDepFp(const SmallFpImpl& ModP, long dim):
404       myArith(ModP),
405       myDim(dim),
406       myNumVecs(0),
407       myColIndices(dim,-1)
408     {
409 //      if (!IsField(K)) CoCoA_ERROR(ERR::NotField, "LinDepMill ctor");
410       if (dim <= 0) CoCoA_ERROR(ERR::NotPositive, "LinDepFp ctor");
411       myM.reserve(dim);
412       myRow.reserve(2*dim);
413       myLinRelnVec.reserve(dim);
414     }
415 
myAppendVec(std::vector<SmallFpImpl::value> & v)416   bool LinDepFp::myAppendVec(std::vector<SmallFpImpl::value>& v)
417   {
418     myLinRelnVec.clear(); // empty unless we find a lin reln
419 //    VerboseLog VERBOSE("LinDepFp::myAppendVec");
420 //    VERBOSE(10) << "Recvd vec " << v << endl;
421     CoCoA_ASSERT(len(v) == myDim);
422     for (int i=0; i < myDim; ++i)
423     {
424       myRow[i] = v[i];
425       myRow[i+myDim] = (i == myNumVecs)?one(SmallFp):zero(SmallFp);
426     }
427 
428     int rightmost = 0;
429     const long p = myArith.myModulus();
430     const long SizeLimit = std::numeric_limits<SmallFpImpl::repr_t>::max()/p; // integer division
431     const long HalfWay_up = (1+SizeLimit)/2; // integer division!
432     long SizeEst = 1;  // values in myRow are less than p*SizeEst
433     for (int c=0; c < myDim; ++c)
434     {
435       const SmallFpImpl::value entry = myArith.myNormalize(myRow[c]);
436       if (IsZero(entry)) { myRow[c] = entry; continue; }
437       if (myColIndices[c] < 0)
438       {
439         // We have a new reducer (and surely no lin dep)
440         vector<SmallFpImpl::value> NewRow(2*myDim);
441         const SmallFpImpl::value inv = myArith.myRecip(entry);
442         NewRow[c] = one(SmallFp);
443         const int END = myNumVecs+myDim;
444         for (int j=c+1; j <= END; ++j)
445           NewRow[j] = myArith.myMul(inv, myArith.myNormalize(myRow[j]));
446 //        VERBOSE(10) << "New reducer for col " << c << "  is " << NewRow << endl;
447         myM.push_back(NewRow);
448         myColIndices[c] = myNumVecs;
449         ++myNumVecs;
450         return false; // no lin dep
451       }
452 
453       myRow[c] = zero(SmallFp);
454       const int reducer = myColIndices[c];
455       if (reducer > rightmost) rightmost = reducer;
456 //      VERBOSE(10) << "Row redn for col " << c << "  by vec " << reducer << " " << myM[reducer] << endl;
457       const SmallFpImpl::value scale = myArith.myNegate(entry);
458 
459       SizeEst += myArith.myExportNonNeg(scale);
460       if (SizeEst > SizeLimit)
461       {
462         // Overflow is possible, so to avoid it we half-reduce entries in myRow
463         const int END2 = rightmost + myDim;
464         for (int j=c+1; j < END2; ++j)
465           myRow[j] = myArith.myHalfNormalize(myRow[j]);
466 
467         SizeEst = HalfWay_up + myArith.myExportNonNeg(scale);
468       }
469 
470       const vector<SmallFpImpl::value>& RedRow = myM[reducer];
471       const int END = reducer+myDim; // all later coords are zero
472       for (int j=c+1; j <= END; ++j)
473         myRow[j] += scale*RedRow[j];
474 
475     }
476     // We have a lin dep; copy it into myLinRelnVec
477     myLinRelnVec.resize(1+myNumVecs);
478     myLinRelnVec[myNumVecs] = one(SmallFp);
479     for (int j=0; j < myNumVecs; ++j)
480       myLinRelnVec[j] = myArith.myNormalize(myRow[j+myDim]);
481 
482     return true;
483   }
484 
485 
SignatureOfPerm(const vector<int> & perm)486 int SignatureOfPerm(const vector<int>& perm)
487 {
488   const int n = len(perm);
489   vector<bool> visited(n); // initially false
490   int sign = 1;
491   for (int i=0; i < n; ++i)
492   {
493     if (visited[i]) continue;
494     int CycleLen = 0;
495     int curr = i;
496     while (!visited[curr])
497     {
498       visited[curr] = true;
499       curr = perm[curr];
500       ++CycleLen;
501     }
502     if ((CycleLen&1) == 0) sign = -sign;
503   }
504   return sign;
505 }
506 
507 
det(MatrixFp & M)508   long/*SmallFpImpl::value*/ det(MatrixFp& M)
509 {
510   const SmallFpImpl& ModP = ModPArith(M);
511     const long p = ModP.myModulus();
512     const long SizeLimit = std::numeric_limits<SmallFpImpl::repr_t>::max()/p; // integer division
513     const long HalfWay_up = (1+SizeLimit)/2; // integer division!
514     long SizeEst = 1;  // values in myRow are less than p*SizeEst
515     const int n = NumRows(M);
516     vector<int> ReducerIndex(n,-1); // -1 means no reducer
517     vector<SmallFpImpl::NonRedValue> CurrRow(n);
518     SmallFpImpl::value DiagProd = one(SmallFp);
519     for (int row=0; row < n; ++row)
520     {
521       //CurrRow = M[row];
522       for (int col=0; col < n; ++col)
523         CurrRow[col] = M(row,col);
524       bool HaveNewReducerRow = false;
525       for (int col=0; col < n; ++col)
526       {
527         const SmallFpImpl::value entry = ModP.myNormalize(CurrRow[col]);
528         if (IsZero(entry)) { CurrRow[col] = entry; continue; }
529         const int reducer = ReducerIndex[col];
530         if (reducer >= 0)
531         {
532         CurrRow[col] = zero(SmallFp);
533 //      VERBOSE(10) << "Row redn for col " << c << "  by vec " << reducer << " " << myM[reducer] << endl;
534         const SmallFpImpl::value scale = ModP.myNegate(entry);
535 
536         SizeEst += ModP.myExportNonNeg(scale);
537         // if (SizeEst <= SizeLimit)
538         // {
539         // const vector<SmallFpImpl::value>& RedRow = M[reducer];
540         // for (int j=col+1; j < n; ++j)
541         //   CurrRow[j] += scale*RedRow[j];
542         // // SmallFpImpl::NonRedValue* Rj = &CurrRow[col+1];
543         // // SmallFpImpl::NonRedValue* END = &CurrRow[n];
544         // // SmallFpImpl::value* mij = &M[reducer][col+1];
545         // // while (Rj != END)
546         // //   *Rj++ = scale * *mij++;
547         // continue;
548         // }
549         if (SizeEst > SizeLimit)
550         {
551           // Overflow is possible, so to avoid it we half-reduce entries in myRow
552           for (int j=col+1; j < n; ++j)
553             CurrRow[j] = ModP.myHalfNormalize(CurrRow[j]);
554 
555           SizeEst = HalfWay_up + ModP.myExportNonNeg(scale);
556         }
557 
558         const vector<SmallFpImpl::value>& RedRow = M[reducer];
559         for (int j=col+1; j < n; ++j)
560           CurrRow[j] += scale*RedRow[j];
561         continue;
562         }
563         if (ReducerIndex[col] < 0)
564         {
565           // We have a new reducer
566           HaveNewReducerRow = true;
567           const SmallFpImpl::value inv = ModP.myRecip(entry);
568           DiagProd = ModP.myMul(DiagProd, entry);
569 //          vector<SmallFpImpl::value>& Mrow = M[row];
570           for (int j=0; j < col; ++j)
571             M(row,j)/*Mrow[j]*/ = zero(SmallFp);
572           M(row,col)/*Mrow[col]*/ = one(SmallFp);
573           for (int j=col+1; j < n; ++j)
574             M(row,j)/*Mrow[j]*/ = ModP.myMul(inv, ModP.myNormalize(CurrRow[j]));
575 //        VERBOSE(10) << "New reducer for col " << c << "  is " << NewRow << endl;
576           ReducerIndex[col] = row;
577           break;
578         }
579 
580 
581       }
582       if (!HaveNewReducerRow) return 0; //zero(SmallFp);
583     }
584     if (SignatureOfPerm(ReducerIndex) == 1)
585       return ModP.myExportNonNeg(DiagProd);
586     return ModP.myModulus() - ModP.myExportNonNeg(DiagProd);
587 }
588 
589 
RowRednFp(const SmallFpImpl & ModP,long dim)590   RowRednFp::RowRednFp(const SmallFpImpl& ModP, long dim):
591       myArith(ModP),
592       myDim(dim),
593       myNumVecs(0),
594       myDiagProd(one(SmallFp)),
595       myColIndices(dim,-1)
596     {
597 //      if (!IsField(K)) CoCoA_ERROR(ERR::NotField, "LinDepMill ctor");
598       if (dim <= 0) CoCoA_ERROR(ERR::NotPositive, "RowRednFp ctor");
599       myM.reserve(dim);
600       myRow.reserve(2*dim);
601 //      myLinRelnVec.reserve(dim);
602     }
603 
myAppendVec(std::vector<SmallFpImpl::value> & v)604   void RowRednFp::myAppendVec(std::vector<SmallFpImpl::value>& v)
605   {
606 //    myLinRelnVec.clear(); // empty unless we find a lin reln
607 //    VerboseLog VERBOSE("RowRednFp::myAppendVec");
608 //    VERBOSE(10) << "Recvd vec " << v << endl;
609     CoCoA_ASSERT(len(v) == myDim);
610     for (int i=0; i < myDim; ++i)
611     {
612       myRow[i] = v[i];
613 //      myRow[i+myDim] = (i == myNumVecs)?one(SmallFp):zero(SmallFp);
614     }
615 
616 //    int rightmost = 0;
617     const long p = myArith.myModulus();
618     const long SizeLimit = std::numeric_limits<SmallFpImpl::repr_t>::max()/p; // integer division
619     const long HalfWay_up = (1+SizeLimit)/2; // integer division!
620     long SizeEst = 1;  // values in myRow are less than p*SizeEst
621     for (int c=0; c < myDim; ++c)
622     {
623       const SmallFpImpl::value entry = myArith.myNormalize(myRow[c]);
624       if (IsZero(entry)) { myRow[c] = entry; continue; }
625       if (myColIndices[c] < 0)
626       {
627         // We have a new reducer
628         vector<SmallFpImpl::value> NewRow(myDim);
629         const SmallFpImpl::value inv = myArith.myRecip(entry);
630         myDiagProd = myArith.myMul(myDiagProd, entry);
631         NewRow[c] = one(SmallFp);
632 //        const int END = myNumVecs+myDim;
633         for (int j=c+1; j < myDim; ++j)
634           NewRow[j] = myArith.myMul(inv, myArith.myNormalize(myRow[j]));
635 //        VERBOSE(10) << "New reducer for col " << c << "  is " << NewRow << endl;
636         myM.push_back(NewRow);
637         myColIndices[c] = myNumVecs;
638         ++myNumVecs;
639         return;
640       }
641 
642       myRow[c] = zero(SmallFp);
643       const int reducer = myColIndices[c];
644 //      if (reducer > rightmost) rightmost = reducer;
645 //      VERBOSE(10) << "Row redn for col " << c << "  by vec " << reducer << " " << myM[reducer] << endl;
646       const SmallFpImpl::value scale = myArith.myNegate(entry);
647 
648       SizeEst += myArith.myExportNonNeg(scale);
649       if (SizeEst > SizeLimit)
650       {
651         // Overflow is possible, so to avoid it we half-reduce entries in myRow
652 //        const int END2 = rightmost + myDim;
653         for (int j=c+1; j < myDim; ++j)
654           myRow[j] = myArith.myHalfNormalize(myRow[j]);
655 
656         SizeEst = HalfWay_up + myArith.myExportNonNeg(scale);
657       }
658 
659       const vector<SmallFpImpl::value>& RedRow = myM[reducer];
660 //      const int END = reducer+myDim; // all later coords are zero
661 
662       for (int j=c+1; j < myDim; ++j)
663         myRow[j] += scale*RedRow[j];
664 
665       // SmallFpImpl::NonRedValue* dest = &myRow[c+1];
666       // const SmallFpImpl::value* reduce = &RedRow[c+1];
667       // SmallFpImpl::NonRedValue* const END = &myRow[myDim+1];
668       // while (dest != END)
669       //   *dest++ += scale * *reduce++;
670 
671     }
672     // // We have a lin dep; copy it into myLinRelnVec
673     // myLinRelnVec.resize(1+myNumVecs);
674     // myLinRelnVec[myNumVecs] = one(SmallFp);
675     // for (int j=0; j < myNumVecs; ++j)
676     //   myLinRelnVec[j] = myArith.myNormalize(myRow[j+myDim]);
677 
678     return;
679   }
680 
myRank()681 long RowRednFp::myRank() const
682 {
683   return myNumVecs;
684 }
685 
myDet()686 SmallFpImpl::value RowRednFp::myDet() const
687 {
688   CoCoA_ASSERT(myNumVecs == myDim); // BUG, should count number of calls!!!
689   if (myNumVecs < myDim) return zero(SmallFp);
690   if (SignatureOfPerm(myColIndices) == 1)
691   return myDiagProd; // sign???
692   else
693     return myArith.myNegate(myDiagProd);
694 }
695 
696 } // end of namespace CoCoA
697