Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/ext/multi_modular.pyx
4045 views
1
"""
2
Utility classes for multi-modular algorithms.
3
"""
4
5
######################################################################
6
# Copyright (C) 2006 William Stein
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
#
10
# The full text of the GPL is available at:
11
# http://www.gnu.org/licenses/
12
######################################################################
13
14
15
include "../ext/interrupt.pxi"
16
include "../ext/stdsage.pxi"
17
include "../ext/gmp.pxi"
18
19
20
from sage.rings.integer_ring import ZZ
21
from sage.rings.arith import random_prime
22
from types import GeneratorType
23
24
# should I have mod_int versions of these functions?
25
# c_inverse_mod_longlong modular inverse used exactly once in _refresh_precomputations
26
from sage.rings.fast_arith cimport arith_llong
27
cdef arith_llong ai
28
ai = arith_llong()
29
30
# We use both integer and double operations, hence the min.
31
# MAX_MODULUS = min(int(sqrt(int(MOD_INT_OVERFLOW))-1), int(2)**int(20))
32
33
# This is what matrix_modn_* supports.
34
MAX_MODULUS = 2**23
35
36
cdef class MultiModularBasis_base:
37
r"""
38
This class stores a list of machine-sized prime numbers,
39
and can do reduction and Chinese Remainder Theorem lifting
40
modulo these primes.
41
42
Lifting implemented via Garner's algorithm, which has the advantage
43
that all reductions are word-sized. For each $i$ precompute
44
45
$\prod_j=1^{i-1} m_j$ and $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$
46
47
48
This class can be initialized in two ways, either with a list of prime
49
moduli or an upper bound for the product of the prime moduli. The prime
50
moduli is generated automatically in the second case::
51
52
sage: from sage.ext.multi_modular import MultiModularBasis_base
53
sage: mm = MultiModularBasis_base([3, 5, 7]); mm
54
MultiModularBasis with moduli [3, 5, 7]
55
56
sage: height = 52348798724
57
sage: mm = MultiModularBasis_base(height); mm
58
MultiModularBasis with moduli [4561, 17351, 28499]
59
sage: mm = MultiModularBasis_base(height); mm
60
MultiModularBasis with moduli [32573, 4339, 30859]
61
sage: mm = MultiModularBasis_base(height); mm
62
MultiModularBasis with moduli [16451, 14323, 28631]
63
64
sage: mm.prod()//height
65
128
66
67
TESTS::
68
69
sage: mm = MultiModularBasis_base((3,5,7)); mm
70
MultiModularBasis with moduli [3, 5, 7]
71
sage: mm = MultiModularBasis_base(primes(10,20)); mm
72
MultiModularBasis with moduli [11, 13, 17, 19]
73
"""
74
75
def __cinit__(self):
76
r"""
77
Allocate the space for the moduli and precomputation lists
78
and initialize the first element of that list.
79
80
EXAMPLES::
81
82
sage: from sage.ext.multi_modular import MultiModularBasis_base
83
sage: mm = MultiModularBasis_base([1099511627791])
84
Traceback (most recent call last):
85
...
86
ValueError: given modulus cannot be manipulated in a single machine word
87
"""
88
mpz_init(self.product)
89
mpz_init(self.half_product)
90
91
cdef _realloc_to_new_count(self, new_count):
92
self.moduli = <mod_int*>sage_realloc(self.moduli, sizeof(mod_int)*new_count)
93
self.partial_products = <mpz_t*>sage_realloc(self.partial_products, sizeof(mpz_t)*new_count)
94
self.C = <mod_int*>sage_realloc(self.C, sizeof(mod_int)*new_count)
95
if self.moduli == NULL or self.partial_products == NULL or self.C == NULL:
96
raise MemoryError, "out of memory allocating multi-modular prime list"
97
98
def __dealloc__(self):
99
"""
100
sage: from sage.ext.multi_modular import MultiModularBasis_base
101
sage: mm = MultiModularBasis_base([1099511627791])
102
Traceback (most recent call last):
103
...
104
ValueError: given modulus cannot be manipulated in a single machine word
105
106
sage: mm = MultiModularBasis_base(1099511627791); mm
107
MultiModularBasis with moduli [4561, 17351, 28499]
108
sage: del mm
109
"""
110
sage_free(self.moduli)
111
for i from 0 <= i < self.n:
112
mpz_clear(self.partial_products[i])
113
sage_free(self.partial_products)
114
sage_free(self.C)
115
mpz_clear(self.product)
116
mpz_clear(self.half_product)
117
118
def __init__(self, val, l_bound=0, u_bound=0):
119
r"""
120
Initialize a multi-modular basis and perform precomputations.
121
122
INPUT:
123
124
- ``val`` - as integer
125
determines how many primes are computed
126
(their product will be at least 2*val)
127
as list, tuple or generator
128
a list of prime moduli to start with
129
- ``l_bound`` - an integer
130
lower bound for the random primes (default: 2^10)
131
- ``u_bound`` - an integer
132
upper bound for the random primes (default: 2^15)
133
134
135
EXAMPLES::
136
137
sage: from sage.ext.multi_modular import MultiModularBasis_base
138
sage: mm = MultiModularBasis_base([1009, 10007]); mm
139
MultiModularBasis with moduli [1009, 10007]
140
sage: mm.prod()
141
10097063
142
143
sage: height = 10097063
144
sage: mm = MultiModularBasis_base(height); mm
145
MultiModularBasis with moduli [...]
146
147
sage: mm.prod()//height > 2
148
True
149
150
sage: mm = MultiModularBasis_base([1000000000000000000000000000057])
151
Traceback (most recent call last):
152
...
153
ValueError: given modulus cannot be manipulated in a single machine word
154
155
sage: mm = MultiModularBasis_base(0); mm
156
MultiModularBasis with moduli [28499]
157
158
sage: mm = MultiModularBasis_base([6, 10])
159
Traceback (most recent call last):
160
...
161
ValueError: the values provided are not relatively prime
162
"""
163
if l_bound == 0:
164
self._l_bound = 2**10
165
elif l_bound < 2:
166
raise ValueError("minimum value for lower bound is 2, given: %s"%(l_bound))
167
else:
168
self._l_bound = l_bound
169
170
if u_bound == 0:
171
self._u_bound = 2**15
172
elif u_bound > MAX_MODULUS:
173
raise ValueError("upper bound cannot be greater than MAX_MODULUS: %s, given: %s"%(MAX_MODULUS, u_bound))
174
else:
175
self._u_bound = u_bound
176
177
from sage.functions.prime_pi import prime_pi # must be here to avoid circular import
178
self._num_primes = prime_pi(self._u_bound) - prime_pi(self._l_bound-1)
179
180
cdef int i
181
if isinstance(val, (list, tuple, GeneratorType)):
182
try:
183
self.extend_with_primes(val, check=True)
184
except ArithmeticError:
185
#See :trac:`10217`
186
raise ValueError("the values provided are not relatively prime")
187
else:
188
self._extend_moduli_to_height(val)
189
190
cdef mod_int _new_random_prime(self, set known_primes) except 1:
191
"""
192
Choose a new random prime for inclusion in the list of moduli,
193
or raise a RuntimeError if there are no more primes.
194
195
INPUT:
196
197
- `known_primes` -- Python set of already known primes in
198
the allowed interval; we will not return a prime in
199
known_primes.
200
"""
201
cdef Py_ssize_t i
202
cdef mod_int p
203
while True:
204
if len(known_primes) >= self._num_primes:
205
raise RuntimeError, "there are not enough primes in the interval [%s, %s] to complete this multimodular computation"%(self._l_bound, self._u_bound)
206
p = random_prime(self._u_bound, lbound =self._l_bound)
207
if p not in known_primes:
208
return p
209
210
def extend_with_primes(self, plist, partial_products = None, check=True):
211
"""
212
Extend the stored list of moduli with the given primes in `plist`.
213
214
EXAMPLES::
215
216
sage: from sage.ext.multi_modular import MultiModularBasis_base
217
sage: mm = MultiModularBasis_base([1009, 10007]); mm
218
MultiModularBasis with moduli [1009, 10007]
219
sage: mm.extend_with_primes([10037, 10039])
220
4
221
sage: mm
222
MultiModularBasis with moduli [1009, 10007, 10037, 10039]
223
224
"""
225
if isinstance(plist, GeneratorType):
226
plist = list(plist)
227
elif not isinstance(plist, (tuple, list)):
228
raise ValueError, "plist should be a list, tuple or a generator, got: %s"%(str(type(plist)))
229
230
cdef Py_ssize_t len_plist = len(plist)
231
232
if len_plist == 0:
233
return self.n
234
if check:
235
for p in plist:
236
if p > MAX_MODULUS:
237
raise ValueError, "given modulus cannot be manipulated in a single machine word"
238
self._realloc_to_new_count(self.n + len_plist)
239
240
cdef Py_ssize_t i
241
for i in range(len_plist):
242
self.moduli[self.n+i] = plist[i]
243
mpz_init(self.partial_products[self.n + i])
244
if partial_products:
245
mpz_set(self.partial_products[self.n + i],
246
(<Integer>partial_products[i]).value)
247
248
cdef int old_count = self.n
249
self.n += len_plist
250
if not partial_products:
251
self._refresh_products(old_count)
252
else:
253
self._refresh_prod()
254
self._refresh_precomputations(old_count)
255
return self.n
256
257
def __cmp__(self, other):
258
"""
259
EXAMPLES::
260
261
sage: from sage.ext.multi_modular import MultiModularBasis_base
262
sage: mm = MultiModularBasis_base([10007])
263
sage: nn = MultiModularBasis_base([10007])
264
sage: mm == nn
265
True
266
267
sage: mm == 1
268
False
269
"""
270
if not PY_TYPE_CHECK(other, MultiModularBasis_base):
271
return cmp(type(other), MultiModularBasis_base)
272
return cmp((self.list(), self._u_bound, self._l_bound),
273
(other.list(), (<MultiModularBasis_base>other)._u_bound,
274
(<MultiModularBasis_base>other)._l_bound))
275
276
def __setstate__(self, state):
277
"""
278
Initialize a new MultiModularBasis_base object from a state stored
279
in a pickle.
280
281
TESTS::
282
283
sage: from sage.ext.multi_modular import MultiModularBasis_base
284
sage: mm = MultiModularBasis_base([10007, 10009])
285
sage: mm == loads(dumps(mm))
286
True
287
288
sage: mm = MultiModularBasis_base([])
289
sage: mm.__setstate__(([10007, 10009], 2^10, 2^15))
290
291
sage: mm
292
MultiModularBasis with moduli [10007, 10009]
293
294
"""
295
if len(state) != 3:
296
raise ValueError, "cannot read state tuple"
297
nmoduli, lbound, ubound = state
298
self._realloc_to_new_count(len(nmoduli))
299
self._l_bound = lbound
300
self._u_bound = ubound
301
self.extend_with_primes(nmoduli, check=False)
302
303
def __getstate__(self):
304
"""
305
Return a tuple describing the state of this object for pickling.
306
307
TESTS::
308
309
sage: from sage.ext.multi_modular import MultiModularBasis_base
310
sage: mm = MultiModularBasis_base([10007, 10009])
311
sage: mm.__getstate__()
312
([10007, 10009], 1024L, 32768L)
313
"""
314
return (self.list(), self._l_bound, self._u_bound)
315
316
def _extend_moduli_to_height(self, height):
317
"""
318
319
EXAMPLES::
320
321
sage: from sage.ext.multi_modular import MultiModularBasis_base
322
sage: mm = MultiModularBasis_base(0); mm
323
MultiModularBasis with moduli [4561]
324
325
sage: mm._extend_moduli_to_height(10000)
326
sage: mm
327
MultiModularBasis with moduli [4561, 17351]
328
329
sage: mm = MultiModularBasis_base([46307]); mm
330
MultiModularBasis with moduli [46307]
331
332
sage: mm._extend_moduli_to_height(10^30); mm
333
MultiModularBasis with moduli [46307, 28499, 32573, 4339, 30859, 16451, 14323, 28631]
334
335
TESTS:
336
337
Verify that Trac #11358 is fixed::
338
339
sage: set_random_seed(0); m = sage.ext.multi_modular.MultiModularBasis_base(0)
340
sage: m._extend_moduli_to_height(prod(prime_range(50)))
341
sage: m = sage.ext.multi_modular.MultiModularBasis_base([],2,100)
342
sage: m._extend_moduli_to_height(prod(prime_range(90)))
343
sage: m._extend_moduli_to_height(prod(prime_range(150)))
344
Traceback (most recent call last):
345
...
346
RuntimeError: there are not enough primes in the interval [2, 100] to complete this multimodular computation
347
348
Another check (which fails horribly before #11358 is fixed)::
349
350
sage: set_random_seed(0); m = sage.ext.multi_modular.MultiModularBasis_base(0); m._extend_moduli_to_height(10**10000)
351
sage: len(set(m)) == len(m)
352
True
353
sage: len(m)
354
2438
355
"""
356
cdef Integer h
357
h = ZZ(height)
358
if h < self._l_bound:
359
h = ZZ(self._l_bound)
360
self._extend_moduli_to_height_c(h.value)
361
362
cdef int _extend_moduli_to_height_c(self, mpz_t height0) except -1:
363
r"""
364
Expand the list of primes and perform precomputations.
365
366
INPUT:
367
368
- ``height`` - determines how many primes are computed
369
(their product must be at least 2*height)
370
"""
371
# real height we use is twice the given, set height to 2*height0
372
cdef mpz_t height
373
mpz_init(height)
374
mpz_mul_2exp(height, height0, 1)
375
# check if we already have enough prime moduli
376
if self.n > 0 and mpz_cmp(height, self.partial_products[self.n-1]) <= 0:
377
mpz_clear(height)
378
return self.n
379
380
# find new prime moduli
381
cdef int i
382
new_moduli = []
383
new_partial_products = []
384
cdef Integer M # keeps current height
385
cdef mod_int p # keeps current prime moduli
386
387
if self.n == 0:
388
M = ZZ(1)
389
else:
390
M = PY_NEW(Integer)
391
mpz_set(M.value, self.partial_products[self.n-1])
392
393
known_primes = set(self)
394
while mpz_cmp(height, M.value) > 0:
395
p = self._new_random_prime(known_primes)
396
new_moduli.append(p)
397
known_primes.add(p)
398
M *= p
399
new_partial_products.append(M)
400
mpz_clear(height)
401
return self.extend_with_primes(new_moduli, new_partial_products,
402
check=False)
403
404
def _extend_moduli_to_count(self, int count):
405
r"""
406
Expand the list of primes and perform precomputations.
407
408
INPUT:
409
410
- ``count`` - the minimum number of moduli in the resulting list
411
412
EXAMPLES::
413
414
sage: from sage.ext.multi_modular import MultiModularBasis_base
415
sage: mm = MultiModularBasis_base([46307]); mm
416
MultiModularBasis with moduli [46307]
417
sage: mm._extend_moduli_to_count(3)
418
3
419
sage: mm
420
MultiModularBasis with moduli [46307, 4561, 17351]
421
"""
422
if count <= self.n:
423
return self.n
424
new_moduli = []
425
426
cdef int i
427
cdef mod_int p
428
known_primes = set(self)
429
for i from self.n <= i < count:
430
p = self._new_random_prime(known_primes)
431
known_primes.add(p)
432
new_moduli.append(p)
433
434
return self.extend_with_primes(new_moduli, check=False)
435
436
def _extend_moduli(self, count):
437
"""
438
Expand the list of prime moduli with `count` new random primes.
439
440
EXAMPLES::
441
442
sage: from sage.ext.multi_modular import MultiModularBasis_base
443
sage: mm = MultiModularBasis_base([46307]); mm
444
MultiModularBasis with moduli [46307]
445
sage: mm._extend_moduli(2); mm
446
MultiModularBasis with moduli [46307, 4561, 17351]
447
"""
448
self._extend_moduli_to_count(self.n + count)
449
450
cdef void _refresh_products(self, int start):
451
r"""
452
Compute and store $\prod_j=1^{i-1} m_j$ of i > start.
453
"""
454
cdef mpz_t z
455
mpz_init(z)
456
if start == 0:
457
mpz_set_ui(self.partial_products[0], self.moduli[0])
458
start += 1
459
for i from start <= i < self.n:
460
mpz_set_ui(z, self.moduli[i])
461
mpz_mul(self.partial_products[i], self.partial_products[i-1], z)
462
mpz_clear(z)
463
self._refresh_prod()
464
465
cdef void _refresh_prod(self):
466
# record the product and half product for balancing the lifts.
467
mpz_set(self.product, self.partial_products[self.n-1])
468
mpz_fdiv_q_ui(self.half_product, self.product, 2)
469
470
cdef void _refresh_precomputations(self, int start) except *:
471
r"""
472
Compute and store $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$ of i >= start.
473
"""
474
if start == 0:
475
start = 1 # first one is trivial, never used
476
self.C[0] = 1
477
for i from start <= i < self.n:
478
self.C[i] = ai.c_inverse_mod_longlong(mpz_fdiv_ui(self.partial_products[i-1], self.moduli[i]), self.moduli[i])
479
480
cdef int min_moduli_count(self, mpz_t height) except -1:
481
r"""
482
Compute the minimum number of primes needed to uniquely determine
483
an integer mod height.
484
"""
485
self._extend_moduli_to_height_c(height)
486
487
cdef int count
488
count = self.n * mpz_sizeinbase(height, 2) / mpz_sizeinbase(self.partial_products[self.n-1], 2) # an estimate
489
count = max(min(count, self.n), 1)
490
while count > 1 and mpz_cmp(height, self.partial_products[count-1]) < 0:
491
count -= 1
492
while mpz_cmp(height, self.partial_products[count-1]) > 0:
493
count += 1
494
495
return count
496
497
cdef mod_int last_prime(self):
498
return self.moduli[self.n-1]
499
500
cdef int mpz_reduce_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1:
501
r"""
502
Performs reduction mod $m_i$ for offset <= i < len
503
504
b[i] = z mod $m_{i+offset}$ for 0 <= i < len
505
506
INPUT:
507
508
- ``z`` - the integer being reduced
509
- ``b`` - array to hold the reductions mod each m_i.
510
It MUST be allocated and have length at least len
511
- ``offset`` - first prime in list to reduce against
512
- ``len`` - number of primes in list to reduce against
513
"""
514
cdef int i
515
cdef mod_int* m
516
m = self.moduli + offset
517
for i from 0 <= i < len:
518
b[i] = mpz_fdiv_ui(z, m[i])
519
return 0
520
521
cdef int mpz_reduce_vec_tail(self, mpz_t* z, mod_int** b, int vn, int offset, int len) except -1:
522
r"""
523
Performs reduction mod $m_i$ for offset <= i < len
524
525
b[i][j] = z[j] mod $m_{i+offset}$ for 0 <= i < len
526
527
INPUT:
528
529
- ``z`` - an array of integers being reduced
530
- ``b`` - array to hold the reductions mod each m_i.
531
It MUST be fully allocated and each
532
have length at least len
533
- ``vn`` - length of z and each b[i]
534
- ``offset`` - first prime in list to reduce against
535
- ``len`` - number of primes in list to reduce against
536
"""
537
cdef int i, j
538
cdef mod_int* m
539
m = self.moduli + offset
540
for i from 0 <= i < len:
541
mi = m[i]
542
for j from 0 <= j < vn:
543
b[i][j] = mpz_fdiv_ui(z[j], mi)
544
return 0
545
546
cdef int mpz_crt_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1:
547
r"""
548
Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$.
549
550
z = b[i] mod $m_{i+offset}$ for 0 <= i < len
551
552
In the case that offset > 0,
553
z remains unchanged mod $\prod_{i=0}^{offset-1} m_i$
554
555
INPUT:
556
557
- ``z`` - a placeholder for the constructed integer
558
z MUST be initialized IF and ONLY IF offset > 0
559
- ``b`` - array holding the reductions mod each m_i.
560
It MUST have length at least len
561
- ``offset`` - first prime in list to reduce against
562
- ``len`` - number of primes in list to reduce against
563
"""
564
cdef int i, s
565
cdef mpz_t u
566
cdef mod_int* m
567
m = self.moduli + offset
568
mpz_init(u)
569
if offset == 0:
570
s = 1
571
mpz_init_set_ui(z, b[0])
572
if b[0] == 0:
573
while s < len and b[s] == 0: # fast forward to first non-zero
574
s += 1
575
else:
576
s = 0
577
for i from s <= i < len:
578
mpz_set_ui(u, ((b[i] + m[i] - mpz_fdiv_ui(z, m[i])) * self.C[i]) % m[i])
579
mpz_mul(u, u, self.partial_products[i-1])
580
mpz_add(z, z, u)
581
582
# normalize to be between -prod/2 and prod/2.
583
if mpz_cmp(z, self.half_product) > 0:
584
mpz_sub(z, z, self.product)
585
mpz_clear(u)
586
return 0
587
588
cdef int mpz_crt_vec_tail(self, mpz_t* z, mod_int** b, int vc, int offset, int len) except -1:
589
r"""
590
Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$.
591
592
z[j] = b[i][j] mod $m_{i+offset}$ for 0 <= i < len
593
594
In the case that offset > 0,
595
z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$
596
597
INPUT:
598
599
- ``z`` - a placeholder for the constructed integers
600
z MUST be allocated and have length at least vc
601
z[j] MUST be initialized IF and ONLY IF offset > 0
602
- ``b`` - array holding the reductions mod each m_i.
603
MUST have length at least len
604
- ``vn`` - length of z and each b[i]
605
- ``offset`` - first prime in list to reduce against
606
- ``len`` - number of primes in list to reduce against
607
"""
608
cdef int i, j
609
cdef mpz_t u
610
cdef mod_int* m
611
612
m = self.moduli + offset
613
mpz_init(u)
614
if offset == 0:
615
s = 1
616
else:
617
s = 0
618
619
for j from 0 <= j < vc:
620
i = s
621
if offset == 0:
622
mpz_set_ui(z[j], b[0][j])
623
if b[0][j] == 0:
624
while i < len and b[i][j] == 0: # fast forward to first non-zero
625
i += 1
626
while i < len:
627
mpz_set_ui(u, ((b[i][j] + m[i] - mpz_fdiv_ui(z[j], m[i])) * self.C[i]) % m[i]) # u = ((b_i - z) * C_i) % m_i
628
mpz_mul(u, u, self.partial_products[i-1])
629
mpz_add(z[j], z[j], u)
630
i += 1
631
632
# normalize to be between -prod/2 and prod/2.
633
if mpz_cmp(z[j], self.half_product) > 0:
634
mpz_sub(z[j], z[j], self.product)
635
636
637
cdef Integer zz
638
zz = PY_NEW(Integer)
639
mpz_set(zz.value, self.half_product)
640
641
mpz_clear(u)
642
return 0
643
644
def crt(self, b):
645
r"""
646
Calculate lift mod $\prod_{i=0}^{len(b)-1} m_i$.
647
648
In the case that offset > 0,
649
z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$
650
651
INPUT:
652
653
- ``b`` - a list of length at most self.n
654
655
OUTPUT:
656
657
Integer z where z = b[i] mod $m_i$ for 0 <= i < len(b)
658
659
EXAMPLES::
660
661
sage: from sage.ext.multi_modular import MultiModularBasis_base
662
sage: mm = MultiModularBasis_base([10007, 10009, 10037, 10039, 17351])
663
sage: res = mm.crt([3,5,7,9]); res
664
8474803647063985
665
sage: res % 10007
666
3
667
sage: res % 10009
668
5
669
sage: res % 10037
670
7
671
sage: res % 10039
672
9
673
674
"""
675
cdef int i, n
676
n = len(b)
677
if n > self.n:
678
raise IndexError, "beyond bound for multi-modular prime list"
679
cdef mod_int* bs
680
bs = <mod_int*>sage_malloc(sizeof(mod_int)*n)
681
if bs == NULL:
682
raise MemoryError, "out of memory allocating multi-modular prime list"
683
for i from 0 <= i < n:
684
bs[i] = b[i]
685
cdef Integer z
686
z = PY_NEW(Integer)
687
self.mpz_crt_tail(z.value, bs, 0, n)
688
sage_free(bs)
689
return z
690
691
def precomputation_list(self):
692
"""
693
Returns a list of the precomputed coefficients
694
`$\prod_j=1^{i-1} m_j^{-1} (mod m_i)$`
695
where `m_i` are the prime moduli.
696
697
EXAMPLES::
698
699
sage: from sage.ext.multi_modular import MultiModularBasis_base
700
sage: mm = MultiModularBasis_base([46307, 10007]); mm
701
MultiModularBasis with moduli [46307, 10007]
702
sage: mm.precomputation_list()
703
[1, 4013]
704
705
"""
706
cdef int i
707
return [ZZ(self.C[i]) for i from 0 <= i < self.n]
708
709
def partial_product(self, n):
710
"""
711
Returns a list containing precomputed partial products.
712
713
EXAMPLES::
714
715
sage: from sage.ext.multi_modular import MultiModularBasis_base
716
sage: mm = MultiModularBasis_base([46307, 10007]); mm
717
MultiModularBasis with moduli [46307, 10007]
718
sage: mm.partial_product(0)
719
46307
720
sage: mm.partial_product(1)
721
463394149
722
723
TESTS::
724
725
sage: mm.partial_product(2)
726
Traceback (most recent call last):
727
...
728
IndexError: beyond bound for multi-modular prime list
729
sage: mm.partial_product(-2)
730
Traceback (most recent call last):
731
...
732
IndexError: negative index not valid
733
734
"""
735
if n >= self.n:
736
raise IndexError, "beyond bound for multi-modular prime list"
737
if n < 0:
738
raise IndexError, "negative index not valid"
739
cdef Integer z
740
z = PY_NEW(Integer)
741
mpz_set(z.value, self.partial_products[n])
742
return z
743
744
def prod(self):
745
"""
746
Returns the product of the prime moduli.
747
748
EXAMPLES::
749
750
sage: from sage.ext.multi_modular import MultiModularBasis_base
751
sage: mm = MultiModularBasis_base([46307]); mm
752
MultiModularBasis with moduli [46307]
753
sage: mm.prod()
754
46307
755
sage: mm = MultiModularBasis_base([46307, 10007]); mm
756
MultiModularBasis with moduli [46307, 10007]
757
sage: mm.prod()
758
463394149
759
760
TESTS::
761
762
sage: mm = MultiModularBasis_base([]); mm
763
MultiModularBasis with moduli []
764
sage: len(mm)
765
0
766
sage: mm.prod()
767
1
768
"""
769
if self.n == 0:
770
return 1
771
cdef Integer z
772
z = PY_NEW(Integer)
773
mpz_set(z.value, self.partial_products[self.n-1])
774
return z
775
776
def list(self):
777
"""
778
Return a list with the prime moduli.
779
780
EXAMPLES::
781
782
sage: from sage.ext.multi_modular import MultiModularBasis_base
783
sage: mm = MultiModularBasis_base([46307, 10007])
784
sage: mm.list()
785
[46307, 10007]
786
"""
787
cdef Py_ssize_t i
788
return [ZZ(self.moduli[i]) for i in range(self.n)]
789
790
def __len__(self):
791
"""
792
Returns the number of moduli stored.
793
794
EXAMPLES::
795
796
sage: from sage.ext.multi_modular import MultiModularBasis_base
797
sage: mm = MultiModularBasis_base([10007])
798
sage: len(mm)
799
1
800
sage: mm._extend_moduli_to_count(2)
801
2
802
sage: len(mm)
803
2
804
"""
805
return self.n
806
807
def __iter__(self):
808
"""
809
Returns an iterator over the prime moduli.
810
811
EXAMPLES::
812
813
sage: from sage.ext.multi_modular import MultiModularBasis_base
814
sage: mm = MultiModularBasis_base([10007, 10009])
815
sage: t = iter(mm); t
816
<listiterator object at ...>
817
sage: list(mm.__iter__())
818
[10007, 10009]
819
820
"""
821
return self.list().__iter__()
822
823
def __getitem__(self, ix):
824
"""
825
Return the moduli stored at index `ix` as a Python long.
826
827
EXAMPLES::
828
829
sage: from sage.ext.multi_modular import MultiModularBasis_base
830
sage: mm = MultiModularBasis_base([10007, 10009])
831
sage: mm[1]
832
10009L
833
sage: mm[-1]
834
Traceback (most recent call last):
835
...
836
IndexError: index out of range
837
838
#sage: mm[:1]
839
MultiModularBasis with moduli [10007]
840
"""
841
if isinstance(ix, slice):
842
return self.__class__(self.list()[ix], l_bound = self._l_bound,
843
u_bound = self._u_bound)
844
845
cdef int i
846
i = ix
847
if i != ix:
848
raise ValueError, "index must be an integer"
849
if i < 0 or i >= self.n:
850
raise IndexError, "index out of range"
851
return self.moduli[i]
852
853
def __repr__(self):
854
"""
855
Return a string representation of this object.
856
857
EXAMPLES::
858
859
sage: from sage.ext.multi_modular import MultiModularBasis_base
860
sage: MultiModularBasis_base([10007])
861
MultiModularBasis with moduli [10007]
862
"""
863
return "MultiModularBasis with moduli "+str(self.list())
864
865
866
cdef class MultiModularBasis(MultiModularBasis_base):
867
"""
868
Class used for storing a MultiModular bases of a fixed length.
869
"""
870
871
872
cdef int mpz_reduce(self, mpz_t z, mod_int* b) except -1:
873
r"""
874
Performs reduction mod $m_i$ for each modulus $m_i$
875
876
b[i] = z mod $m_i$ for 0 <= i < len(self)
877
878
INPUT:
879
880
- ``z`` - the integer being reduced
881
- ``b`` - array to hold the reductions mod each m_i.
882
It MUST be allocated and have length at least len
883
"""
884
self.mpz_reduce_tail(z, b, 0, self.n)
885
886
cdef int mpz_reduce_vec(self, mpz_t* z, mod_int** b, int vn) except -1:
887
r"""
888
Performs reduction mod $m_i$ for each modulus $m_i$
889
890
b[i][j] = z[j] mod $m_i$ for 0 <= i < len(self)
891
892
INPUT:
893
894
- ``z`` - an array of integers being reduced
895
- ``b`` - array to hold the reductions mod each m_i.
896
It MUST be fully allocated and each
897
have length at least len
898
- ``vn`` - length of z and each b[i]
899
"""
900
self.mpz_reduce_vec_tail(z, b, vn, 0, self.n)
901
902
cdef int mpz_crt(self, mpz_t z, mod_int* b) except -1:
903
r"""
904
Calculate lift mod $\prod m_i$.
905
906
z = b[i] mod $m_{i+offset}$ for 0 <= i < len(self)
907
908
INPUT:
909
910
- ``z`` - a placeholder for the constructed integer
911
z MUST NOT be initialized
912
- ``b`` - array holding the reductions mod each $m_i$.
913
It MUST have length at least len(self)
914
"""
915
self.mpz_crt_tail(z, b, 0, self.n)
916
917
cdef int mpz_crt_vec(self, mpz_t* z, mod_int** b, int vn) except -1:
918
r"""
919
Calculate lift mod $\prod m_i$.
920
921
z[j] = b[i][j] mod $m_i$ for 0 <= i < len(self)
922
923
INPUT:
924
925
- ``z`` - a placeholder for the constructed integers
926
z MUST be allocated and have length at least vn,
927
but each z[j] MUST NOT be initialized
928
- ``b`` - array holding the reductions mod each $m_i$.
929
It MUST have length at least len(self)
930
- ``vn`` - length of z and each b[i]
931
"""
932
self.mpz_crt_vec_tail(z, b, vn, 0, self.n)
933
934
935
cdef class MutableMultiModularBasis(MultiModularBasis):
936
"""
937
Class used for performing multi-modular methods,
938
with the possibility of removing bad primes.
939
"""
940
def next_prime(self):
941
"""
942
Picks a new random prime between the bounds given during the
943
initialization of this object, updates the precomputed data,
944
and returns the new prime modulus.
945
946
EXAMPLES::
947
948
sage: from sage.ext.multi_modular import MutableMultiModularBasis
949
sage: mm = MutableMultiModularBasis([10007])
950
sage: mm.next_prime()
951
4561L
952
sage: mm
953
MultiModularBasis with moduli [10007, 4561]
954
955
"""
956
return self.next_prime_c()
957
958
cdef mod_int next_prime_c(self) except -1:
959
self._extend_moduli(1)
960
return self.moduli[self.n-1]
961
962
def replace_prime(self, ix):
963
"""
964
Replace the prime moduli at the given index with a different one,
965
update the precomputed data accordingly, and return the new prime.
966
967
EXAMPLES::
968
969
sage: from sage.ext.multi_modular import MutableMultiModularBasis
970
sage: mm = MutableMultiModularBasis([10007, 10009, 10037, 10039])
971
sage: mm
972
MultiModularBasis with moduli [10007, 10009, 10037, 10039]
973
sage: mm.prod()
974
10092272478850909
975
sage: mm.precomputation_list()
976
[1, 5004, 6536, 6060]
977
sage: mm.partial_product(2)
978
1005306552331
979
sage: mm.replace_prime(1)
980
4561L
981
sage: mm
982
MultiModularBasis with moduli [10007, 4561, 10037, 10039]
983
sage: mm.prod()
984
4598946425820661
985
sage: mm.precomputation_list()
986
[1, 2314, 3274, 3013]
987
sage: mm.partial_product(2)
988
458108021299
989
990
"""
991
return self.replace_prime_c(ix)
992
993
cdef mod_int replace_prime_c(self, int ix) except -1:
994
r"""
995
Replace $m_{ix}$ in the list of moduli with a new
996
prime number greater than all others in the list,
997
and recompute all precomputations.
998
999
INPUT:
1000
1001
- ``ix`` - index into list of moduli
1002
1003
OUTPUT:
1004
1005
p -- the new prime modulus
1006
"""
1007
cdef mod_int new_p
1008
1009
if ix < 0 or ix >= self.n:
1010
raise IndexError, "index out of range"
1011
1012
new_p = self._new_random_prime(set(self))
1013
self.moduli[ix] = new_p
1014
1015
self._refresh_products(ix)
1016
self._refresh_precomputations(ix)
1017
return new_p
1018
1019