| 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 next_prime # should I just use probable primes? |
|---|
| 22 | |
|---|
| 23 | # should I have mod_int versions of these functions? |
|---|
| 24 | # c_inverse_mod_longlong modular inverse used exactly once in _refresh_precomputations |
|---|
| 25 | from sage.ext.arith cimport arith_llong |
|---|
| 26 | cdef arith_llong ai |
|---|
| 27 | ai = arith_llong() |
|---|
| 28 | |
|---|
| 29 | # We use both integer and double operations, hence the min. |
|---|
| 30 | # MAX_MODULUS = min(int(sqrt(int(MOD_INT_OVERFLOW))-1), int(2)**int(20)) |
|---|
| 31 | |
|---|
| 32 | # Hard coded because currently matrix_modn_dense is implemented using C ints |
|---|
| 33 | # which are always 32-bit. Once this gets firxed, i.e., there is a better |
|---|
| 34 | # matrix_modn class, then this can change. |
|---|
| 35 | MAX_MODULUS = 46341 |
|---|
| 36 | |
|---|
| 37 | # TODO: have one global instance for sharing, copy for MutableMultiModularBasis |
|---|
| 38 | cdef class MultiModularBasis_base: |
|---|
| 39 | r""" |
|---|
| 40 | This class stores a list of machine-sized prime numbers, |
|---|
| 41 | and can do reduction and Chinese Remainder Theorem lifting |
|---|
| 42 | modulo these primes. |
|---|
| 43 | |
|---|
| 44 | Lifting implemented via Garner's algorithm, which has the advantage |
|---|
| 45 | that all reductions are word-sized. For each $i$ precompute |
|---|
| 46 | |
|---|
| 47 | $\prod_j=1^{i-1} m_j$ and $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$ |
|---|
| 48 | """ |
|---|
| 49 | |
|---|
| 50 | def __new__(self, height): |
|---|
| 51 | r""" |
|---|
| 52 | Allocate the space for the moduli and precomputation lists |
|---|
| 53 | and initalize the first element of that list. |
|---|
| 54 | """ |
|---|
| 55 | cdef mod_int p |
|---|
| 56 | p = next_prime(MAX_MODULUS/7) |
|---|
| 57 | |
|---|
| 58 | self.n = 1 |
|---|
| 59 | |
|---|
| 60 | self.moduli = <mod_int*>sage_malloc(sizeof(mod_int)) |
|---|
| 61 | self.partial_products = <mpz_t*>sage_malloc(sizeof(mpz_t)) |
|---|
| 62 | self.C = <mod_int*>sage_malloc(sizeof(mod_int)) |
|---|
| 63 | |
|---|
| 64 | if self.moduli == NULL or self.partial_products == NULL or self.C == NULL: |
|---|
| 65 | raise MemoryError, "out of memory allocating multi-modular prime list" |
|---|
| 66 | |
|---|
| 67 | self.moduli[0] = p |
|---|
| 68 | mpz_init_set_ui(self.partial_products[0], p) |
|---|
| 69 | self.C[0] = 1 |
|---|
| 70 | |
|---|
| 71 | mpz_init(self.product) |
|---|
| 72 | mpz_init(self.half_product) |
|---|
| 73 | |
|---|
| 74 | def __dealloc__(self): |
|---|
| 75 | sage_free(self.moduli) |
|---|
| 76 | sage_free(self.partial_products) |
|---|
| 77 | sage_free(self.C) |
|---|
| 78 | |
|---|
| 79 | def __init__(self, height): |
|---|
| 80 | r""" |
|---|
| 81 | Initialize a multi-modular basis and perform precomputations. |
|---|
| 82 | |
|---|
| 83 | INPUT: |
|---|
| 84 | height -- as integer |
|---|
| 85 | determines how many primes are computed |
|---|
| 86 | (their product will be at least 2*height) |
|---|
| 87 | as list |
|---|
| 88 | a list of prime moduli to start with |
|---|
| 89 | """ |
|---|
| 90 | cdef int i |
|---|
| 91 | if isinstance(height, list): |
|---|
| 92 | m = height |
|---|
| 93 | self.n = len(m) |
|---|
| 94 | self.moduli = <mod_int*>sage_realloc(self.moduli, sizeof(mod_int) * self.n) |
|---|
| 95 | self.partial_products = <mpz_t*>sage_realloc(self.partial_products, sizeof(mpz_t) * self.n) |
|---|
| 96 | self.C = <mod_int*>sage_realloc(self.C, sizeof(mod_int) * self.n) |
|---|
| 97 | if self.moduli == NULL or self.partial_products == NULL or self.C == NULL: |
|---|
| 98 | raise MemoryError, "out of memory allocating multi-modular prime list" |
|---|
| 99 | for i from 0 <= i < len(m): |
|---|
| 100 | if m[i] > MAX_MODULUS: |
|---|
| 101 | raise ValueError, "given modulus cannot be manipulated in a single machine word" |
|---|
| 102 | self.moduli[i] = m[i] |
|---|
| 103 | for i from 1 <= i < len(m): |
|---|
| 104 | mpz_init(self.partial_products[i]) |
|---|
| 105 | self._refresh_products(0) |
|---|
| 106 | self._refresh_precomputations(0) |
|---|
| 107 | else: |
|---|
| 108 | self._extend_moduli_to_height(height) |
|---|
| 109 | |
|---|
| 110 | self._refresh_prod() |
|---|
| 111 | |
|---|
| 112 | |
|---|
| 113 | def _extend_moduli_to_height(self, height): |
|---|
| 114 | cdef Integer h |
|---|
| 115 | h = ZZ(height) |
|---|
| 116 | self._extend_moduli_to_height_c(h.value) |
|---|
| 117 | |
|---|
| 118 | cdef int _extend_moduli_to_height_c(self, mpz_t height0) except -1: |
|---|
| 119 | r""" |
|---|
| 120 | Expand the list of primes and perform precomputations. |
|---|
| 121 | |
|---|
| 122 | INPUT: |
|---|
| 123 | height -- determines how many primes are computed |
|---|
| 124 | (their product must be at least height) |
|---|
| 125 | """ |
|---|
| 126 | cdef mpz_t height |
|---|
| 127 | mpz_init(height) |
|---|
| 128 | mpz_mul_2exp(height, height0, 1) |
|---|
| 129 | if mpz_cmp(height, self.partial_products[self.n-1]) <= 0: |
|---|
| 130 | return self.n |
|---|
| 131 | cdef int i |
|---|
| 132 | new_moduli = [] |
|---|
| 133 | new_partial_products = [] |
|---|
| 134 | cdef Integer p, M |
|---|
| 135 | M = PY_NEW(Integer) |
|---|
| 136 | mpz_set(M.value, self.partial_products[self.n-1]) |
|---|
| 137 | p = ZZ(self.last_prime()) |
|---|
| 138 | while mpz_cmp(height, M.value) > 0: |
|---|
| 139 | p = next_prime(p) |
|---|
| 140 | new_moduli.append(p) |
|---|
| 141 | M *= p |
|---|
| 142 | new_partial_products.append(M) |
|---|
| 143 | mpz_clear(height) |
|---|
| 144 | |
|---|
| 145 | cdef int new_count, old_count |
|---|
| 146 | old_count = self.n |
|---|
| 147 | new_count = self.n + len(new_moduli) |
|---|
| 148 | |
|---|
| 149 | self.moduli = <mod_int*>sage_realloc(self.moduli, sizeof(mod_int)*new_count) |
|---|
| 150 | self.partial_products = <mpz_t*>sage_realloc(self.partial_products, sizeof(mpz_t)*new_count) |
|---|
| 151 | self.C = <mod_int*>sage_realloc(self.C, sizeof(mod_int)*new_count) |
|---|
| 152 | if self.moduli == NULL or self.partial_products == NULL or self.C == NULL: |
|---|
| 153 | raise MemoryError, "out of memory allocating multi-modular prime list" |
|---|
| 154 | |
|---|
| 155 | for i from self.n <= i < new_count: |
|---|
| 156 | self.moduli[i] = new_moduli[i-self.n] |
|---|
| 157 | mpz_init_set(self.partial_products[i], (<Integer>new_partial_products[i-self.n]).value) |
|---|
| 158 | |
|---|
| 159 | self.n = new_count |
|---|
| 160 | self._refresh_precomputations(old_count) |
|---|
| 161 | return new_count |
|---|
| 162 | |
|---|
| 163 | def _extend_moduli_to_count(self, int count): |
|---|
| 164 | r""" |
|---|
| 165 | Expand the list of primes and perform precomputations. |
|---|
| 166 | |
|---|
| 167 | INPUT: |
|---|
| 168 | count -- the minimum number of moduli in the resulting list |
|---|
| 169 | """ |
|---|
| 170 | if count <= self.n: |
|---|
| 171 | return self.n |
|---|
| 172 | self.moduli = <mod_int*>sage_realloc(self.moduli, sizeof(mod_int) * count) |
|---|
| 173 | self.partial_products = <mpz_t*>sage_realloc(self.partial_products, sizeof(mpz_t) * count) |
|---|
| 174 | self.C = <mod_int*>sage_realloc(self.C, sizeof(mod_int) * count) |
|---|
| 175 | if self.moduli == NULL or self.partial_products == NULL or self.C == NULL: |
|---|
| 176 | raise MemoryError, "out of memory allocating multi-modular prime list" |
|---|
| 177 | |
|---|
| 178 | cdef int i |
|---|
| 179 | cdef Integer p |
|---|
| 180 | p = ZZ(self.last_prime()) |
|---|
| 181 | for i from self.n <= i < count: |
|---|
| 182 | p = next_prime(p) |
|---|
| 183 | self.moduli[i] = p |
|---|
| 184 | mpz_init(self.partial_products[i]) |
|---|
| 185 | mpz_mul(self.partial_products[i], self.partial_products[i-1], p.value) |
|---|
| 186 | old_count = self.n |
|---|
| 187 | self.n = count |
|---|
| 188 | self._refresh_precomputations(old_count) |
|---|
| 189 | return count |
|---|
| 190 | |
|---|
| 191 | def _extend_moduli(self, count): |
|---|
| 192 | self._extend_moduli_to_count(self.n + count) |
|---|
| 193 | |
|---|
| 194 | cdef void _refresh_products(self, int start): |
|---|
| 195 | r""" |
|---|
| 196 | Compute and store $\prod_j=1^{i-1} m_j$ of i > start. |
|---|
| 197 | """ |
|---|
| 198 | cdef mpz_t z |
|---|
| 199 | mpz_init(z) |
|---|
| 200 | if start == 0: |
|---|
| 201 | mpz_set_ui(self.partial_products[0], self.moduli[0]) |
|---|
| 202 | start += 1 |
|---|
| 203 | for i from start <= i < self.n: |
|---|
| 204 | mpz_set_ui(z, self.moduli[i]) |
|---|
| 205 | mpz_mul(self.partial_products[i], self.partial_products[i-1], z) |
|---|
| 206 | mpz_clear(z) |
|---|
| 207 | self._refresh_prod() |
|---|
| 208 | |
|---|
| 209 | cdef void _refresh_prod(self): |
|---|
| 210 | # record the product and half product for balancing the lifts. |
|---|
| 211 | mpz_set(self.product, self.partial_products[self.n-1]) |
|---|
| 212 | mpz_fdiv_q_ui(self.half_product, self.product, 2) |
|---|
| 213 | |
|---|
| 214 | cdef void _refresh_precomputations(self, int start): |
|---|
| 215 | r""" |
|---|
| 216 | Compute and store $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$ of i >= start. |
|---|
| 217 | """ |
|---|
| 218 | if start == 0: |
|---|
| 219 | start = 1 # first one is trivial, never used |
|---|
| 220 | for i from start <= i < self.n: |
|---|
| 221 | self.C[i] = ai.c_inverse_mod_longlong(mpz_fdiv_ui(self.partial_products[i-1], self.moduli[i]), self.moduli[i]) |
|---|
| 222 | |
|---|
| 223 | cdef int min_moduli_count(self, mpz_t height) except -1: |
|---|
| 224 | r""" |
|---|
| 225 | Compute the minimum number of primes needed to uniquely determin |
|---|
| 226 | an integer mod height. |
|---|
| 227 | """ |
|---|
| 228 | self._extend_moduli_to_height_c(height) |
|---|
| 229 | |
|---|
| 230 | cdef int count |
|---|
| 231 | count = self.n * mpz_sizeinbase(height, 2) / mpz_sizeinbase(self.partial_products[self.n-1], 2) # an estimate |
|---|
| 232 | count = max(min(count, self.n), 1) |
|---|
| 233 | while count > 1 and mpz_cmp(height, self.partial_products[count-1]) < 0: |
|---|
| 234 | count -= 1 |
|---|
| 235 | while mpz_cmp(height, self.partial_products[count-1]) > 0: |
|---|
| 236 | count += 1 |
|---|
| 237 | |
|---|
| 238 | return count |
|---|
| 239 | |
|---|
| 240 | cdef int moduli_list_c(self, mod_int** moduli): |
|---|
| 241 | memcpy(moduli[0], self.moduli, sizeof(mod_int)*self.n) |
|---|
| 242 | return self.n |
|---|
| 243 | |
|---|
| 244 | cdef mod_int last_prime(self): |
|---|
| 245 | return self.moduli[self.n-1] |
|---|
| 246 | |
|---|
| 247 | cdef int mpz_reduce_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1: |
|---|
| 248 | r""" |
|---|
| 249 | Performs reduction mod $m_i$ for offset <= i < len |
|---|
| 250 | |
|---|
| 251 | b[i] = z mod $m_{i+offset}$ for 0 <= i < len |
|---|
| 252 | |
|---|
| 253 | INPUT: |
|---|
| 254 | z -- the integer being reduced |
|---|
| 255 | b -- array to hold the reductions mod each m_i. |
|---|
| 256 | It MUST be allocated and have length at least len |
|---|
| 257 | offset -- first prime in list to reduce against |
|---|
| 258 | len -- number of primes in list to reduce against |
|---|
| 259 | """ |
|---|
| 260 | cdef int i |
|---|
| 261 | cdef mod_int* m |
|---|
| 262 | m = self.moduli + offset |
|---|
| 263 | for i from 0 <= i < len: |
|---|
| 264 | b[i] = mpz_fdiv_ui(z, m[i]) |
|---|
| 265 | return 0 |
|---|
| 266 | |
|---|
| 267 | cdef int mpz_reduce_vec_tail(self, mpz_t* z, mod_int** b, int vn, int offset, int len) except -1: |
|---|
| 268 | r""" |
|---|
| 269 | Performs reduction mod $m_i$ for offset <= i < len |
|---|
| 270 | |
|---|
| 271 | b[i][j] = z[j] mod $m_{i+offset}$ for 0 <= i < len |
|---|
| 272 | |
|---|
| 273 | INPUT: |
|---|
| 274 | z -- an array of integers being reduced |
|---|
| 275 | b -- array to hold the reductions mod each m_i. |
|---|
| 276 | It MUST be fully allocated and each |
|---|
| 277 | have length at least len |
|---|
| 278 | vn -- length of z and each b[i] |
|---|
| 279 | offset -- first prime in list to reduce against |
|---|
| 280 | len -- number of primes in list to reduce against |
|---|
| 281 | """ |
|---|
| 282 | cdef int i, j |
|---|
| 283 | cdef mod_int* m |
|---|
| 284 | m = self.moduli + offset |
|---|
| 285 | for i from 0 <= i < len: |
|---|
| 286 | mi = m[i] |
|---|
| 287 | for j from 0 <= j < vn: |
|---|
| 288 | b[i][j] = mpz_fdiv_ui(z[j], mi) |
|---|
| 289 | return 0 |
|---|
| 290 | |
|---|
| 291 | cdef int mpz_crt_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1: |
|---|
| 292 | r""" |
|---|
| 293 | Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$. |
|---|
| 294 | |
|---|
| 295 | z = b[i] mod $m_{i+offset}$ for 0 <= i < len |
|---|
| 296 | |
|---|
| 297 | In the case that offset > 0, |
|---|
| 298 | z remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ |
|---|
| 299 | |
|---|
| 300 | INPUT: |
|---|
| 301 | z -- a placeholder for the constructed integer |
|---|
| 302 | z MUST be initalized IF and ONLY IF offset > 0 |
|---|
| 303 | b -- array holding the reductions mod each m_i. |
|---|
| 304 | It MUST have length at least len |
|---|
| 305 | offset -- first prime in list to reduce against |
|---|
| 306 | len -- number of primes in list to reduce against |
|---|
| 307 | """ |
|---|
| 308 | cdef int i, s |
|---|
| 309 | cdef mpz_t u |
|---|
| 310 | cdef mod_int* m |
|---|
| 311 | m = self.moduli + offset |
|---|
| 312 | mpz_init(u) |
|---|
| 313 | if offset == 0: |
|---|
| 314 | s = 1 |
|---|
| 315 | mpz_init_set_ui(z, b[0]) |
|---|
| 316 | if b[0] == 0: |
|---|
| 317 | while s < len and b[s] == 0: # fast forward to first non-zero |
|---|
| 318 | s += 1 |
|---|
| 319 | else: |
|---|
| 320 | s = 0 |
|---|
| 321 | for i from s <= i < len: |
|---|
| 322 | mpz_set_ui(u, ((b[i] + m[i] - mpz_fdiv_ui(z, m[i])) * self.C[i]) % m[i]) |
|---|
| 323 | mpz_mul(u, u, self.partial_products[i-1]) |
|---|
| 324 | mpz_add(z, z, u) |
|---|
| 325 | |
|---|
| 326 | # normalize to be between -prod/2 and prod/2. |
|---|
| 327 | if mpz_cmp(z, self.half_product) > 0: |
|---|
| 328 | mpz_sub(z, z, self.product) |
|---|
| 329 | mpz_clear(u) |
|---|
| 330 | return 0 |
|---|
| 331 | |
|---|
| 332 | cdef int mpz_crt_vec_tail(self, mpz_t* z, mod_int** b, int vc, int offset, int len) except -1: |
|---|
| 333 | r""" |
|---|
| 334 | Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$. |
|---|
| 335 | |
|---|
| 336 | z[j] = b[i][j] mod $m_{i+offset}$ for 0 <= i < len |
|---|
| 337 | |
|---|
| 338 | In the case that offset > 0, |
|---|
| 339 | z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ |
|---|
| 340 | |
|---|
| 341 | INPUT: |
|---|
| 342 | z -- a placeholder for the constructed integers |
|---|
| 343 | z MUST be allocated and have length at least vc |
|---|
| 344 | z[j] MUST be initalized IF and ONLY IF offset > 0 |
|---|
| 345 | b -- array holding the reductions mod each m_i. |
|---|
| 346 | MUST have length at least len |
|---|
| 347 | vn -- length of z and each b[i] |
|---|
| 348 | offset -- first prime in list to reduce against |
|---|
| 349 | len -- number of primes in list to reduce against |
|---|
| 350 | """ |
|---|
| 351 | cdef int i, j |
|---|
| 352 | cdef mpz_t u |
|---|
| 353 | cdef mod_int* m |
|---|
| 354 | |
|---|
| 355 | m = self.moduli + offset |
|---|
| 356 | mpz_init(u) |
|---|
| 357 | if offset == 0: |
|---|
| 358 | s = 1 |
|---|
| 359 | else: |
|---|
| 360 | s = 0 |
|---|
| 361 | |
|---|
| 362 | for j from 0 <= j < vc: |
|---|
| 363 | i = s |
|---|
| 364 | if offset == 0: |
|---|
| 365 | mpz_init_set_ui(z[j], b[0][j]) |
|---|
| 366 | if b[0][j] == 0: |
|---|
| 367 | while i < len and b[i][j] == 0: # fast forward to first non-zero |
|---|
| 368 | i += 1 |
|---|
| 369 | while i < len: |
|---|
| 370 | 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 |
|---|
| 371 | mpz_mul(u, u, self.partial_products[i-1]) |
|---|
| 372 | mpz_add(z[j], z[j], u) |
|---|
| 373 | i += 1 |
|---|
| 374 | |
|---|
| 375 | # normalize to be between -prod/2 and prod/2. |
|---|
| 376 | if mpz_cmp(z[j], self.half_product) > 0: |
|---|
| 377 | mpz_sub(z[j], z[j], self.product) |
|---|
| 378 | |
|---|
| 379 | |
|---|
| 380 | cdef Integer zz |
|---|
| 381 | zz = PY_NEW(Integer) |
|---|
| 382 | mpz_init_set(zz.value, self.product) |
|---|
| 383 | mpz_set(zz.value, self.half_product) |
|---|
| 384 | |
|---|
| 385 | mpz_clear(u) |
|---|
| 386 | return 0 |
|---|
| 387 | |
|---|
| 388 | def crt(self, b): |
|---|
| 389 | r""" |
|---|
| 390 | Calculate lift mod $\prod_{i=0}^{len(b)-1} m_i$. |
|---|
| 391 | |
|---|
| 392 | In the case that offset > 0, |
|---|
| 393 | z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ |
|---|
| 394 | |
|---|
| 395 | INPUT: |
|---|
| 396 | b -- a list of length at most self.n |
|---|
| 397 | OUTPUT: |
|---|
| 398 | Integer z where z = b[i] mod $m_i$ for 0 <= i < len(b) |
|---|
| 399 | """ |
|---|
| 400 | cdef int i, n |
|---|
| 401 | n = len(b) |
|---|
| 402 | if n > self.n: |
|---|
| 403 | raise IndexError, "beyond bound for multi-modular prime list" |
|---|
| 404 | cdef mod_int* bs |
|---|
| 405 | bs = <mod_int*>sage_malloc(sizeof(mod_int)*n) |
|---|
| 406 | if bs == NULL: |
|---|
| 407 | raise MemoryError, "out of memory allocating multi-modular prime list" |
|---|
| 408 | for i from 0 <= i < n: |
|---|
| 409 | bs[i] = b[i] |
|---|
| 410 | cdef Integer z |
|---|
| 411 | z = PY_NEW(Integer) |
|---|
| 412 | self.mpz_crt_tail(z.value, bs, 0, n) |
|---|
| 413 | sage_free(bs) |
|---|
| 414 | return z |
|---|
| 415 | |
|---|
| 416 | def precomputation_list(self): |
|---|
| 417 | cdef int i |
|---|
| 418 | return [ZZ(self.C[i]) for i from 0 <= i < self.n] |
|---|
| 419 | |
|---|
| 420 | def partial_product(self, n): |
|---|
| 421 | if n > self.n: |
|---|
| 422 | raise IndexError, "beyond bound for multi-modular prime list" |
|---|
| 423 | cdef Integer z |
|---|
| 424 | z = PY_NEW(Integer) |
|---|
| 425 | mpz_init_set(z.value, self.partial_products[n-1]) |
|---|
| 426 | return z |
|---|
| 427 | |
|---|
| 428 | def prod(self): |
|---|
| 429 | cdef Integer z |
|---|
| 430 | z = PY_NEW(Integer) |
|---|
| 431 | mpz_init_set(z.value, self.partial_products[self.n-1]) |
|---|
| 432 | return z |
|---|
| 433 | |
|---|
| 434 | def list(self): |
|---|
| 435 | cdef int i |
|---|
| 436 | cdef mod_int* moduli |
|---|
| 437 | moduli = <mod_int*>sage_malloc(sizeof(mod_int) * self.n) |
|---|
| 438 | n = self.moduli_list_c(&moduli) |
|---|
| 439 | m = [ZZ(moduli[i]) for i from 0 <= i < n] |
|---|
| 440 | sage_free(moduli) |
|---|
| 441 | return m |
|---|
| 442 | |
|---|
| 443 | def __len__(self): |
|---|
| 444 | return self.n |
|---|
| 445 | |
|---|
| 446 | def __iter__(self): |
|---|
| 447 | return self.list().__iter__() |
|---|
| 448 | |
|---|
| 449 | def __getitem__(self, ix): |
|---|
| 450 | |
|---|
| 451 | if isinstance(ix, slice): |
|---|
| 452 | return type(self)(self.list()[ix]) |
|---|
| 453 | |
|---|
| 454 | cdef int i |
|---|
| 455 | i = ix |
|---|
| 456 | if i < 0 or i >= self.n: |
|---|
| 457 | raise IndexError, "index out of range" |
|---|
| 458 | return self.moduli[i] |
|---|
| 459 | |
|---|
| 460 | def __repr__(self): |
|---|
| 461 | return str(type(self))+str(self.list()) |
|---|
| 462 | |
|---|
| 463 | |
|---|
| 464 | cdef class MultiModularBasis(MultiModularBasis_base): |
|---|
| 465 | """ |
|---|
| 466 | Class used for storing a MultiModular bases of a fixed length. |
|---|
| 467 | """ |
|---|
| 468 | |
|---|
| 469 | |
|---|
| 470 | cdef int mpz_reduce(self, mpz_t z, mod_int* b) except -1: |
|---|
| 471 | r""" |
|---|
| 472 | Performs reduction mod $m_i$ for each modulus $m_i$ |
|---|
| 473 | |
|---|
| 474 | b[i] = z mod $m_i$ for 0 <= i < len(self) |
|---|
| 475 | |
|---|
| 476 | INPUT: |
|---|
| 477 | z -- the integer being reduced |
|---|
| 478 | b -- array to hold the reductions mod each m_i. |
|---|
| 479 | It MUST be allocated and have length at least len |
|---|
| 480 | """ |
|---|
| 481 | self.mpz_reduce_tail(z, b, 0, self.n) |
|---|
| 482 | |
|---|
| 483 | cdef int mpz_reduce_vec(self, mpz_t* z, mod_int** b, int vn) except -1: |
|---|
| 484 | r""" |
|---|
| 485 | Performs reduction mod $m_i$ for each modulus $m_i$ |
|---|
| 486 | |
|---|
| 487 | b[i][j] = z[j] mod $m_i$ for 0 <= i < len(self) |
|---|
| 488 | |
|---|
| 489 | INPUT: |
|---|
| 490 | z -- an array of integers being reduced |
|---|
| 491 | b -- array to hold the reductions mod each m_i. |
|---|
| 492 | It MUST be fully allocated and each |
|---|
| 493 | have length at least len |
|---|
| 494 | vn -- length of z and each b[i] |
|---|
| 495 | """ |
|---|
| 496 | self.mpz_reduce_vec_tail(z, b, vn, 0, self.n) |
|---|
| 497 | |
|---|
| 498 | cdef int mpz_crt(self, mpz_t z, mod_int* b) except -1: |
|---|
| 499 | r""" |
|---|
| 500 | Calculate lift mod $\prod m_i$. |
|---|
| 501 | |
|---|
| 502 | z = b[i] mod $m_{i+offset}$ for 0 <= i < len(self) |
|---|
| 503 | |
|---|
| 504 | INPUT: |
|---|
| 505 | z -- a placeholder for the constructed integer |
|---|
| 506 | z MUST NOT be initalized |
|---|
| 507 | b -- array holding the reductions mod each $m_i$. |
|---|
| 508 | It MUST have length at least len(self) |
|---|
| 509 | """ |
|---|
| 510 | self.mpz_crt_tail(z, b, 0, self.n) |
|---|
| 511 | |
|---|
| 512 | cdef int mpz_crt_vec(self, mpz_t* z, mod_int** b, int vn) except -1: |
|---|
| 513 | r""" |
|---|
| 514 | Calculate lift mod $\prod m_i$. |
|---|
| 515 | |
|---|
| 516 | z[j] = b[i][j] mod $m_i$ for 0 <= i < len(self) |
|---|
| 517 | |
|---|
| 518 | INPUT: |
|---|
| 519 | z -- a placeholder for the constructed integers |
|---|
| 520 | z MUST be allocated and have length at least vn, |
|---|
| 521 | but each z[j] MUST NOT be initalized |
|---|
| 522 | b -- array holding the reductions mod each $m_i$. |
|---|
| 523 | It MUST have length at least len(self) |
|---|
| 524 | vn -- length of z and each b[i] |
|---|
| 525 | """ |
|---|
| 526 | self.mpz_crt_vec_tail(z, b, vn, 0, self.n) |
|---|
| 527 | |
|---|
| 528 | |
|---|
| 529 | cdef class MutableMultiModularBasis(MultiModularBasis): |
|---|
| 530 | """ |
|---|
| 531 | Class used for performing multi-modular methods, |
|---|
| 532 | with the possiblity of removing bad primes. |
|---|
| 533 | """ |
|---|
| 534 | |
|---|
| 535 | cdef mod_int last_prime(self): |
|---|
| 536 | if self.moduli[self.n-1] > self.__last_prime: |
|---|
| 537 | self.__last_prime = self.moduli[self.n-1] |
|---|
| 538 | return self.__last_prime |
|---|
| 539 | |
|---|
| 540 | def next_prime(self): |
|---|
| 541 | return self.next_prime_c() |
|---|
| 542 | |
|---|
| 543 | cdef mod_int next_prime_c(self) except -1: |
|---|
| 544 | self._extend_moduli(1) |
|---|
| 545 | return self.moduli[self.n-1] |
|---|
| 546 | |
|---|
| 547 | def replace_prime(self, ix): |
|---|
| 548 | return self.replace_prime_c(ix) |
|---|
| 549 | |
|---|
| 550 | cdef mod_int replace_prime_c(self, int ix) except -1: |
|---|
| 551 | r""" |
|---|
| 552 | Replace $m_{ix} in the list of moduli with a new |
|---|
| 553 | prime number greater than all others in the list, |
|---|
| 554 | and recompute all precomputations. |
|---|
| 555 | |
|---|
| 556 | INPUT: |
|---|
| 557 | ix -- index into list of moduli |
|---|
| 558 | |
|---|
| 559 | OUTPUT: |
|---|
| 560 | p -- the new prime modulus |
|---|
| 561 | """ |
|---|
| 562 | cdef int i |
|---|
| 563 | cdef mod_int new_p |
|---|
| 564 | |
|---|
| 565 | if ix < 0 or ix >= self.n: |
|---|
| 566 | raise IndexError, "index out of range" |
|---|
| 567 | |
|---|
| 568 | new_p = next_prime(self.last_prime()) |
|---|
| 569 | self.__last_prime = new_p |
|---|
| 570 | self.moduli[ix] = new_p |
|---|
| 571 | |
|---|
| 572 | self._refresh_products(ix) |
|---|
| 573 | self._refresh_precomputations(ix) |
|---|
| 574 | return new_p |
|---|