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