polylib 7.01
matrix.c
Go to the documentation of this file.
1/* matrix.c
2 COPYRIGHT
3 Both this software and its documentation are
4
5 Copyright 1993 by IRISA /Universite de Rennes I -
6 France, Copyright 1995,1996 by BYU, Provo, Utah
7 all rights reserved.
8
9*/
10
11#include <ctype.h>
12#include <polylib/polylib.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17#ifdef mac_os
18#define abs __abs
19#endif
20
21/*
22 * Allocate space for matrix dimensioned by 'NbRows X NbColumns'.
23 */
24Matrix *Matrix_Alloc(unsigned NbRows, unsigned NbColumns) {
25
26 Matrix *Mat;
27 Value *p, **q;
28 int i;
29
30 Mat = (Matrix *)malloc(sizeof(Matrix));
31 if (!Mat) {
32 errormsg1("Matrix_Alloc", "outofmem", "out of memory space");
33 return 0;
34 }
35 Mat->NbRows = NbRows;
36 Mat->NbColumns = NbColumns;
37 if (NbRows == 0 || NbColumns == 0) {
38 Mat->p = (Value **)0;
39 Mat->p_Init = (Value *)0;
40 Mat->p_Init_size = 0;
41 } else {
42 q = (Value **)malloc(NbRows * sizeof(*q));
43 if (!q) {
44 free(Mat);
45 errormsg1("Matrix_Alloc", "outofmem", "out of memory space");
46 return 0;
47 }
48 p = value_alloc(NbRows * NbColumns, &Mat->p_Init_size);
49 if (!p) {
50 free(q);
51 free(Mat);
52 errormsg1("Matrix_Alloc", "outofmem", "out of memory space");
53 return 0;
54 }
55 Mat->p = q;
56 Mat->p_Init = p;
57 for (i = 0; i < NbRows; i++) {
58 *q++ = p;
59 p += NbColumns;
60 }
61 }
62
63 return Mat;
64} /* Matrix_Alloc */
65
66/*
67 * Free the memory space occupied by Matrix 'Mat'
68 */
69void Matrix_Free(Matrix *Mat) {
70 if(Mat==NULL)
71 return;
72 if (Mat->p_Init)
73 value_free(Mat->p_Init, Mat->p_Init_size);
74
75 if (Mat->p)
76 free(Mat->p);
77 free(Mat);
78
79} /* Matrix_Free */
80
81/*
82 * Increase number of lines of matrix 'Mat', in place.
83 */
84void Matrix_Extend(Matrix *Mat, unsigned NbRows) {
85 Value *p, **q;
86 int i;
87
88 q = (Value **)realloc(Mat->p, NbRows * sizeof(*q));
89 if (!q) {
90 errormsg1("Matrix_Extend", "outofmem", "out of memory space");
91 return;
92 }
93 Mat->p = q;
94 if (Mat->p_Init_size < NbRows * Mat->NbColumns) {
95 p = (Value *)realloc(Mat->p_Init, NbRows * Mat->NbColumns * sizeof(Value));
96 if (!p) {
97 errormsg1("Matrix_Extend", "outofmem", "out of memory space");
98 return;
99 }
100 Mat->p_Init = p;
101 Vector_Set(Mat->p_Init + Mat->NbRows * Mat->NbColumns, 0,
102 Mat->p_Init_size - Mat->NbRows * Mat->NbColumns);
103 for (i = Mat->p_Init_size; i < Mat->NbColumns * NbRows; ++i)
104 value_init(Mat->p_Init[i]);
105 Mat->p_Init_size = Mat->NbColumns * NbRows;
106 } else
107 Vector_Set(Mat->p_Init + Mat->NbRows * Mat->NbColumns, 0,
108 (NbRows - Mat->NbRows) * Mat->NbColumns);
109 for (i = 0; i < NbRows; i++) {
110 Mat->p[i] = Mat->p_Init + (i * Mat->NbColumns);
111 }
112 Mat->NbRows = NbRows;
113}
114
115/*
116 * Print the contents of the Matrix 'Mat'
117 */
118void Matrix_Print(FILE *Dst, const char *Format, Matrix *Mat) {
119 Value *p;
120 int i, j;
121 unsigned NbRows, NbColumns;
122
123 if (!Mat)
124 {
125 return;
126 }
127
128
129 fprintf(Dst, "%d %d\n", NbRows = Mat->NbRows, NbColumns = Mat->NbColumns);
130 if (NbColumns == 0) {
131 fprintf(Dst, "\n");
132 return;
133 }
134 for (i = 0; i < NbRows; i++) {
135 p = *(Mat->p + i);
136 for (j = 0; j < NbColumns; j++) {
137 if (!Format) {
138 value_print(Dst, " " P_VALUE_FMT " ", *p++);
139 } else {
140 value_print(Dst, Format, *p++);
141 }
142 }
143 fprintf(Dst, "\n");
144 }
145} /* Matrix_Print */
146
147/*
148 * Read the contents of the Matrix 'Mat' from standard input
149 */
151 return Matrix_Read_InputFile(Mat, stdin);
152} /* Matrix_Read_Input */
153
154/*
155 * Read the contents of the Matrix 'Mat' from file open in fp
156 */
158
159 Value *p;
160 int i, j, n;
161 char *c, s[1024];
162
163 p = Mat->p_Init;
164 for (i = 0; i < Mat->NbRows; i++) {
165 do {
166 c = fgets(s, 1024, fp);
167 if (!c)
168 break;
169 /* jump to the first non space char */
170 while (isspace(*c) && *c != '\n' && *c)
171 ++c;
172 } while (*c == '#' ||
173 *c == '\n'); /* continue if the line is empty or is a comment */
174 if (!c) {
175 errormsg1("Matrix_Read", "baddim", "not enough rows");
176 return (NULL);
177 }
178
179 if (c - s >= 1023) {
180 /* skip to EOL and ignore the rest of the line if longer than 1024 */
181 errormsg1(
182 "Matrix_Read", "warning",
183 "line longer than 1024 chars (ignored remaining chars till EOL)");
184 do
185 n = getchar();
186 while (n != EOF && n != '\n');
187 }
188
189 for (j = 0; j < Mat->NbColumns; j++) {
190 char *z;
191 if (*c == '\n' || *c == '#' || *c == '\0') {
192 errormsg1("Matrix_Read", "baddim", "not enough columns");
193 return (NULL);
194 }
195 /* go the the next space or end */
196 for (z = c; *z; z++) {
197 if (*z == '\n' || *z == '#' || isspace(*z))
198 break;
199 }
200 if (*z)
201 *z = '\0';
202 else
203 z--; /* hit EOL :/ go back one char */
204 value_read(*(p++), c);
205 /* go point to the next non space char */
206 c = z + 1;
207 while (isspace(*c))
208 c++;
209 }
210 }
211 return (Mat);
212} /* Matrix_Read_InputFile */
213
214/*
215 * Read the contents of the matrix 'Mat' from standard input.
216 * a '#' is a comment (till EOL)
217 */
218Matrix *Matrix_Read(void) { return Matrix_ReadFile(stdin); } /* Matrix_Read */
219
220/*
221 * Read the contents of the matrix 'Mat' from file open in fp.
222 * a '#' is a comment (till EOL)
223 */
225 Matrix *Mat;
226 unsigned NbRows, NbColumns;
227 char s[1024];
228
229 if (fgets(s, 1024, fp) == NULL)
230 return NULL;
231 while ((*s == '#' || *s == '\n') ||
232 (sscanf(s, "%d %d", &NbRows, &NbColumns) < 2)) {
233 if (fgets(s, 1024, fp) == NULL)
234 return NULL;
235 }
236 Mat = Matrix_Alloc(NbRows, NbColumns);
237 if (!Mat) {
238 errormsg1("Matrix_Read", "outofmem", "out of memory space");
239 return (NULL);
240 }
241 if (!Matrix_Read_Input(Mat)) {
242 Matrix_Free(Mat);
243 Mat = NULL;
244 }
245 return Mat;
246} /* Matrix_ReadFile */
247
248/*
249 * Basic hermite engine
250 *
251 * Modifies H to its HNF, writes to U and Q if not NULL.
252 * returns the rank of H.
253 */
254static int hermite(Matrix *H, Matrix *U, Matrix *Q) {
255
256 int nc, nr, i, j, k, rank, reduced, pivotrow;
257 Value pivot, x, aux;
258
259 /* T -1 T */
260 /* Computes form: A = Q H and U A = H and U = Q */
261
262 if (!H) {
263 errormsg1("Domlib", "nullH", "hermite: ? Null H");
264 return -1;
265 }
266 nc = H->NbColumns;
267 nr = H->NbRows;
268
269 /* Initialize all the 'Value' variables */
270 value_init(pivot);
271 value_init(x);
272 value_init(aux);
273
274 #ifdef DEBUG
275 fprintf(stderr, "Start -----------\n");
276 Matrix_Print(stderr, 0, H);
277 #endif
278 for (k = 0, rank = 0; k < nc && rank < nr; k = k + 1) {
279 reduced = 1; /* go through loop the first time */
280 #ifdef DEBUG
281 fprintf(stderr, "Working on col %d. Rank=%d ----------\n", k + 1,
282 rank + 1);
283 #endif
284 while (reduced) {
285 reduced = 0;
286
287 /* 1. find pivot row */
288 value_absolute(pivot, H->p[rank][k]);
289
290 /* the kth-diagonal element */
291 pivotrow = rank;
292
293 /* find the row i>rank with smallest nonzero element in col k */
294 for (i = rank + 1; i < nr; i++) {
295 value_absolute(x, H->p[i][k]);
296 if (value_notzero_p(x) && (value_lt(x, pivot) || value_zero_p(pivot))) {
297 value_assign(pivot, x);
298 pivotrow = i;
299 }
300 }
301
302 /* 2. Bring pivot to diagonal (exchange rows pivotrow and rank) */
303 if (pivotrow != rank) {
304 Vector_Exchange(H->p[pivotrow], H->p[rank], nc);
305 if (U)
306 Vector_Exchange(U->p[pivotrow], U->p[rank], nr);
307 if (Q)
308 Vector_Exchange(Q->p[pivotrow], Q->p[rank], nr);
309
310 #ifdef DEBUG
311 fprintf(stderr, "Exchange rows %d and %d -----------\n", rank + 1,
312 pivotrow + 1);
313 Matrix_Print(stderr, 0, H);
314 #endif
315 }
316
317 /* 3. Invert the row 'rank' if pivot is negative */
318 if (value_neg_p(H->p[rank][k])) {
319 for (j = 0; j < nc; j++)
320 value_oppose(H->p[rank][j], H->p[rank][j]);
321
322 /* H->p[rank][j] = -(H->p[rank][j]); */
323 if (U)
324 for (j = 0; j < nr; j++)
325 value_oppose(U->p[rank][j], U->p[rank][j]);
326
327 /* U->p[rank][j] = -(U->p[rank][j]); */
328 if (Q)
329 for (j = 0; j < nr; j++)
330 value_oppose(Q->p[rank][j], Q->p[rank][j]);
331
332 /* Q->p[rank][j] = -(Q->p[rank][j]); */
333 #ifdef DEBUG
334 fprintf(stderr, "Negate row %d -----------\n", rank + 1);
335 Matrix_Print(stderr, 0, H);
336 #endif
337 }
338 if (value_notzero_p(pivot)) {
339
340 /* 4. Reduce the column modulo the pivot */
341 /* This eventually zeros out everything below the */
342 /* diagonal and produces an upper triangular matrix */
343
344 for (i = rank + 1; i < nr; i++) {
345 value_assign(x, H->p[i][k]);
346 if (value_notzero_p(x)) {
347 value_modulus(aux, x, pivot);
348
349 /* floor[integer division] (corrected for neg x) */
350 if (value_neg_p(x) && value_notzero_p(aux)) {
351
352 /* x=(x/pivot)-1; */
353 value_division(x, x, pivot);
354 value_decrement(x, x);
355 } else
356 value_division(x, x, pivot);
357 for (j = 0; j < nc; j++) {
358 value_multiply(aux, x, H->p[rank][j]);
359 value_subtract(H->p[i][j], H->p[i][j], aux);
360 }
361
362 /* U->p[i][j] -= (x * U->p[rank][j]); */
363 if (U)
364 for (j = 0; j < nr; j++) {
365 value_multiply(aux, x, U->p[rank][j]);
366 value_subtract(U->p[i][j], U->p[i][j], aux);
367 }
368
369 /* Q->p[rank][j] += (x * Q->p[i][j]); */
370 if (Q)
371 for (j = 0; j < nr; j++) {
372 value_addmul(Q->p[rank][j], x, Q->p[i][j]);
373 }
374 reduced = 1;
375
376 #ifdef DEBUG
377 fprintf(stderr, "row %d = row %d - %d row %d -----------\n", i + 1,
378 i + 1, x, rank + 1);
379 Matrix_Print(stderr, 0, H);
380 #endif
381
382 } /* if (x) */
383 } /* for (i) */
384 } /* if (pivot != 0) */
385 } /* while (reduced) */
386
387 /* Last finish up this column */
388 /* 5. Make pivot column positive (above pivot row) */
389 /* x should be zero for i>k */
390
391 if (value_notzero_p(pivot)) {
392 for (i = 0; i < rank; i++) {
393 value_assign(x, H->p[i][k]);
394 if (value_notzero_p(x)) {
395 value_modulus(aux, x, pivot);
396
397 /* floor[integer division] (corrected for neg x) */
398 if (value_neg_p(x) && value_notzero_p(aux)) {
399 value_division(x, x, pivot);
400 value_decrement(x, x);
401
402 /* x=(x/pivot)-1; */
403 } else
404 value_division(x, x, pivot);
405
406 /* H->p[i][j] -= x * H->p[rank][j]; */
407 for (j = 0; j < nc; j++) {
408 value_multiply(aux, x, H->p[rank][j]);
409 value_subtract(H->p[i][j], H->p[i][j], aux);
410 }
411
412 /* U->p[i][j] -= x * U->p[rank][j]; */
413 if (U)
414 for (j = 0; j < nr; j++) {
415 value_multiply(aux, x, U->p[rank][j]);
416 value_subtract(U->p[i][j], U->p[i][j], aux);
417 }
418
419 /* Q->p[rank][j] += x * Q->p[i][j]; */
420 if (Q)
421 for (j = 0; j < nr; j++) {
422 value_addmul(Q->p[rank][j], x, Q->p[i][j]);
423 }
424 #ifdef DEBUG
425 fprintf(stderr, "row %d = row %d - %d row %d -----------\n", i + 1,
426 i + 1, x, rank + 1);
427 Matrix_Print(stderr, 0, H);
428 #endif
429 } /* if (x) */
430 } /* for (i) */
431 rank++;
432 } /* if (pivot!=0) */
433 } /* for (k) */
434
435 /* Clear all the 'Value' variables */
436 value_clear(pivot);
437 value_clear(x);
438 value_clear(aux);
439 return rank;
440} /* Hermite */
441
442/*
443 * Right Hermite
444 * Computes H, U and Q such that: A = QH, and UA = H
445 *
446 * Allocates matrices *Qp and *Up if Qp and Up are not NULL.
447 */
448void right_hermite(Matrix *A, Matrix **Hp, Matrix **Up, Matrix **Qp) {
449
450 Matrix *H, *Q, *U;
451 int i, j, nr, nc;
452 Value tmp;
453
454 /* Computes form: A = QH , UA = H */
455 nc = A->NbColumns;
456 nr = A->NbRows;
457
458 /* H = A */
459 *Hp = H = Matrix_Alloc(nr, nc);
460 if (!H) {
461 errormsg1("DomRightHermite", "outofmem", "out of memory space");
462 return;
463 }
464
465 /* Initialize all the 'Value' variables */
466 value_init(tmp);
467
468 Vector_Copy(A->p_Init, H->p_Init, nr * nc);
469
470 /* U = I */
471 if (Up) {
472 *Up = U = Matrix_Alloc(nr, nr);
473 if (!U) {
474 errormsg1("DomRightHermite", "outofmem", "out of memory space");
475 value_clear(tmp);
476 return;
477 }
478 Vector_Set(U->p_Init, 0, nr * nr); /* zero's */
479 for (i = 0; i < nr; i++) /* with diagonal of 1's */
480 value_set_si(U->p[i][i], 1);
481 } else
482 U = (Matrix *)0;
483
484 /* Q = I */
485 /* Actually I compute Q transpose... its easier */
486 if (Qp) {
487 *Qp = Q = Matrix_Alloc(nr, nr);
488 if (!Q) {
489 errormsg1("DomRightHermite", "outofmem", "out of memory space");
490 value_clear(tmp);
491 return;
492 }
493 Vector_Set(Q->p_Init, 0, nr * nr); /* zero's */
494 for (i = 0; i < nr; i++) /* with diagonal of 1's */
495 value_set_si(Q->p[i][i], 1);
496 } else
497 Q = (Matrix *)0;
498
499 // call to base Hermite engine
500 hermite(H, U, Q);
501
502 /* Q is returned transposed */
503 /* Transpose Q */
504 if (Q) {
505 for (i = 0; i < nr; i++) {
506 for (j = i + 1; j < nr; j++) {
507 value_assign(tmp, Q->p[i][j]);
508 value_assign(Q->p[i][j], Q->p[j][i]);
509 value_assign(Q->p[j][i], tmp);
510 }
511 }
512 }
513 value_clear(tmp);
514 return;
515} /* right_hermite */
516
517/*
518 * Left Hermite:
519 * compute H, U and Q such that: A = HQ, and AU = H.
520 *
521 * Allocates matrices *Qp and *Up if Qp and Up are not NULL.
522 */
523void left_hermite(Matrix *A, Matrix **Hp, Matrix **Qp, Matrix **Up) {
524
525 Matrix *H, *HT, *Q, *U;
526 int i, j, nc, nr;
527 Value tmp;
528
529 /* Computes left form: A = HQ , AU = H ,
530 T T T T T T
531 using right form A = Q H , U A = H */
532
533 nr = A->NbRows;
534 nc = A->NbColumns;
535
536 /* HT = A transpose */
537 HT = Matrix_Alloc(nc, nr);
538 if (!HT) {
539 errormsg1("DomLeftHermite", "outofmem", "out of memory space");
540 return;
541 }
542 value_init(tmp);
543 for (i = 0; i < nr; i++)
544 for (j = 0; j < nc; j++)
545 value_assign(HT->p[j][i], A->p[i][j]);
546
547 /* U = I */
548 if (Up) {
549 *Up = U = Matrix_Alloc(nc, nc);
550 if (!U) {
551 errormsg1("DomLeftHermite", "outofmem", "out of memory space");
552 value_clear(tmp);
553 return;
554 }
555 Vector_Set(U->p_Init, 0, nc * nc); /* zero's */
556 for (i = 0; i < nc; i++) /* with diagonal of 1's */
557 value_set_si(U->p[i][i], 1);
558 } else
559 U = (Matrix *)0;
560
561 /* Q = I */
562 if (Qp) {
563 *Qp = Q = Matrix_Alloc(nc, nc);
564 if (!Q) {
565 errormsg1("DomLeftHermite", "outofmem", "out of memory space");
566 value_clear(tmp);
567 return;
568 }
569 Vector_Set(Q->p_Init, 0, nc * nc); /* zero's */
570 for (i = 0; i < nc; i++) /* with diagonal of 1's */
571 value_set_si(Q->p[i][i], 1);
572 } else
573 Q = (Matrix *)0;
574
575 // call to base Hermite engine
576 hermite(HT, U, Q);
577
578 /* H = HT transpose */
579 *Hp = H = Matrix_Alloc(nr, nc);
580 if (!H) {
581 errormsg1("DomLeftHermite", "outofmem", "out of memory space");
582 value_clear(tmp);
583 return;
584 }
585 for (i = 0; i < nr; i++)
586 for (j = 0; j < nc; j++)
587 value_assign(H->p[i][j], HT->p[j][i]);
588 Matrix_Free(HT);
589
590 /* Transpose U */
591 if (U) {
592 for (i = 0; i < nc; i++) {
593 for (j = i + 1; j < nc; j++) {
594 value_assign(tmp, U->p[i][j]);
595 value_assign(U->p[i][j], U->p[j][i]);
596 value_assign(U->p[j][i], tmp);
597 }
598 }
599 }
600 value_clear(tmp);
601} /* left_hermite */
602
603/*
604 * Given a integer matrix 'Mat'(k x k), compute its inverse rational matrix
605 * 'MatInv' k x (k+1). The last column of each row in matrix MatInv is used
606 * to store the common denominator of the entries in a row. The output is 1,
607 * if 'Mat' is non-singular (invertible), otherwise the output is 0. Note::
608 * (1) Matrix 'Mat' is modified during the inverse operation.
609 * (2) Matrix 'MatInv' must be preallocated before passing into this function.
610 */
611int MatInverse(Matrix *Mat, Matrix *MatInv) {
612
613 int i, k, j, c;
614 Value x, gcd, piv;
615 Value m1, m2;
616
617 if (Mat->NbRows != Mat->NbColumns) {
618 fprintf(stderr, "Trying to invert a non-square matrix !\n");
619 return 0;
620 }
621
622 /* Initialize all the 'Value' variables */
623 value_init(x);
624 value_init(gcd);
625 value_init(piv);
626 value_init(m1);
627 value_init(m2);
628
629 k = Mat->NbRows;
630
631 /* Initialise MatInv */
632 Vector_Set(MatInv->p[0], 0, k * (k + 1));
633
634 /* Initialize 'MatInv' to Identity matrix form. Each diagonal entry is set*/
635 /* to 1. Last column of each row (denominator of each entry in a row) is */
636 /* also set to 1. */
637 for (i = 0; i < k; ++i) {
638 value_set_si(MatInv->p[i][i], 1);
639 value_set_si(MatInv->p[i][k], 1); /* denum */
640 }
641 /* Apply Gauss-Jordan elimination method on the two matrices 'Mat' and */
642 /* 'MatInv' in parallel. */
643 for (i = 0; i < k; ++i) {
644
645 /* Check if the diagonal entry (new pivot) is non-zero or not */
646 if (value_zero_p(Mat->p[i][i])) {
647
648 /* Search for a non-zero pivot down the column(i) */
649 for (j = i; j < k; ++j)
650 if (value_notzero_p(Mat->p[j][i]))
651 break;
652
653 /* If no non-zero pivot is found, the matrix 'Mat' is non-invertible */
654 /* Return 0. */
655 if (j == k) {
656
657 /* Clear all the 'Value' variables */
658 value_clear(x);
659 value_clear(gcd);
660 value_clear(piv);
661 value_clear(m1);
662 value_clear(m2);
663 return 0;
664 }
665
666 /* Exchange the rows, row(i) and row(j) so that the diagonal element */
667 /* Mat->p[i][i] (pivot) is non-zero. Repeat the same operations on */
668 /* matrix 'MatInv'. */
669 for (c = 0; c < k; ++c) {
670
671 /* Interchange rows, row(i) and row(j) of matrix 'Mat' */
672 value_assign(x, Mat->p[j][c]);
673 value_assign(Mat->p[j][c], Mat->p[i][c]);
674 value_assign(Mat->p[i][c], x);
675
676 /* Interchange rows, row(i) and row(j) of matrix 'MatInv' */
677 value_assign(x, MatInv->p[j][c]);
678 value_assign(MatInv->p[j][c], MatInv->p[i][c]);
679 value_assign(MatInv->p[i][c], x);
680 }
681 }
682
683 /* Make all the entries in column(i) of matrix 'Mat' zero except the */
684 /* diagonal entry. Repeat the same sequence of operations on matrix */
685 /* 'MatInv'. */
686 for (j = 0; j < k; ++j) {
687 if (j == i)
688 continue; /* Skip the pivot */
689 value_assign(x, Mat->p[j][i]);
690 if (value_notzero_p(x)) {
691 value_assign(piv, Mat->p[i][i]);
692 value_gcd(gcd, x, piv);
693 if (value_notone_p(gcd)) {
694 value_divexact(x, x, gcd);
695 value_divexact(piv, piv, gcd);
696 }
697 for (c = ((j > i) ? i : 0); c < k; ++c) {
698 value_multiply(m1, piv, Mat->p[j][c]);
699 value_multiply(m2, x, Mat->p[i][c]);
700 value_subtract(Mat->p[j][c], m1, m2);
701 }
702 for (c = 0; c < k; ++c) {
703 value_multiply(m1, piv, MatInv->p[j][c]);
704 value_multiply(m2, x, MatInv->p[i][c]);
705 value_subtract(MatInv->p[j][c], m1, m2);
706 }
707
708 /* Simplify row(j) of the two matrices 'Mat' and 'MatInv' by */
709 /* dividing the rows with the common GCD. */
710 Vector_Gcd(&MatInv->p[j][0], k, &m1);
711 Vector_Gcd(&Mat->p[j][0], k, &m2);
712 value_gcd(gcd, m1, m2);
713 if (value_notone_p(gcd)) {
714 for (c = 0; c < k; ++c) {
715 value_divexact(Mat->p[j][c], Mat->p[j][c], gcd);
716 value_divexact(MatInv->p[j][c], MatInv->p[j][c], gcd);
717 }
718 }
719 }
720 }
721 }
722
723 /* Simplify every row so that 'Mat' reduces to Identity matrix. Perform */
724 /* the same sequence of operations on the matrix 'MatInv'. */
725 for (j = 0; j < k; ++j) {
726 value_assign(MatInv->p[j][k], Mat->p[j][j]);
727
728 /* Make the last column (denominator of each entry) of every row greater */
729 /* than zero. */
730 Vector_Normalize_Positive(&MatInv->p[j][0], k + 1, k);
731 }
732
733 /* Clear all the 'Value' variables */
734 value_clear(x);
735 value_clear(gcd);
736 value_clear(piv);
737 value_clear(m1);
738 value_clear(m2);
739
740 return 1;
741} /* MatInverse */
742
743/*
744 * Given (m x n) integer matrix 'X' and n x (k+1) rational matrix 'P', compute
745 * the rational m x (k+1) rational matrix 'S'. The last column in each row of
746 * the rational matrices is used to store the common denominator of elements
747 * in a row.
748 */
749void rat_prodmat(Matrix *S, Matrix *X, Matrix *P) {
750
751 int i, j, k;
752 int last_column_index = P->NbColumns - 1;
753 Value lcm, gcd, last_column_entry, s1;
754 Value m1, m2;
755
756 /* Initialize all the 'Value' variables */
757 value_init(lcm);
758 value_init(gcd);
759 value_init(last_column_entry);
760 value_init(s1);
761 value_init(m1);
762 value_init(m2);
763
764 /* Compute the LCM of last column entries (denominators) of rows */
765 value_assign(lcm, P->p[0][last_column_index]);
766 for (k = 1; k < P->NbRows; ++k) {
767 value_assign(last_column_entry, P->p[k][last_column_index]);
768 value_gcd(gcd, lcm, last_column_entry);
769 value_divexact(m1, last_column_entry, gcd);
770 value_multiply(lcm, lcm, m1);
771 }
772
773 /* S[i][j] = Sum(X[i][k] * P[k][j] where Sum is extended over k = 1..nbrows*/
774 for (i = 0; i < X->NbRows; ++i)
775 for (j = 0; j < P->NbColumns - 1; ++j) {
776
777 /* Initialize s1 to zero. */
778 value_set_si(s1, 0);
779 for (k = 0; k < P->NbRows; ++k) {
780
781 /* If the LCM of last column entries is one, simply add the products */
782 if (value_one_p(lcm)) {
783 value_addmul(s1, X->p[i][k], P->p[k][j]);
784 }
785
786 /* Numerator (num) and denominator (denom) of S[i][j] is given by :- */
787 /* numerator = Sum(X[i][k]*P[k][j]*lcm/P[k][last_column_index]) and */
788 /* denominator= lcm where Sum is extended over k = 1..nbrows. */
789 else {
790 value_multiply(m1, X->p[i][k], P->p[k][j]);
791 value_division(m2, lcm, P->p[k][last_column_index]);
792 value_addmul(s1, m1, m2);
793 }
794 }
795 value_assign(S->p[i][j], s1);
796 }
797
798 for (i = 0; i < S->NbRows; ++i) {
799 value_assign(S->p[i][last_column_index], lcm);
800
801 /* Normalize the rows so that last element >=0 */
802 Vector_Normalize_Positive(&S->p[i][0], S->NbColumns, S->NbColumns - 1);
803 }
804
805 /* Clear all the 'Value' variables */
806 value_clear(lcm);
807 value_clear(gcd);
808 value_clear(last_column_entry);
809 value_clear(s1);
810 value_clear(m1);
811 value_clear(m2);
812
813 return;
814} /* rat_prodmat */
815
816/*
817 * Given a matrix 'Mat' and vector 'p1', compute the matrix-vector product
818 * and store the result in vector 'p2'.
819 */
821
822 int NbRows, NbColumns, i, j;
823 Value **cm, *q, *cp1, *cp2;
824
825 NbRows = Mat->NbRows;
826 NbColumns = Mat->NbColumns;
827
828 cm = Mat->p;
829 cp2 = p2;
830 for (i = 0; i < NbRows; i++) {
831 q = *cm++;
832 cp1 = p1;
833 value_multiply(*cp2, *q, *cp1);
834 q++;
835 cp1++;
836
837 /* *cp2 = *q++ * *cp1++ */
838 for (j = 1; j < NbColumns; j++) {
839 value_addmul(*cp2, *q, *cp1);
840 q++;
841 cp1++;
842 }
843 cp2++;
844 }
845 return;
846} /* Matrix_Vector_Product */
847
848/*
849 * Given a vector 'p1' and a matrix 'Mat', compute the vector-matrix product
850 * and store the result in vector 'p2'
851 */
853
854 int NbRows, NbColumns, i, j;
855 Value **cm, *cp1, *cp2;
856
857 NbRows = Mat->NbRows;
858 NbColumns = Mat->NbColumns;
859 cp2 = p2;
860 cm = Mat->p;
861 for (j = 0; j < NbColumns; j++) {
862 cp1 = p1;
863 value_multiply(*cp2, *(*cm + j), *cp1);
864 cp1++;
865
866 /* *cp2= *(*cm+j) * *cp1++; */
867 for (i = 1; i < NbRows; i++) {
868 value_addmul(*cp2, *(*(cm + i) + j), *cp1);
869 cp1++;
870 }
871 cp2++;
872 }
873 return;
874} /* Vector_Matrix_Product */
875
876/*
877 * Given matrices 'Mat1' and 'Mat2', compute the matrix product and store in
878 * matrix 'Mat3'
879 */
880void Matrix_Product(Matrix *Mat1, Matrix *Mat2, Matrix *Mat3) {
881
882 int Size, i, j, k;
883 unsigned NbRows, NbColumns;
884 Value **q1, **q2, *p1, *p3, sum;
885
886 NbRows = Mat1->NbRows;
887 NbColumns = Mat2->NbColumns;
888
889 Size = Mat1->NbColumns;
890 if (Mat2->NbRows != Size || Mat3->NbRows != NbRows ||
891 Mat3->NbColumns != NbColumns) {
892 fprintf(stderr, "? Matrix_Product : incompatible matrix dimension\n");
893 return;
894 }
895 value_init(sum);
896 p3 = Mat3->p_Init;
897 q1 = Mat1->p;
898 q2 = Mat2->p;
899
900 /* Mat3[i][j] = Sum(Mat1[i][k]*Mat2[k][j] where sum is over k = 1..nbrows */
901 for (i = 0; i < NbRows; i++) {
902 for (j = 0; j < NbColumns; j++) {
903 p1 = *(q1 + i);
904 value_set_si(sum, 0);
905 for (k = 0; k < Size; k++) {
906 value_addmul(sum, *p1, *(*(q2 + k) + j));
907 p1++;
908 }
909 value_assign(*p3, sum);
910 p3++;
911 }
912 }
913 value_clear(sum);
914 return;
915} /* Matrix_Product */
916
917/*
918 * Given a rational matrix 'Mat'(k x k), compute its inverse rational matrix
919 * 'MatInv' k x k.
920 * The output is 1,
921 * if 'Mat' is non-singular (invertible), otherwise the output is 0. Note::
922 * (1) Matrix 'Mat' is modified during the inverse operation.
923 * (2) Matrix 'MatInv' must be preallocated before passing into this function.
924 */
925int Matrix_Inverse(Matrix *Mat, Matrix *MatInv) {
926
927 int i, k, j, c;
928 Value x, gcd, piv;
929 Value m1, m2;
930 Value *den;
931
932 if (Mat->NbRows != Mat->NbColumns) {
933 fprintf(stderr, "Trying to invert a non-square matrix !\n");
934 return 0;
935 }
936
937 /* Initialize all the 'Value' variables */
938 value_init(x);
939 value_init(gcd);
940 value_init(piv);
941 value_init(m1);
942 value_init(m2);
943
944 k = Mat->NbRows;
945
946 /* Initialise MatInv */
947 Vector_Set(MatInv->p[0], 0, k * k);
948
949 /* Initialize 'MatInv' to Identity matrix form. Each diagonal entry is set*/
950 /* to 1. Last column of each row (denominator of each entry in a row) is */
951 /* also set to 1. */
952 for (i = 0; i < k; ++i) {
953 value_set_si(MatInv->p[i][i], 1);
954 /* value_set_si(MatInv->p[i][k],1); // denum */
955 }
956 /* Apply Gauss-Jordan elimination method on the two matrices 'Mat' and */
957 /* 'MatInv' in parallel. */
958 for (i = 0; i < k; ++i) {
959
960 /* Check if the diagonal entry (new pivot) is non-zero or not */
961 if (value_zero_p(Mat->p[i][i])) {
962
963 /* Search for a non-zero pivot down the column(i) */
964 for (j = i; j < k; ++j)
965 if (value_notzero_p(Mat->p[j][i]))
966 break;
967
968 /* If no non-zero pivot is found, the matrix 'Mat' is non-invertible */
969 /* Return 0. */
970 if (j == k) {
971
972 /* Clear all the 'Value' variables */
973 value_clear(x);
974 value_clear(gcd);
975 value_clear(piv);
976 value_clear(m1);
977 value_clear(m2);
978 return 0;
979 }
980
981 /* Exchange the rows, row(i) and row(j) so that the diagonal element */
982 /* Mat->p[i][i] (pivot) is non-zero. Repeat the same operations on */
983 /* matrix 'MatInv'. */
984 for (c = 0; c < k; ++c) {
985
986 /* Interchange rows, row(i) and row(j) of matrix 'Mat' */
987 value_assign(x, Mat->p[j][c]);
988 value_assign(Mat->p[j][c], Mat->p[i][c]);
989 value_assign(Mat->p[i][c], x);
990
991 /* Interchange rows, row(i) and row(j) of matrix 'MatInv' */
992 value_assign(x, MatInv->p[j][c]);
993 value_assign(MatInv->p[j][c], MatInv->p[i][c]);
994 value_assign(MatInv->p[i][c], x);
995 }
996 }
997
998 /* Make all the entries in column(i) of matrix 'Mat' zero except the */
999 /* diagonal entry. Repeat the same sequence of operations on matrix */
1000 /* 'MatInv'. */
1001 for (j = 0; j < k; ++j) {
1002 if (j == i)
1003 continue; /* Skip the pivot */
1004 value_assign(x, Mat->p[j][i]);
1005 if (value_notzero_p(x)) {
1006 value_assign(piv, Mat->p[i][i]);
1007 value_gcd(gcd, x, piv);
1008 if (value_notone_p(gcd)) {
1009 value_divexact(x, x, gcd);
1010 value_divexact(piv, piv, gcd);
1011 }
1012 for (c = ((j > i) ? i : 0); c < k; ++c) {
1013 value_multiply(m1, piv, Mat->p[j][c]);
1014 value_multiply(m2, x, Mat->p[i][c]);
1015 value_subtract(Mat->p[j][c], m1, m2);
1016 }
1017 for (c = 0; c < k; ++c) {
1018 value_multiply(m1, piv, MatInv->p[j][c]);
1019 value_multiply(m2, x, MatInv->p[i][c]);
1020 value_subtract(MatInv->p[j][c], m1, m2);
1021 }
1022
1023 /* Simplify row(j) of the two matrices 'Mat' and 'MatInv' by */
1024 /* dividing the rows with the common GCD. */
1025 Vector_Gcd(&MatInv->p[j][0], k, &m1);
1026 Vector_Gcd(&Mat->p[j][0], k, &m2);
1027 value_gcd(gcd, m1, m2);
1028 if (value_notone_p(gcd)) {
1029 for (c = 0; c < k; ++c) {
1030 value_divexact(Mat->p[j][c], Mat->p[j][c], gcd);
1031 value_divexact(MatInv->p[j][c], MatInv->p[j][c], gcd);
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 /* Find common denom for each row */
1039 den = (Value *)malloc(k * sizeof(Value));
1040 value_set_si(x, 1);
1041 for (j = 0; j < k; ++j) {
1042 value_init(den[j]);
1043 value_assign(den[j], Mat->p[j][j]);
1044
1045 /* gcd is always positive */
1046 Vector_Gcd(&MatInv->p[j][0], k, &gcd);
1047 value_gcd(gcd, gcd, den[j]);
1048 if (value_neg_p(den[j]))
1049 value_oppose(gcd, gcd); /* make denominator positive */
1050 if (value_notone_p(gcd)) {
1051 for (c = 0; c < k; c++)
1052 value_divexact(MatInv->p[j][c], MatInv->p[j][c], gcd); /* normalize */
1053 value_divexact(den[j], den[j], gcd);
1054 }
1055 value_gcd(gcd, x, den[j]);
1056 value_divexact(m1, den[j], gcd);
1057 value_multiply(x, x, m1);
1058 }
1059 if (value_notone_p(x))
1060 for (j = 0; j < k; ++j) {
1061 for (c = 0; c < k; c++) {
1062 value_division(m1, x, den[j]);
1063 value_multiply(MatInv->p[j][c], MatInv->p[j][c], m1); /* normalize */
1064 }
1065 }
1066
1067 /* Clear all the 'Value' variables */
1068 for (j = 0; j < k; ++j) {
1069 value_clear(den[j]);
1070 }
1071 value_clear(x);
1072 value_clear(gcd);
1073 value_clear(piv);
1074 value_clear(m1);
1075 value_clear(m2);
1076 free(den);
1077
1078 return 1;
1079} /* Matrix_Inverse */
#define value_oppose(ref, val)
Definition: arithmetique.h:555
#define value_notzero_p(val)
Definition: arithmetique.h:579
#define value_divexact(ref, val1, val2)
Definition: arithmetique.h:551
#define value_gcd(ref, val1, val2)
Definition: arithmetique.h:559
#define value_notone_p(val)
Definition: arithmetique.h:581
#define value_one_p(val)
Definition: arithmetique.h:580
#define value_absolute(ref, val)
Definition: arithmetique.h:556
#define value_decrement(ref, val)
Definition: arithmetique.h:549
#define value_zero_p(val)
Definition: arithmetique.h:578
#define value_assign(v1, v2)
Definition: arithmetique.h:485
int Value
Definition: arithmetique.h:294
#define value_set_si(val, i)
Definition: arithmetique.h:486
#define value_addmul(ref, val1, val2)
Definition: arithmetique.h:542
#define value_read(val, str)
Definition: arithmetique.h:489
#define value_clear(val)
Definition: arithmetique.h:488
#define value_division(ref, val1, val2)
Definition: arithmetique.h:550
#define value_multiply(ref, val1, val2)
Definition: arithmetique.h:546
#define value_print(Dst, fmt, val)
Definition: arithmetique.h:490
#define value_lt(v1, v2)
Definition: arithmetique.h:509
#define value_modulus(ref, val1, val2)
Definition: arithmetique.h:552
#define value_subtract(ref, val1, val2)
Definition: arithmetique.h:547
#define value_neg_p(val)
Definition: arithmetique.h:575
#define value_init(val)
Definition: arithmetique.h:484
void errormsg1(const char *f, const char *msgname, const char *msg)
Definition: errormsg.c:25
static int hermite(Matrix *H, Matrix *U, Matrix *Q)
Definition: matrix.c:254
void Vector_Matrix_Product(Value *p1, Matrix *Mat, Value *p2)
Definition: matrix.c:852
void Matrix_Product(Matrix *Mat1, Matrix *Mat2, Matrix *Mat3)
Definition: matrix.c:880
void Matrix_Extend(Matrix *Mat, unsigned NbRows)
Definition: matrix.c:84
void Matrix_Vector_Product(Matrix *Mat, Value *p1, Value *p2)
Definition: matrix.c:820
Matrix * Matrix_Alloc(unsigned NbRows, unsigned NbColumns)
Definition: matrix.c:24
Matrix * Matrix_Read_Input(Matrix *Mat)
Definition: matrix.c:150
int Matrix_Inverse(Matrix *Mat, Matrix *MatInv)
Definition: matrix.c:925
void right_hermite(Matrix *A, Matrix **Hp, Matrix **Up, Matrix **Qp)
Definition: matrix.c:448
Matrix * Matrix_Read(void)
Definition: matrix.c:218
void Matrix_Print(FILE *Dst, const char *Format, Matrix *Mat)
Definition: matrix.c:118
Matrix * Matrix_Read_InputFile(Matrix *Mat, FILE *fp)
Definition: matrix.c:157
void rat_prodmat(Matrix *S, Matrix *X, Matrix *P)
Definition: matrix.c:749
void left_hermite(Matrix *A, Matrix **Hp, Matrix **Qp, Matrix **Up)
Definition: matrix.c:523
int MatInverse(Matrix *Mat, Matrix *MatInv)
Definition: matrix.c:611
Matrix * Matrix_ReadFile(FILE *fp)
Definition: matrix.c:224
void Matrix_Free(Matrix *Mat)
Definition: matrix.c:69
static int nr
Definition: polyparam.c:280
static int n
Definition: polyparam.c:278
char s[128]
Definition: polytest.c:8
Definition: types.h:88
unsigned NbRows
Definition: types.h:89
Value ** p
Definition: types.h:90
int p_Init_size
Definition: types.h:92
unsigned NbColumns
Definition: types.h:89
Value * p_Init
Definition: types.h:91
#define P_VALUE_FMT
Definition: types.h:42
Value * value_alloc(int want, int *got)
Definition: vector.c:826
void Vector_Set(Value *p, int n, unsigned length)
Definition: vector.c:248
void value_free(Value *p, int size)
Definition: vector.c:875
void Vector_Normalize_Positive(Value *p, int length, int pos)
Definition: vector.c:607
void Vector_Gcd(Value *p, unsigned length, Value *min)
Definition: vector.c:518
void Vector_Exchange(Value *p1, Value *p2, unsigned length)
Definition: vector.c:263
void Vector_Copy(Value *p1, Value *p2, unsigned length)
Definition: vector.c:276