Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Testing latest pari + WASM + node.js... and it works?! Wow.

28486 views
License: GPL3
ubuntu2004
1
/* Copyright (C) 2000 The PARI group.
2
3
This file is part of the PARI/GP package.
4
5
PARI/GP is free software; you can redistribute it and/or modify it under the
6
terms of the GNU General Public License as published by the Free Software
7
Foundation; either version 2 of the License, or (at your option) any later
8
version. It is distributed in the hope that it will be useful, but WITHOUT
9
ANY WARRANTY WHATSOEVER.
10
11
Check the License for details. You should have received a copy of it, along
12
with the package; see the file 'COPYING'. If not, write to the Free Software
13
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
14
#include "pari.h"
15
#include "paripriv.h"
16
17
#define DEBUGLEVEL DEBUGLEVEL_mathnf
18
19
#define dbg_printf(lvl) if (DEBUGLEVEL >= (lvl) + 3) err_printf
20
21
/********************************************************************/
22
/** **/
23
/** BLACK BOX HERMITE RINGS AND HOWELL NORMAL FORM **/
24
/** contributed by Aurel Page (2017) **/
25
/** **/
26
/********************************************************************/
27
28
/*
29
bb_hermite R:
30
- add(a,b): a+b
31
- neg(a): -a
32
- mul(a,b): a*b
33
- extgcd(a,b,&small): [d,U] with d in R and U in GL_2(R) such that [0;d] = [a;b]*U.
34
set small==1 to assert that U is a 'small' operation (no red needed).
35
- rann(a): b in R such that b*R = {x in R | a*x==0}
36
- lquo(a,b,&r): q in R such that r=a-b*q is a canonical representative
37
of the image of a in R/b*R. The canonical lift of 0 must be 0.
38
- unit(a): u unit in R^* such that a*u is a canonical generator of the ideal a*R
39
- equal0(a): a==0?
40
- equal1(a): a==1?
41
- s(n): image of the small integer n in R
42
- red(a): unique representative of a as an element of R
43
44
op encoding of elementary operations:
45
- t_VECSMALL: the corresponding permutation (vecpermute)
46
- [Vecsmall([i,j])]: the transposition Ci <-> Cj
47
- [Vecsmall([i]),u], u in R^*: Ci <- Ci*u
48
- [Vecsmall([i,j]),a], a in R: Ci <- Ci + Cj*a
49
- [Vecsmall([i,j,0]),U], U in GL_2(R): (Ci|Cj) <- (Ci|Cj)*U
50
*/
51
52
struct bb_hermite
53
{
54
GEN (*add)(void*, GEN, GEN);
55
GEN (*neg)(void*, GEN);
56
GEN (*mul)(void*, GEN, GEN);
57
GEN (*extgcd)(void*, GEN, GEN, int*);
58
GEN (*rann)(void*, GEN);
59
GEN (*lquo)(void*, GEN, GEN, GEN*);
60
GEN (*unit)(void*, GEN);
61
int (*equal0)(GEN);
62
int (*equal1)(GEN);
63
GEN (*s)(void*, long);
64
GEN (*red)(void*, GEN);
65
};
66
67
static GEN
68
_Fp_add(void *data, GEN x, GEN y) { (void) data; return addii(x,y); }
69
70
static GEN
71
_Fp_neg(void *data, GEN x) { (void) data; return negi(x); }
72
73
static GEN
74
_Fp_mul(void *data, GEN x, GEN y) { (void) data; return mulii(x,y); }
75
76
static GEN
77
_Fp_rann(void *data, GEN x)
78
{
79
GEN d, N = (GEN)data;
80
if (!signe(x)) return gen_1;
81
d = gcdii(x,N);
82
return modii(diviiexact(N,d),N);
83
}
84
85
static GEN
86
_Fp_lquo(void *data, GEN x, GEN y, GEN* r) { (void) data; return truedvmdii(x,y,r); }
87
88
/* D=MN, p|M => !p|a, p|N => p|a, return M */
89
static GEN
90
Z_split(GEN D, GEN a)
91
{
92
long i, n;
93
GEN N;
94
n = expi(D);
95
n = n<2 ? 1 : expu(n)+1;
96
for (i=1;i<=n;i++)
97
a = Fp_sqr(a,D);
98
N = gcdii(a,D);
99
return diviiexact(D,N);
100
}
101
102
/* c s.t. gcd(a+cb,N) = gcd(a,b,N) without factoring */
103
static GEN
104
Z_stab(GEN a, GEN b, GEN N)
105
{
106
GEN g, a2, N2;
107
g = gcdii(a,b);
108
g = gcdii(g,N);
109
N2 = diviiexact(N,g);
110
a2 = diviiexact(a,g);
111
return Z_split(N2,a2);
112
}
113
114
static GEN
115
_Fp_unit(void *data, GEN x)
116
{
117
GEN g,s,v,d,N=(GEN)data,N2;
118
long i;
119
if (!signe(x)) return NULL;
120
g = bezout(x,N,&s,&v);
121
if (equali1(g) || equali1(gcdii(s,N))) return mkvec2(g,s);
122
N2 = diviiexact(N,g);
123
for (i=0; i<5; i++)
124
{
125
s = addii(s,N2);
126
if (equali1(gcdii(s,N))) return mkvec2(g,s);
127
}
128
d = Z_stab(s,N2,N);
129
d = mulii(d,N2);
130
v = Fp_add(s,d,N);
131
if (equali1(v)) return NULL;
132
return mkvec2(g,v);
133
}
134
135
static GEN
136
_Fp_extgcd(void *data, GEN x, GEN y, int* smallop)
137
{
138
GEN d,u,v,m;
139
if (equali1(y))
140
{
141
*smallop = 1;
142
return mkvec2(y,mkmat2(
143
mkcol2(gen_1,Fp_neg(x,(GEN)data)),
144
mkcol2(gen_0,gen_1)));
145
}
146
*smallop = 0;
147
d = bezout(x,y,&u,&v);
148
if (!signe(d)) return mkvec2(d,matid(2));
149
m = cgetg(3,t_MAT);
150
m = mkmat2(
151
mkcol2(diviiexact(y,d),negi(diviiexact(x,d))),
152
mkcol2(u,v));
153
return mkvec2(d,m);
154
}
155
156
static int
157
_Fp_equal0(GEN x) { return !signe(x); }
158
159
static int
160
_Fp_equal1(GEN x) { return equali1(x); }
161
162
static GEN
163
_Fp_s(void *data, long x)
164
{
165
if (!x) return gen_0;
166
if (x==1) return gen_1;
167
return modsi(x,(GEN)data);
168
}
169
170
static GEN
171
_Fp_red(void *data, GEN x) { return Fp_red(x, (GEN)data); }
172
173
/* p not necessarily prime */
174
static const struct bb_hermite Fp_hermite=
175
{_Fp_add,_Fp_neg,_Fp_mul,_Fp_extgcd,_Fp_rann,_Fp_lquo,_Fp_unit,_Fp_equal0,_Fp_equal1,_Fp_s,_Fp_red};
176
177
static const struct bb_hermite*
178
get_Fp_hermite(void **data, GEN p)
179
{
180
*data = (void*)p; return &Fp_hermite;
181
}
182
183
static void
184
gen_redcol(GEN C, long lim, void* data, const struct bb_hermite *R)
185
{
186
long i;
187
for (i=1; i<=lim; i++)
188
if (!R->equal0(gel(C,i)))
189
gel(C,i) = R->red(data, gel(C,i));
190
}
191
192
static GEN
193
/* return NULL if a==0 */
194
/* assume C*a is zero after lim */
195
gen_rightmulcol(GEN C, GEN a, long lim, int fillzeros, void* data, const struct bb_hermite *R)
196
{
197
GEN Ca,zero;
198
long i;
199
if (R->equal1(a)) return C;
200
if (R->equal0(a)) return NULL;
201
Ca = cgetg(lg(C),t_COL);
202
for (i=1; i<=lim; i++)
203
gel(Ca,i) = R->mul(data, gel(C,i), a);
204
if (fillzeros && lim+1 < lg(C))
205
{
206
zero = R->s(data,0);
207
for (i=lim+1; i<lg(C); i++)
208
gel(Ca,i) = zero;
209
}
210
return Ca;
211
}
212
213
static void
214
/* C1 <- C1 + C2 */
215
/* assume C2[i]==0 for i>lim */
216
gen_addcol(GEN C1, GEN C2, long lim, void* data, const struct bb_hermite *R)
217
{
218
long i;
219
for (i=1; i<=lim; i++)
220
gel(C1,i) = R->add(data, gel(C1,i), gel(C2,i));
221
}
222
223
static void
224
/* H[,i] <- H[,i] + C*a */
225
/* assume C is zero after lim */
226
gen_addrightmul(GEN H, GEN C, GEN a, long i, long lim, void* data, const struct bb_hermite *R)
227
{
228
GEN Ca;
229
if (R->equal0(a)) return;
230
Ca = gen_rightmulcol(C, a, lim, 0, data, R);
231
gen_addcol(gel(H,i), Ca, lim, data, R);
232
}
233
234
static GEN
235
gen_zerocol(long n, void* data, const struct bb_hermite *R)
236
{
237
GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
238
long i;
239
for (i=1; i<=n; i++) gel(C,i) = zero;
240
return C;
241
}
242
243
static GEN
244
gen_zeromat(long m, long n, void* data, const struct bb_hermite *R)
245
{
246
GEN M = cgetg(n+1,t_MAT);
247
long i;
248
for (i=1; i<=n; i++) gel(M,i) = gen_zerocol(m, data, R);
249
return M;
250
}
251
252
static GEN
253
gen_colei(long n, long i, void* data, const struct bb_hermite *R)
254
{
255
GEN C = cgetg(n+1,t_COL), zero = R->s(data, 0);
256
long j;
257
for (j=1; j<=n; j++)
258
if (i!=j) gel(C,j) = zero;
259
else gel(C,j) = R->s(data,1);
260
return C;
261
}
262
263
static GEN
264
gen_matid_hermite(long n, void* data, const struct bb_hermite *R)
265
{
266
GEN M = cgetg(n+1,t_MAT);
267
long i;
268
for (i=1; i<=n; i++) gel(M,i) = gen_colei(n, i, data, R);
269
return M;
270
}
271
272
static GEN
273
gen_matmul_hermite(GEN A, GEN B, void* data, const struct bb_hermite *R)
274
{
275
GEN M,sum,prod,zero = R->s(data,0);
276
long a,b,c,c2,i,j,k;
277
RgM_dimensions(A,&a,&c);
278
RgM_dimensions(B,&c2,&b);
279
if (c!=c2) pari_err_DIM("gen_matmul_hermite");
280
M = cgetg(b+1,t_MAT);
281
for (j=1; j<=b; j++)
282
{
283
gel(M,j) = cgetg(a+1,t_COL);
284
for (i=1; i<=a; i++)
285
{
286
sum = zero;
287
for (k=1; k<=c; k++)
288
{
289
prod = R->mul(data, gcoeff(A,i,k), gcoeff(B,k,j));
290
sum = R->add(data, sum, prod);
291
}
292
gcoeff(M,i,j) = sum;
293
}
294
gen_redcol(gel(M,j), a, data, R);
295
}
296
return M;
297
}
298
299
static void
300
/* U = [u1,u2]~, C <- A*u1 + B*u2 */
301
/* assume both A, B and C are zero after lim */
302
gen_rightlincomb(GEN A, GEN B, GEN U, GEN *C, long lim, void* data, const struct bb_hermite *R)
303
{
304
GEN Au1, Bu2;
305
Au1 = gen_rightmulcol(A, gel(U,1), lim, 1, data, R);
306
Bu2 = gen_rightmulcol(B, gel(U,2), lim, 1, data, R);
307
if (!Au1 && !Bu2) { *C = gen_zerocol(lg(A)-1, data, R); return; }
308
if (!Au1) { *C = Bu2; return; }
309
if (!Bu2) { *C = Au1; return; }
310
gen_addcol(Au1, Bu2, lim, data, R);
311
*C = Au1;
312
}
313
314
static void
315
/* (H[,i] | H[,j]) <- (H[,i] | H[,j]) * U */
316
/* assume both columns are zero after lim */
317
gen_elem(GEN H, GEN U, long i, long j, long lim, void* data, const struct bb_hermite *R)
318
{
319
GEN Hi, Hj;
320
Hi = shallowcopy(gel(H,i));
321
Hj = shallowcopy(gel(H,j));
322
gen_rightlincomb(Hi, Hj, gel(U,1), &gel(H,i), lim, data, R);
323
gen_rightlincomb(Hi, Hj, gel(U,2), &gel(H,j), lim, data, R);
324
}
325
326
static int
327
/* assume C is zero after lim */
328
gen_is_zerocol(GEN C, long lim, void* data, const struct bb_hermite *R)
329
{
330
long i;
331
(void) data;
332
for (i=1; i<=lim; i++)
333
if (!R->equal0(gel(C,i))) return 0;
334
return 1;
335
}
336
337
/* The mkop* functions return NULL if the corresponding operation is the identity */
338
339
static GEN
340
/* Ci <- Ci + Cj*a */
341
mkoptransv(long i, long j, GEN a, void* data, const struct bb_hermite *R)
342
{
343
a = R->red(data,a);
344
if (R->equal0(a)) return NULL;
345
return mkvec2(mkvecsmall2(i,j),a);
346
}
347
348
static GEN
349
/* (Ci|Cj) <- (Ci|Cj)*U */
350
mkopU(long i, long j, GEN U, void* data, const struct bb_hermite *R)
351
{
352
if (R->equal1(gcoeff(U,1,1)) && R->equal0(gcoeff(U,1,2))
353
&& R->equal1(gcoeff(U,2,2))) return mkoptransv(i,j,gcoeff(U,2,1),data,R);
354
return mkvec2(mkvecsmall3(i,j,0),U);
355
}
356
357
static GEN
358
/* Ci <- Ci*u */
359
mkopmul(long i, GEN u, const struct bb_hermite *R)
360
{
361
if (R->equal1(u)) return NULL;
362
return mkvec2(mkvecsmall(i),u);
363
}
364
365
static GEN
366
/* Ci <-> Cj */
367
mkopswap(long i, long j)
368
{
369
return mkvec(mkvecsmall2(i,j));
370
}
371
372
/* M: t_MAT. Apply the operation op to M by right multiplication. */
373
static void
374
gen_rightapply(GEN M, GEN op, void* data, const struct bb_hermite *R)
375
{
376
GEN M2, ind, X;
377
long i, j, m = lg(gel(M,1))-1;
378
switch (typ(op))
379
{
380
case t_VECSMALL:
381
M2 = vecpermute(M,op);
382
for (i=1; i<lg(M); i++) gel(M,i) = gel(M2,i);
383
return;
384
case t_VEC:
385
ind = gel(op,1);
386
switch (lg(op))
387
{
388
case 2:
389
swap(gel(M,ind[1]),gel(M,ind[2]));
390
return;
391
case 3:
392
X = gel(op,2);
393
i = ind[1];
394
switch (lg(ind))
395
{
396
case 2:
397
gel(M,i) = gen_rightmulcol(gel(M,i), X, m, 0, data, R);
398
gen_redcol(gel(M,i), m, data, R);
399
return;
400
case 3:
401
gen_addrightmul(M, gel(M,ind[2]), X, i, m, data, R);
402
gen_redcol(gel(M,i), m, data, R);
403
return;
404
case 4:
405
j = ind[2];
406
gen_elem(M, X, i, j, m, data, R);
407
gen_redcol(gel(M,i), m, data, R);
408
gen_redcol(gel(M,j), m, data, R);
409
return;
410
}
411
}
412
}
413
}
414
415
/* C: t_COL. Apply the operation op to C by left multiplication. */
416
static void
417
gen_leftapply(GEN C, GEN op, void* data, const struct bb_hermite *R)
418
{
419
GEN C2, ind, X;
420
long i, j;
421
switch (typ(op))
422
{
423
case t_VECSMALL:
424
C2 = vecpermute(C,perm_inv(op));
425
for (i=1; i<lg(C); i++) gel(C,i) = gel(C2,i);
426
return;
427
case t_VEC:
428
ind = gel(op,1);
429
switch (lg(op))
430
{
431
case 2:
432
swap(gel(C,ind[1]),gel(C,ind[2]));
433
return;
434
case 3:
435
X = gel(op,2);
436
i = ind[1];
437
switch (lg(ind))
438
{
439
case 2:
440
gel(C,i) = R->mul(data, X, gel(C,i));
441
gel(C,i) = R->red(data, gel(C,i));
442
return;
443
case 3:
444
j = ind[2];
445
if (R->equal0(gel(C,i))) return;
446
gel(C,j) = R->add(data, gel(C,j), R->mul(data, X, gel(C,i)));
447
return;
448
case 4:
449
j = ind[2];
450
C2 = gen_matmul_hermite(X, mkmat(mkcol2(gel(C,i),gel(C,j))), data, R);
451
gel(C,i) = gcoeff(C2,1,1);
452
gel(C,j) = gcoeff(C2,2,1);
453
return;
454
}
455
}
456
}
457
}
458
459
/* \prod_i det ops[i]. Only makes sense if R is commutative. */
460
static GEN
461
gen_detops(GEN ops, void* data, const struct bb_hermite *R)
462
{
463
GEN d = R->s(data,1);
464
long i, l = lg(ops);
465
for (i = 1; i < l; i++)
466
{
467
GEN X, op = gel(ops,i);
468
switch (typ(op))
469
{
470
case t_VECSMALL:
471
if (perm_sign(op) < 0) d = R->neg(data,d);
472
break;
473
case t_VEC:
474
switch (lg(op))
475
{
476
case 2:
477
d = R->neg(data,d);
478
break;
479
case 3:
480
X = gel(op,2);
481
switch (lg(gel(op,1)))
482
{
483
case 2:
484
d = R->mul(data, d, X);
485
d = R->red(data, d);
486
break;
487
case 4:
488
{
489
GEN A = gcoeff(X,1,1), B = gcoeff(X,1,2);
490
GEN C = gcoeff(X,2,1), D = gcoeff(X,2,2);
491
GEN AD = R->mul(data,A,D);
492
GEN BC = R->mul(data,B,C);
493
d = R->mul(data, d, R->add(data, AD, R->neg(data,BC)));
494
d = R->red(data, d);
495
break;
496
}
497
}
498
break;
499
}
500
break;
501
}
502
}
503
return d;
504
}
505
506
static int
507
gen_is_inv(GEN x, void* data, const struct bb_hermite *R)
508
{
509
GEN u = R->unit(data, x);
510
if (!u) return R->equal1(x);
511
return R->equal1(gel(u,1));
512
}
513
514
static long
515
gen_last_inv_diago(GEN A, void* data, const struct bb_hermite *R)
516
{
517
long i,m,n,j;
518
RgM_dimensions(A,&m,&n);
519
for (i=1,j=n-m+1; i<=m; i++,j++)
520
if (!gen_is_inv(gcoeff(A,i,j),data,R)) return i-1;
521
return m;
522
}
523
524
static GEN
525
/* remove_zerocols: 0 none, 1 until square, 2 all */
526
/* early abort: if not right-invertible, abort, return NULL, and set ops to the
527
* noninvertible pivot */
528
gen_howell_i(GEN A, long remove_zerocols, long permute_zerocols, long early_abort, long only_triangular, GEN* ops, void *data, const struct bb_hermite *R)
529
{
530
pari_sp av = avma, av1;
531
GEN H,U,piv=gen_0,u,q,a,perm,iszero,C,zero=R->s(data,0),d,g,r,op,one=R->s(data,1);
532
long m,n,i,j,s,si,i2,si2,nbz,lim,extra,maxop=0,nbop=0,lastinv=0;
533
int smallop;
534
535
av1 = avma;
536
537
RgM_dimensions(A,&m,&n);
538
if (early_abort && n<m)
539
{
540
if (ops) *ops = zero;
541
return NULL;
542
}
543
if (n<m+1)
544
{
545
extra = m+1-n;
546
H = shallowmatconcat(mkvec2(gen_zeromat(m,extra,data,R),A));
547
}
548
else
549
{
550
extra = 0;
551
H = RgM_shallowcopy(A);
552
}
553
RgM_dimensions(H,&m,&n);
554
s = n-m; /* shift */
555
556
if(ops)
557
{
558
maxop = m*n + (m*(m+1))/2 + 1;
559
*ops = zerovec(maxop); /* filled with placeholders so gerepile can handle it */
560
}
561
562
/* put in triangular form */
563
for (i=m,si=s+m; i>0 && si>extra; i--,si--) /* si = s+i */
564
{
565
if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
566
/* bottom-right diagonal */
567
for (j = extra+1; j < si; j++)
568
{
569
if (R->red) gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
570
if (R->equal0(gcoeff(H,i,j))) continue;
571
U = R->extgcd(data, gcoeff(H,i,j), gcoeff(H,i,si), &smallop);
572
d = gel(U,1);
573
U = gel(U,2);
574
if (n>10)
575
{
576
/* normalize diagonal coefficient -> faster reductions on this row */
577
u = R->unit(data, d);
578
if (u)
579
{
580
g = gel(u,1);
581
u = gel(u,2);
582
gcoeff(U,1,2) = R->mul(data, gcoeff(U,1,2), u);
583
gcoeff(U,2,2) = R->mul(data, gcoeff(U,2,2), u);
584
d = g;
585
}
586
}
587
gen_elem(H, U, j, si, i-1, data, R);
588
if (ops)
589
{
590
op = mkopU(j,si,U,data,R);
591
if (op) { nbop++; gel(*ops, nbop) = op; }
592
}
593
gcoeff(H,i,j) = zero;
594
gcoeff(H,i,si) = d;
595
if (R->red && !smallop)
596
{
597
gen_redcol(gel(H,si), i-1, data, R);
598
gen_redcol(gel(H,j), i-1, data, R);
599
}
600
}
601
602
if (early_abort)
603
{
604
d = gcoeff(H,i,si);
605
u = R->unit(data, d);
606
if (u) d = gel(u,1);
607
if (!R->equal1(d))
608
{
609
if (ops) *ops = d;
610
return NULL;
611
}
612
}
613
614
if (gc_needed(av,1))
615
{
616
if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[1]. i=%ld",i);
617
gerepileall(av1,ops?2:1,&H,ops);
618
}
619
}
620
621
if (!ops)
622
lastinv = gen_last_inv_diago(H, data, R);
623
624
/* put in reduced Howell form */
625
if (!only_triangular)
626
{
627
for (i=m,si=s+m; i>0; i--,si--) /* si = s+i */
628
{
629
/* normalize diagonal coefficient */
630
if (i<=lastinv) /* lastinv>0 => !ops */
631
gcoeff(H,i,si) = one;
632
else
633
{
634
u = R->unit(data,gcoeff(H,i,si));
635
if (u)
636
{
637
g = gel(u,1);
638
u = gel(u,2);
639
gel(H,si) = gen_rightmulcol(gel(H,si), u, i-1, 1, data, R);
640
gcoeff(H,i,si) = g;
641
if (R->red) gen_redcol(gel(H,si), i-1, data, R);
642
if (ops)
643
{
644
op = mkopmul(si,u,R);
645
if (op) { nbop++; gel(*ops,nbop) = op; }
646
}
647
}
648
else if (R->red) gcoeff(H,i,si) = R->red(data, gcoeff(H,i,si));
649
}
650
piv = gcoeff(H,i,si);
651
652
/* reduce above diagonal */
653
if (!R->equal0(piv))
654
{
655
C = gel(H,si);
656
for (j=si+1; j<=n; j++)
657
{
658
if (i<=lastinv) /* lastinv>0 => !ops */
659
gcoeff(H,i,j) = zero;
660
else
661
{
662
gcoeff(H,i,j) = R->red(data, gcoeff(H,i,j));
663
if (R->equal1(piv)) { q = gcoeff(H,i,j); r = zero; }
664
else q = R->lquo(data, gcoeff(H,i,j), piv, &r);
665
q = R->neg(data,q);
666
gen_addrightmul(H, C, q, j, i-1, data, R);
667
if (ops)
668
{
669
op = mkoptransv(j,si,q,data,R);
670
if (op) { nbop++; gel(*ops,nbop) = op; }
671
}
672
gcoeff(H,i,j) = r;
673
}
674
}
675
}
676
677
/* ensure Howell property */
678
if (i>1)
679
{
680
a = R->rann(data, piv);
681
if (!R->equal0(a))
682
{
683
gel(H,1) = gen_rightmulcol(gel(H,si), a, i-1, 1, data, R);
684
if (gel(H,1) == gel(H,si)) gel(H,1) = shallowcopy(gel(H,1)); /* in case rightmulcol cheated */
685
if (ops)
686
{
687
op = mkoptransv(1,si,a,data,R);
688
if (op) { nbop++; gel(*ops,nbop) = op; }
689
}
690
for (i2=i-1,si2=s+i2; i2>0; i2--,si2--)
691
{
692
if (R->red) gcoeff(H,i2,1) = R->red(data, gcoeff(H,i2,1));
693
if (R->equal0(gcoeff(H,i2,1))) continue;
694
if (R->red) gcoeff(H,i2,si2) = R->red(data, gcoeff(H,i2,si2));
695
if (R->equal0(gcoeff(H,i2,si2)))
696
{
697
swap(gel(H,1), gel(H,si2));
698
if (ops) { nbop++; gel(*ops,nbop) = mkopswap(1,si2); }
699
continue;
700
}
701
U = R->extgcd(data, gcoeff(H,i2,1), gcoeff(H,i2,si2), &smallop);
702
d = gel(U,1);
703
U = gel(U,2);
704
gen_elem(H, U, 1, si2, i2-1, data, R);
705
if (ops)
706
{
707
op = mkopU(1,si2,U,data,R);
708
if (op) { nbop++; gel(*ops,nbop) = op; }
709
}
710
gcoeff(H,i2,1) = zero;
711
gcoeff(H,i2,si2) = d;
712
if (R->red && !smallop)
713
{
714
gen_redcol(gel(H,si2), i2, data, R);
715
gen_redcol(gel(H,1), i2-1, data, R);
716
}
717
}
718
}
719
}
720
721
if (gc_needed(av,1))
722
{
723
if (DEBUGMEM>1) pari_warn(warnmem,"gen_howell[2]. i=%ld",i);
724
gerepileall(av1,ops?3:2,&H,&piv,ops);
725
}
726
}
727
}
728
729
if (R->red)
730
for (j=1; j<=n; j++)
731
{
732
lim = maxss(0,m-n+j);
733
gen_redcol(gel(H,j), lim, data, R);
734
for (i=lim+1; i<=m; i++) gcoeff(H,i,j) = zero;
735
}
736
737
/* put zero columns first */
738
iszero = cgetg(n+1,t_VECSMALL);
739
740
nbz = 0;
741
for (i=1; i<=n; i++)
742
{
743
iszero[i] = gen_is_zerocol(gel(H,i), maxss(0,m-n+i), data, R);
744
if (iszero[i]) nbz++;
745
}
746
747
j = 1;
748
if (permute_zerocols)
749
{
750
perm = cgetg(n+1, t_VECSMALL);
751
for (i=1; i<=n; i++)
752
if (iszero[i])
753
{
754
perm[j] = i;
755
j++;
756
}
757
}
758
else perm = cgetg(n-nbz+1, t_VECSMALL);
759
for (i=1; i<=n; i++)
760
if (!iszero[i])
761
{
762
perm[j] = i;
763
j++;
764
}
765
766
if (permute_zerocols || remove_zerocols==2) H = vecpermute(H, perm);
767
if (permute_zerocols && remove_zerocols==2) H = vecslice(H, nbz+1, n);
768
if (remove_zerocols==1) H = vecslice(H, s+1, n);
769
if (permute_zerocols && ops) { nbop++; gel(*ops,nbop) = perm; }
770
771
if (ops) { setlg(*ops, nbop+1); } /* should have nbop <= maxop */
772
773
return H;
774
}
775
776
static GEN
777
gen_howell(GEN A, long remove_zerocols, long permute_zerocols, long early_abort, long only_triangular, GEN* ops, void *data, const struct bb_hermite *R)
778
{
779
pari_sp av = avma;
780
GEN H = gen_howell_i(A, remove_zerocols, permute_zerocols, early_abort, only_triangular, ops, data, R);
781
gerepileall(av, ops?2:1, &H, ops);
782
return H;
783
}
784
785
static GEN
786
gen_matimage(GEN A, GEN* U, void *data, const struct bb_hermite *R)
787
{
788
GEN ops, H;
789
if (U)
790
{
791
pari_sp av = avma, av1;
792
long m, n, i, r, n2, pergc;
793
RgM_dimensions(A,&m,&n);
794
H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
795
av1 = avma;
796
r = lg(H)-1;
797
*U = shallowmatconcat(mkvec2(gen_zeromat(n, maxss(0,m-n+1), data, R), gen_matid_hermite(n, data, R)));
798
n2 = lg(*U)-1;
799
pergc = maxss(m,n);
800
for (i=1; i<lg(ops); i++)
801
{
802
gen_rightapply(*U, gel(ops,i), data, R);
803
if (!(i%pergc) && gc_needed(av,1))
804
{
805
if (DEBUGMEM>1) pari_warn(warnmem,"gen_matimage. i=%ld",i);
806
gerepileall(av1,1,U);
807
}
808
}
809
if (r<n2) *U = vecslice(*U, n2-r+1, n2);
810
gerepileall(av, 2, &H, U);
811
return H;
812
}
813
else return gen_howell(A, 2, 0, 0, 0, NULL, data, R);
814
}
815
816
static GEN
817
/* H in true Howell form: no zero columns */
818
gen_kernel_howell(GEN H, void *data, const struct bb_hermite *R)
819
{
820
GEN K, piv, FK;
821
long m, n, j, j2, i;
822
RgM_dimensions(H,&m,&n);
823
K = gen_zeromat(n, n, data, R);
824
for (j=n,i=m; j>0; j--)
825
{
826
while (R->equal0(gcoeff(H,i,j))) i--;
827
piv = gcoeff(H,i,j);
828
if (R->equal0(piv)) continue;
829
gcoeff(K,j,j) = R->rann(data, piv);
830
if (j<n)
831
{
832
FK = gen_matmul_hermite(matslice(H,i,i,j+1,n), matslice(K, j+1, n, j+1, n), data, R);
833
for (j2=j+1; j2<=n; j2++)
834
gcoeff(K,j,j2) = R->neg(data, R->lquo(data, gcoeff(FK,1,j2-j), piv, NULL));
835
/* remainder has to be zero */
836
}
837
}
838
return K;
839
}
840
841
static GEN
842
/* (H,ops) Howell form of A, n = number of columns of A, return a kernel of A */
843
gen_kernel_from_howell(GEN H, GEN ops, long n, void *data, const struct bb_hermite *R)
844
{
845
pari_sp av;
846
GEN K, KH, zC;
847
long m, r, n2, nbz, i, o, extra, j;
848
RgM_dimensions(H,&m,&r);
849
if (!r) return gen_matid_hermite(n, data, R); /* zerology: what if 0==1 in R? */
850
n2 = maxss(n,m+1);
851
extra = n2-n;
852
nbz = n2-r;
853
/* compute kernel of augmented matrix */
854
KH = gen_kernel_howell(H, data, R);
855
zC = gen_zerocol(nbz, data, R);
856
K = cgetg(nbz+r+1, t_MAT);
857
for (i=1; i<=nbz; i++)
858
gel(K,i) = gen_colei(nbz+r, i, data, R);
859
for (i=1; i<=r; i++)
860
gel(K,nbz+i) = shallowconcat(zC, gel(KH,i));
861
for (i=1; i<lg(K); i++)
862
{
863
av = avma;
864
for (o=lg(ops)-1; o>0; o--)
865
gen_leftapply(gel(K,i), gel(ops,o), data, R);
866
gen_redcol(gel(K,i), nbz+r, data, R);
867
gerepileall(av, 1, &gel(K,i));
868
}
869
/* deduce kernel of original matrix */
870
K = rowpermute(K, cyclic_perm(n2,extra));
871
K = gen_howell_i(K, 2, 0, 0, 0, NULL, data, R);
872
for (j=lg(K)-1, i=n2; j>0; j--)
873
{
874
while (R->equal0(gcoeff(K,i,j))) i--;
875
if (i<=n) return matslice(K, 1, n, 1, j);
876
}
877
return cgetg(1,t_MAT);
878
}
879
880
/* not GC-clean */
881
static GEN
882
gen_kernel(GEN A, GEN* im, void *data, const struct bb_hermite *R)
883
{
884
pari_sp av = avma;
885
long n = lg(A)-1;
886
GEN H, ops, K;
887
H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
888
gerepileall(av,2,&H,&ops);
889
K = gen_kernel_from_howell(H, ops, n, data, R);
890
if (im) *im = H;
891
return K;
892
}
893
894
/* right inverse, not GC-clean */
895
static GEN
896
gen_inv(GEN A, void* data, const struct bb_hermite *R)
897
{
898
pari_sp av;
899
GEN ops, H, U, un=R->s(data,1);
900
long m,n,j,o,n2;
901
RgM_dimensions(A,&m,&n);
902
av = avma;
903
H = gen_howell_i(A, 0, 0, 1, 0, &ops, data, R);
904
if (!H) pari_err_INV("gen_inv", ops);
905
n2 = lg(H)-1;
906
ops = gerepilecopy(av,ops); /* get rid of H */
907
U = gen_zeromat(n2, m, data, R);
908
for (j=1; j<=m; j++)
909
gcoeff(U,j+n2-m,j) = un;
910
for (j=1; j<=m; j++)
911
{
912
av = avma;
913
for (o=lg(ops)-1; o>0; o--)
914
gen_leftapply(gel(U,j), gel(ops,o), data, R);
915
gen_redcol(gel(U,j), n2, data, R);
916
gerepileall(av, 1, &gel(U,j));
917
}
918
if (n2>n) U = rowslice(U, n2-n+1, n2);
919
return U;
920
}
921
922
/*
923
H true Howell form (no zero columns).
924
Compute Z = Y - HX canonical representative of R^m mod H(R^n)
925
*/
926
static GEN
927
gen_reduce_mod_howell(GEN H, GEN Y, GEN *X, void* data, const struct bb_hermite *R)
928
{
929
pari_sp av = avma;
930
long m,n,i,j;
931
GEN Z, q, r, C;
932
RgM_dimensions(H,&m,&n);
933
if (X) *X = gen_zerocol(n,data,R);
934
Z = shallowcopy(Y);
935
i = m;
936
for (j=n; j>0; j--)
937
{
938
while (R->equal0(gcoeff(H,i,j))) i--;
939
q = R->lquo(data, gel(Z,i), gcoeff(H,i,j), &r);
940
gel(Z,i) = r;
941
C = gen_rightmulcol(gel(H,j), R->neg(data,q), i, 0, data, R);
942
if (C) gen_addcol(Z, C, i-1, data, R);
943
if (X) gel(*X,j) = q;
944
}
945
gen_redcol(Z, lg(Z)-1, data, R);
946
if (X)
947
{
948
gerepileall(av, 2, &Z, X);
949
return Z;
950
}
951
return gerepilecopy(av, Z);
952
}
953
954
/* H: Howell form of A */
955
/* (m,n): dimensions of original matrix A */
956
/* return NULL if no solution */
957
static GEN
958
gen_solve_from_howell(GEN H, GEN ops, long m, long n, GEN Y, void* data, const struct bb_hermite *R)
959
{
960
pari_sp av = avma;
961
GEN Z, X;
962
long n2, mH, nH, i;
963
RgM_dimensions(H,&mH,&nH);
964
n2 = maxss(n,m+1);
965
Z = gen_reduce_mod_howell(H, Y, &X, data, R);
966
if (!gen_is_zerocol(Z,m,data,R)) { set_avma(av); return NULL; }
967
968
X = shallowconcat(zerocol(n2-nH),X);
969
for (i=lg(ops)-1; i>0; i--) gen_leftapply(X, gel(ops,i), data, R);
970
X = vecslice(X, n2-n+1, n2);
971
gen_redcol(X, n, data, R);
972
return gerepilecopy(av, X);
973
}
974
975
/* return NULL if no solution, not GC-clean */
976
static GEN
977
gen_solve(GEN A, GEN Y, GEN* K, void* data, const struct bb_hermite *R)
978
{
979
GEN H, ops, X;
980
long m,n;
981
982
RgM_dimensions(A,&m,&n);
983
if (!n) m = lg(Y)-1;
984
H = gen_howell_i(A, 2, 1, 0, 0, &ops, data, R);
985
X = gen_solve_from_howell(H, ops, m, n, Y, data, R);
986
if (!X) return NULL;
987
if (K) *K = gen_kernel_from_howell(H, ops, n, data, R);
988
return X;
989
}
990
991
GEN
992
matimagemod(GEN A, GEN d, GEN* U)
993
{
994
void *data;
995
const struct bb_hermite* R;
996
if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matimagemod", A);
997
if (typ(d)!=t_INT) pari_err_TYPE("matimagemod", d);
998
if (signe(d)<=0) pari_err_DOMAIN("matimagemod", "d", "<=", gen_0, d);
999
if (equali1(d)) return cgetg(1,t_MAT);
1000
R = get_Fp_hermite(&data, d);
1001
return gen_matimage(A, U, data, R);
1002
}
1003
1004
/* for testing purpose */
1005
/*
1006
GEN
1007
ZM_hnfmodid2(GEN A, GEN d)
1008
{
1009
pari_sp av = avma;
1010
void *data;
1011
long i;
1012
const struct bb_hermite* R = get_Fp_hermite(&data, d);
1013
GEN H;
1014
if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("ZM_hnfmodid2", A);
1015
if (typ(d)!=t_INT) pari_err_TYPE("ZM_hnfmodid2", d);
1016
H = gen_howell_i(A, 1, 0, 0, 0, NULL, data, R);
1017
for (i=1; i<lg(H); i++)
1018
if (!signe(gcoeff(H,i,i))) gcoeff(H,i,i) = d;
1019
return gerepilecopy(av,H);
1020
}
1021
*/
1022
1023
GEN
1024
matdetmod(GEN A, GEN d)
1025
{
1026
pari_sp av = avma;
1027
void *data;
1028
const struct bb_hermite* R;
1029
long n = lg(A)-1, i;
1030
GEN D, H, ops;
1031
if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matdetmod", A);
1032
if (typ(d)!=t_INT) pari_err_TYPE("matdetmod", d);
1033
if (signe(d)<=0) pari_err_DOMAIN("matdetmod", "d", "<=", gen_0, d);
1034
if (!n) return equali1(d) ? gen_0 : gen_1;
1035
if (n != nbrows(A)) pari_err_DIM("matdetmod");
1036
if (equali1(d)) return gen_0;
1037
R = get_Fp_hermite(&data, d);
1038
H = gen_howell_i(A, 1, 0, 0, 1, &ops, data, R);
1039
D = gen_detops(ops, data, R);
1040
D = Fp_inv(D, d);
1041
for (i = 1; i <= n; i++) D = Fp_mul(D, gcoeff(H,i,i), d);
1042
return gerepileuptoint(av, D);
1043
}
1044
1045
GEN
1046
matkermod(GEN A, GEN d, GEN* im)
1047
{
1048
pari_sp av = avma;
1049
void *data;
1050
const struct bb_hermite* R;
1051
long m,n;
1052
GEN K;
1053
if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matkermod", A);
1054
if (typ(d)!=t_INT) pari_err_TYPE("matkermod", d);
1055
if (signe(d)<=0) pari_err_DOMAIN("makermod", "d", "<=", gen_0, d);
1056
if (equali1(d)) return cgetg(1,t_MAT);
1057
R = get_Fp_hermite(&data, d);
1058
RgM_dimensions(A,&m,&n);
1059
if (!im && m>2*n) /* TODO tune treshold */
1060
A = shallowtrans(matimagemod(shallowtrans(A),d,NULL));
1061
K = gen_kernel(A, im, data, R);
1062
gerepileall(av,im?2:1,&K,im);
1063
return K;
1064
}
1065
1066
/* left inverse */
1067
GEN
1068
matinvmod(GEN A, GEN d)
1069
{
1070
pari_sp av = avma;
1071
void *data;
1072
const struct bb_hermite* R;
1073
GEN U;
1074
if (typ(A)!=t_MAT || !RgM_is_ZM(A)) pari_err_TYPE("matinvmod", A);
1075
if (typ(d)!=t_INT) pari_err_TYPE("matinvmod", d);
1076
if (signe(d)<=0) pari_err_DOMAIN("matinvmod", "d", "<=", gen_0, d);
1077
if (equali1(d)) {
1078
long m,n;
1079
RgM_dimensions(A,&m,&n);
1080
if (m<n) pari_err_INV("matinvmod",A);
1081
return zeromatcopy(n,m);
1082
}
1083
R = get_Fp_hermite(&data, d);
1084
U = gen_inv(shallowtrans(A), data, R);
1085
return gerepilecopy(av, shallowtrans(U));
1086
}
1087
1088
/* assume all D[i]>0, not GC-clean */
1089
static GEN
1090
matsolvemod_finite(GEN M, GEN D, GEN Y, long flag)
1091
{
1092
void *data;
1093
const struct bb_hermite* R;
1094
GEN X, K, d;
1095
long m, n;
1096
1097
RgM_dimensions(M,&m,&n);
1098
if (typ(D)==t_COL)
1099
{ /* create extra variables for the D[i] */
1100
GEN MD;
1101
long i, i2, extra = 0;
1102
d = gen_1;
1103
for (i=1; i<lg(D); i++) d = lcmii(d,gel(D,i));
1104
for (i=1; i<lg(D); i++) if (!equalii(gel(D,i),d)) extra++;
1105
MD = cgetg(extra+1,t_MAT);
1106
i2 = 1;
1107
for (i=1; i<lg(D); i++)
1108
if (!equalii(gel(D,i),d))
1109
{
1110
gel(MD,i2) = Rg_col_ei(gel(D,i),m,i);
1111
i2++;
1112
}
1113
M = shallowconcat(M,MD);
1114
}
1115
else d = D;
1116
1117
R = get_Fp_hermite(&data, d);
1118
X = gen_solve(M, Y, flag? &K: NULL, data, R);
1119
if (!X) return gen_0;
1120
X = vecslice(X,1,n);
1121
1122
if (flag)
1123
{
1124
K = rowslice(K,1,n);
1125
K = hnfmodid(shallowconcat(zerocol(n),K),d);
1126
X = mkvec2(X,K);
1127
}
1128
return X;
1129
}
1130
1131
/* Return a solution of congruence system sum M[i,j] X_j = Y_i mod D_i
1132
* If pU != NULL, put in *pU a Z-basis of the homogeneous system.
1133
* Works for all non negative D_i but inefficient compared to
1134
* matsolvemod_finite; to be used only when one D_i is 0 */
1135
static GEN
1136
gaussmoduloall(GEN M, GEN D, GEN Y, GEN *pU)
1137
{
1138
pari_sp av = avma;
1139
long n, m, j, l, lM = lg(M);
1140
GEN delta, H, U, u1, u2, x;
1141
1142
if (lM == 1)
1143
{
1144
long lY = 0;
1145
switch(typ(Y))
1146
{
1147
case t_INT: break;
1148
case t_COL: lY = lg(Y); break;
1149
default: pari_err_TYPE("gaussmodulo",Y);
1150
}
1151
switch(typ(D))
1152
{
1153
case t_INT: break;
1154
case t_COL: if (lY && lY != lg(D)) pari_err_DIM("gaussmodulo");
1155
break;
1156
default: pari_err_TYPE("gaussmodulo",D);
1157
}
1158
if (pU) *pU = cgetg(1, t_MAT);
1159
return cgetg(1,t_COL);
1160
}
1161
n = nbrows(M);
1162
switch(typ(D))
1163
{
1164
case t_COL:
1165
if (lg(D)-1!=n) pari_err_DIM("gaussmodulo");
1166
delta = diagonal_shallow(D); break;
1167
case t_INT: delta = scalarmat_shallow(D,n); break;
1168
default: pari_err_TYPE("gaussmodulo",D);
1169
return NULL; /* LCOV_EXCL_LINE */
1170
}
1171
switch(typ(Y))
1172
{
1173
case t_INT: Y = const_col(n, Y); break;
1174
case t_COL:
1175
if (lg(Y)-1!=n) pari_err_DIM("gaussmodulo");
1176
break;
1177
default: pari_err_TYPE("gaussmodulo",Y);
1178
return NULL; /* LCOV_EXCL_LINE */
1179
}
1180
H = ZM_hnfall_i(shallowconcat(M,delta), &U, 1);
1181
Y = hnf_solve(H,Y); if (!Y) return gen_0;
1182
l = lg(H); /* may be smaller than lM if some moduli are 0 */
1183
n = l-1;
1184
m = lg(U)-1 - n;
1185
u1 = cgetg(m+1,t_MAT);
1186
u2 = cgetg(n+1,t_MAT);
1187
for (j=1; j<=m; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u1,j) = c; }
1188
U += m;
1189
for (j=1; j<=n; j++) { GEN c = gel(U,j); setlg(c,lM); gel(u2,j) = c; }
1190
/* (u1 u2)
1191
* (M D) (* * ) = (0 H) */
1192
u1 = ZM_lll(u1, 0.75, LLL_INPLACE);
1193
Y = ZM_ZC_mul(u2,Y);
1194
x = ZC_reducemodmatrix(Y, u1);
1195
if (!pU) x = gerepileupto(av, x);
1196
else
1197
{
1198
gerepileall(av, 2, &x, &u1);
1199
*pU = u1;
1200
}
1201
return x;
1202
}
1203
/* to be used when one D_i is 0 */
1204
static GEN
1205
msolvemod0(GEN M, GEN D, GEN Y, long flag)
1206
{
1207
pari_sp av = avma;
1208
GEN y, x, U;
1209
if (!flag) return gaussmoduloall(M,D,Y,NULL);
1210
y = cgetg(3,t_VEC);
1211
x = gaussmoduloall(M,D,Y,&U);
1212
if (x == gen_0) { set_avma(av); return gen_0; }
1213
gel(y,1) = x;
1214
gel(y,2) = U; return y;
1215
1216
}
1217
GEN
1218
matsolvemod(GEN M, GEN D, GEN Y, long flag)
1219
{
1220
pari_sp av = avma;
1221
long m, n, i, char0 = 0;
1222
if (typ(M)!=t_MAT || !RgM_is_ZM(M)) pari_err_TYPE("matsolvemod (M)",M);
1223
RgM_dimensions(M,&m,&n);
1224
if (typ(D)!=t_INT && (typ(D)!=t_COL || !RgV_is_ZV(D)))
1225
pari_err_TYPE("matsolvemod (D)",D);
1226
if (n)
1227
{ if (typ(D)==t_COL && lg(D)!=m+1) pari_err_DIM("matsolvemod [1]"); }
1228
else
1229
{ if (typ(D)==t_COL) m = lg(D)-1; }
1230
if (typ(Y)==t_INT)
1231
Y = const_col(m,Y);
1232
else if (typ(Y)!=t_COL || !RgV_is_ZV(Y)) pari_err_TYPE("matsolvemod (Y)",Y);
1233
if (!n && !m) m = lg(Y)-1;
1234
else if (m != lg(Y)-1) pari_err_DIM("matsolvemod [2]");
1235
if (typ(D)==t_INT)
1236
{
1237
if (signe(D)<0) pari_err_DOMAIN("matsolvemod","D","<",gen_0,D);
1238
if (!signe(D)) char0 = 1;
1239
}
1240
else /*typ(D)==t_COL*/
1241
for (i=1; i<=m; i++)
1242
{
1243
if (signe(gel(D,i))<0)
1244
pari_err_DOMAIN("matsolvemod","D[i]","<",gen_0,gel(D,i));
1245
if (!signe(gel(D,i))) char0 = 1;
1246
}
1247
return gerepilecopy(av, char0? msolvemod0(M,D,Y,flag)
1248
: matsolvemod_finite(M,D,Y,flag));
1249
}
1250
GEN
1251
gaussmodulo2(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 1); }
1252
GEN
1253
gaussmodulo(GEN M, GEN D, GEN Y) { return matsolvemod(M,D,Y, 0); }
1254
1255