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