oILAB
Loading...
Searching...
No Matches
IntegerMath.h
Go to the documentation of this file.
1/* This file is part of gbLAB.
2 *
3 * gbLAB is distributed without any warranty under the MIT License.
4 */
5
6
7#ifndef gbLAB_IntegerMath_h_
8#define gbLAB_IntegerMath_h_
9
10#include <numeric>
11#include <Eigen/Dense>
12#include <vector>
13#include <iostream>
14#include <algorithm>
15#include <deque>
16
17
18
19namespace gbLAB
20{
21 template <typename IntScalarType>
23 {
24 inline static IntScalarType positive_modulo(IntScalarType i, IntScalarType n) {
25 return (i % n + n) % n;
26 }
27
28 static IntScalarType sgn(const IntScalarType& a)
29 {
30 return a<0? -1 : 1;
31 }
32
33 static IntScalarType gcd(const IntScalarType &a, const IntScalarType &b)
34 {
35 const IntScalarType absA(abs(a));
36 const IntScalarType absB(abs(b));
37 return absB > 0 ? gcd(absB, absA % absB) : (absA > 0 ? absA : 1);
38 }
39
40 template<typename T>
41 static IntScalarType gcd(const Eigen::MatrixBase<T>& a)
42 {
43 switch (a.size())
44 {
45 case 0:
46 {
47 throw std::runtime_error("gcd: array size is zero\n");
48 return 0;
49 break;
50 }
51
52 case 1:
53 {
54 return a(0);
55 break;
56 }
57
58 case 2:
59 {
60 return gcd(a(0),a(1));
61 break;
62 }
63
64 default:
65 {
66 IntScalarType temp(a(0));
67 for(long k=1;k<a.size();++k)
68 {
69 if (temp==0 && a(k)==0 && k!=a.size()-1)
70 temp= 0;
71 else
72 temp=gcd(temp,a(k));
73 }
74 return temp;
75 break;
76 }
77 }
78 }
79
80 static IntScalarType lcm(const IntScalarType &a, const IntScalarType &b)
81 {
82 return a * b / gcd(a, b);
83 }
84
85 template<typename T>
86 static IntScalarType lcm(const Eigen::MatrixBase<T>& a)
87 {
88 switch (a.size())
89 {
90 case 0:
91 {
92 throw std::runtime_error("lcm: array size is zero\n");
93 return 0;
94 break;
95 }
96
97 case 1:
98 {
99 return a(0);
100 break;
101 }
102
103 case 2:
104 {
105 return lcm(a(0),a(1));
106 break;
107 }
108
109 default:
110 {
111 IntScalarType temp(a(0));
112 for(long k=1;k<a.size();++k)
113 {
114 if (temp==0 && a(k)==0 && k!=a.size()-1)
115 temp= 0;
116 else
117 temp=lcm(temp,a(k));
118 }
119 return temp;
120 break;
121 }
122 }
123
124 }
125
126 static Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic>
127 integerGramSchmidt(const Eigen::Vector<IntScalarType,Eigen::Dynamic>& a)
128 {
129 int dim= a.size();
130 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> matrixA;
131 Eigen::Matrix<IntScalarType,Eigen::Dynamic,1> tempx;
132 matrixA.resize(1,1);
133 matrixA << a(0);
134
135 for(int i=0;i<dim-1;i++) {
136 matrixA.conservativeResize(i + 1, i + 2);
137 // zero the new column
138 matrixA.col(i + 1) = Eigen::Vector<IntScalarType, Eigen::Dynamic>::Zero(i + 1);
139
140 matrixA(0, i + 1) = a(i + 1);
141 tempx.conservativeResize(i + 2, 1);
142
143 // fill the new row with tempx
144 if (i != 0)
145 {
146 tempx(i + 1) = 0;
147 matrixA.row(i) = tempx;
148 }
149
150 Eigen::Vector<IntScalarType,Eigen::Dynamic> det=
151 Eigen::Vector<IntScalarType,Eigen::Dynamic>::Zero(i+2);
152 for(int j=0;j<i+2; j++)
153 {
154 Eigen::MatrixXd minor= Eigen::MatrixXd::Zero(i+1,i+1);
155 std::vector<IntScalarType> indicesToKeep(i+2);
156 std::iota(std::begin(indicesToKeep),std::end(indicesToKeep),0);
157 indicesToKeep.erase(std::remove(indicesToKeep.begin(),indicesToKeep.end(),j),indicesToKeep.end());
158
159 Eigen::Vector<IntScalarType,Eigen::Dynamic> indicesToKeepVector;
160 indicesToKeepVector= Eigen::Map<Eigen::Vector<IntScalarType,Eigen::Dynamic>>(indicesToKeep.data(),i+1);
161 minor= matrixA(Eigen::indexing::all, indicesToKeepVector).template cast<double>();
162 det(j)= round(minor.determinant());
163 }
164
165 IntScalarType e= gcd(det);
166
167
168 if (det == Eigen::Vector<IntScalarType,Eigen::Dynamic>::Zero(i+2))
169 {
170 tempx= Eigen::Vector<IntScalarType,Eigen::Dynamic>::Zero(i+2);
171 tempx(i)= 1;
172 continue;
173 }
174
175 for (int j = 0; j < i + 2; j++)
176 tempx(j) = pow(-1, j + 1) * det(j) / e;
177 }
178 matrixA.conservativeResize(dim,dim);
179 matrixA.row(dim-1)= tempx;
180
181 return matrixA.block(1,0,dim-1,dim);
182 }
183
184 // Finds such x and y, that a x + b y = gcd(a, b)
185 //https://github.com/ADJA/algos/blob/master/NumberTheory/DiophantineEquation.cpp
186 static IntScalarType extended_gcd(IntScalarType a, IntScalarType b, IntScalarType &x, IntScalarType &y)
187 {
188 if (b == 0)
189 {
190 x = 1;
191 y = 0;
192 return a;
193 }
194 IntScalarType x1, y1;
195 IntScalarType g = extended_gcd(b, a % b, x1, y1);
196 x = y1;
197 y = x1 - (a / b) * y1;
198 return g;
199 }
200
201 // Solves equation a x + b y = c, writes answer to x and y
202 static void solveDiophantine2vars(IntScalarType a, IntScalarType b, IntScalarType c, IntScalarType &x, IntScalarType &y)
203 {
204
205 IntScalarType g = extended_gcd(a, b, x, y);
206
207 if (c % g != 0)
208 {
209 std::cout << a << " " << b << " " << c << " " << g << std::endl;
210 puts("Impossible");
211 exit(0);
212 }
213
214 c /= g;
215
216 x = x * c ;
217 y = y * c ;
218 }
219
220 // Find u such that a_1 u_1 + a_2 u_2 + .... + a_n u_n = 1
221 // Method 0:
222 // "A fast algorithm to find reduced hyperplane unit cells and solve N-dimensional Bézout's identities."
223 // Acta Crystallographica Section A: Foundations and Advances 77.5 (2021).
224 // template<typename ArrayType>
225 // static ArrayType solveBezout(const ArrayType& a)
226 template<typename T>
227 static Eigen::Vector<IntScalarType,Eigen::Dynamic> solveBezout(const Eigen::MatrixBase<T>& a)
228 {
229 int n= a.size();
230 if (n<2) throw std::runtime_error("the size of arrays should be at least two");
231 if (abs(IntegerMath<IntScalarType>::gcd(a)) != 1 || a.isZero())
232 throw std::runtime_error("No solution since the gcd is not 1 or -1.");
233
234 Eigen::Vector<IntScalarType,Eigen::Dynamic> u(n);
235
236 IntScalarType p,q;
237 IntScalarType g12= extended_gcd(a(0),a(1),p,q); // g12 - gcd of a(0) and a(1)
238
239 // form the n-1 sized vector na= {g,a2,...,a_{n-1}}
240 Eigen::Vector<IntScalarType,Eigen::Dynamic> na(n-1);
241 na(0)= g12; na(Eigen::seq(1,n-2))= a(Eigen::seq(2,n-1));
242
243 Eigen::Vector<IntScalarType,Eigen::Dynamic> k(n-1);
244 if (n==2)
245 {
246 IntScalarType g= extended_gcd(a(0),a(1),u(0),u(1));
247 if (g<0) u= -u;
248 return u;
249 }
250 else
251 {
252 k = solveBezout(na);
253 // {p*k0,q*k0,k1,..,k_{n-2}}
254 u(0) = p * k(0);
255 u(1) = q * k(0);
256 u(Eigen::seq(2, n - 1)) = k(Eigen::seq(1, n - 2));
257 return u;
258 }
259 }
260
261 template<typename T>
262 static Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> ccum(const Eigen::MatrixBase<T>& qin)
263 //static Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic>
264 //ccum(const Eigen::Vector<IntScalarType,Eigen::Dynamic>& qin)
265 {
266 int n= qin.size();
267 Eigen::Vector<IntScalarType,Eigen::Dynamic> q(n);
268 q= qin;
269
270 try
271 {
272 if (n==1)
273 throw std::runtime_error("Size of the input matrix for CCUM should be >1.");
274 if (abs(gcd(qin)) != 1)
275 throw std::runtime_error("The absolute gcd of the input array is not 1.");
276 }
277 catch(std::runtime_error& e)
278 {
279 std::cerr << e.what();
280 }
281
282 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> Q=
283 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic>::Identity(n,n);
284
285 // first ensure that q(0) is non-zero; otherwise swap
286 bool swapFirst= false;
287 int swapFirstElementWith;
288 if (q(0)==0)
289 {
290 int elementIndex= -1;
291 for (auto element : q)
292 {
293 elementIndex++;
294 if (element !=0) {
295 // swap
296 IntScalarType temp= q(0);
297 q(0)= element;
298 q(elementIndex)= temp;
299 swapFirst = true;
300 swapFirstElementWith= elementIndex;
301 break;
302 }
303 }
304 }
305 Q.col(0)= q;
306
307 int elementIndex= -1;
308 IntScalarType y;
309 // ensure q(0) and q(1) are co-prime; otherwise swap
310 for (const IntScalarType& element : q)
311 {
312 elementIndex++;
313 if (elementIndex == 0) continue;
314 if (!(q(0)==0 && element==0))
315 y= gcd(q(0),element);
316 else
317 continue;
318 if (abs(y)==1)
319 {
320 // swap rows
321 int temp= Q(1,0);
322 Q(1,0)= Q(elementIndex,0);
323 Q(elementIndex,0)= temp;
324
325 IntScalarType u,v;
327 //Q(0,elementIndex)=-v; Q(elementIndex,elementIndex)= u;
328 Q(0,1)=-v; Q(1,1)= u;
329 // unswap
330 Q.row(1).swap(Q.row(elementIndex));
331
332 if(swapFirst) Q.row(0).swap(Q.row(swapFirstElementWith));
333 return Q;
334 }
335 }
336
337 // At this point, q(0) is not co-prime with any other elements of q
338 if (!(q(0) ==0 && q(1) == 0))
339 y= gcd(q(0),q(1));
340 else
341 y= 0;
342 IntScalarType u,v;
344
345 IntScalarType alpha,beta;
347 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> M=
348 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic>::Identity(n,n);
349 M.block(0,0,2,2) << u, v,
350 -beta, alpha;
351
352 Eigen::Vector<IntScalarType,Eigen::Dynamic> t;
353 t= M*q;
354 elementIndex= -1;
355 int swappedElementIndex;
356 bool swapped= false;
357 for (IntScalarType& element : t)
358 {
359 elementIndex++;
360 if (elementIndex<2) continue;
361 // Find a element in {t(2),...,} that y:=gcd(q(0), q(1)) does not divide
362 // We are guaranteed to find such an element since gcd(q)=1
363 if (element%y != 0)
364 {
365 // swap the found element with t(1)
366 int temp= t(elementIndex);
367 t(elementIndex)= t(1);
368 t(1)= temp;
369 swappedElementIndex= elementIndex;
370 swapped= true;
371 break;
372 }
373 assert(elementIndex!=t.size());
374 }
375
376 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> tempMatrix(n,n) ;
377 tempMatrix= ccum(t);
378
379 // inv(P)*ccum(t)
380 // swap rows 0 and swappedElementIndex
381 if (swapped) tempMatrix.row(1).swap(tempMatrix.row(swappedElementIndex));
382
383 // inv(M)*inv(P)*ccum(t)
384 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> invM=
385 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic>::Identity(n,n);
386 invM.block(0,0,2,2) << alpha, -v,
387 beta, u;
388 Eigen::Matrix<IntScalarType,Eigen::Dynamic,Eigen::Dynamic> output(n,n);
389 output= invM*tempMatrix;
390 if(swapFirst) output.row(0).swap(output.row(swapFirstElementWith));
391 return output;
392 }
393
394
395 static std::deque<std::vector<IntScalarType>> comb(const int& n, const int& k)
396 {
397 std::deque<std::vector<IntScalarType>> output;
398 std::string bitmask(k, 1); // K leading 1's
399 bitmask.resize(n, 0); // N-K trailing 0's
400
401 // print integers and permute bitmask
402 do {
403 std::vector<IntScalarType> kCombination(k);
404 int arrIndex= 0;
405
406 for (int i = 0; i < n; ++i) // [0..N-1] integers
407 {
408 if (bitmask[i])
409 {
410 kCombination[arrIndex]= (IntScalarType) i;
411 arrIndex++;
412 }
413 }
414 assert(arrIndex==k);
415 output.push_back(kCombination);
416 } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
417 return output;
418 }
419
420 };
421
422 template <typename IntScalarType, int dim>
423 class MatrixDimIExt : public Eigen::Matrix<IntScalarType,dim,dim>
424 {
425 public:
426 static Eigen::Matrix<IntScalarType,dim,dim> adjoint(const Eigen::Matrix<IntScalarType,dim,dim>& input)
427 {
428 Eigen::Matrix<IntScalarType,dim,dim> output;
429 for (int i=0; i<dim; i++)
430 {
431 // remove i-th row
432 Eigen::Matrix<IntScalarType,dim-1,dim> temp1 ;
433 temp1 << input(Eigen::seq(0,i-1),Eigen::all),
434 input(Eigen::seq(i+1,dim-1),Eigen::all);
435 for (int j=0; j<dim; j++)
436 {
437 // remove jth column
438 Eigen::Matrix<IntScalarType,dim-1,dim-1> temp2 ;
439 temp2 << temp1(Eigen::all,Eigen::seq(0,j-1)), temp1(Eigen::all,Eigen::seq(j+1,dim-1));
440
441 output(i,j)=(IntScalarType) std::pow(-1,i+j+2)*temp2.template cast<double>().determinant();
442 }
443 }
444
445 return output.transpose();
446 }
447 };
448
449 template<typename IntScalarType>
450 class MatrixDimIExt<IntScalarType,1> : public Eigen::Matrix<IntScalarType,1,1>
451 {
452 public:
453 static Eigen::Matrix<IntScalarType,1,1> adjoint(const Eigen::Matrix<IntScalarType,1,1>& input)
454 {
455 Eigen::Matrix<IntScalarType,1,1> output;
456 output << 1;
457 return output;
458 }
459
460 };
461}
462#endif
static Eigen::Matrix< IntScalarType, 1, 1 > adjoint(const Eigen::Matrix< IntScalarType, 1, 1 > &input)
static Eigen::Matrix< IntScalarType, dim, dim > adjoint(const Eigen::Matrix< IntScalarType, dim, dim > &input)
static IntScalarType gcd(const Eigen::MatrixBase< T > &a)
Definition IntegerMath.h:41
static Eigen::Matrix< IntScalarType, Eigen::Dynamic, Eigen::Dynamic > ccum(const Eigen::MatrixBase< T > &qin)
static IntScalarType lcm(const Eigen::MatrixBase< T > &a)
Definition IntegerMath.h:86
static IntScalarType sgn(const IntScalarType &a)
Definition IntegerMath.h:28
static std::deque< std::vector< IntScalarType > > comb(const int &n, const int &k)
static IntScalarType gcd(const IntScalarType &a, const IntScalarType &b)
Definition IntegerMath.h:33
static IntScalarType positive_modulo(IntScalarType i, IntScalarType n)
Definition IntegerMath.h:24
static Eigen::Vector< IntScalarType, Eigen::Dynamic > solveBezout(const Eigen::MatrixBase< T > &a)
static IntScalarType lcm(const IntScalarType &a, const IntScalarType &b)
Definition IntegerMath.h:80
static Eigen::Matrix< IntScalarType, Eigen::Dynamic, Eigen::Dynamic > integerGramSchmidt(const Eigen::Vector< IntScalarType, Eigen::Dynamic > &a)
static void solveDiophantine2vars(IntScalarType a, IntScalarType b, IntScalarType c, IntScalarType &x, IntScalarType &y)
static IntScalarType extended_gcd(IntScalarType a, IntScalarType b, IntScalarType &x, IntScalarType &y)