/* Copyright (C) 2021 Daniel Schultz This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ #include "fmpz.h" #include "fmpz_vec.h" static void _fmpz_multi_mod_fit_length(fmpz_multi_mod_t P, slong k) { slong i; k = FLINT_MAX(WORD(1), k); for (i = k; i < P->alloc; i++) { fmpz_clear(P->prog[i].modulus); fmpz_clear(P->moduli + i); } P->prog = FLINT_ARRAY_REALLOC(P->prog, k, _fmpz_multi_mod_instr); P->moduli = FLINT_ARRAY_REALLOC(P->moduli, k, fmpz); for (i = P->alloc; i < k; i++) { fmpz_init(P->prog[i].modulus); fmpz_init(P->moduli + i); } P->alloc = k; } static int _fill_sort(slong * link, fmpz * v, slong j) { while (j >= 0) { int cmp = fmpz_cmp(v + j, v + j + 1); if (fmpz_is_zero(v + j) || fmpz_is_zero(v + j + 1)) return 0; /* hit the smaller branch first */ if (cmp > 0) { fmpz_swap(v + j, v + j + 1); SLONG_SWAP(link[j], link[j + 1]); } if (!_fill_sort(link, v, link[j + 0])) return 0; j = link[j + 1]; } return 1; } static void _fill_prog( fmpz_multi_mod_t P, slong * link, fmpz * v, slong j, slong a_idx) { slong i, b_idx, c_idx; FLINT_ASSERT(j >= 0); if (link[j] >= 0) { b_idx = a_idx + 1; } else { b_idx = -1 - link[j]; FLINT_ASSERT(b_idx < P->alloc); fmpz_set(P->moduli + b_idx, v + j); b_idx = -1 - b_idx; } if (link[j + 1] >= 0) { c_idx = a_idx + 1; } else { c_idx = -1 - link[j + 1]; FLINT_ASSERT(c_idx < P->alloc); fmpz_set(P->moduli + c_idx, v + j + 1); c_idx = -1 - c_idx; } i = P->length; FLINT_ASSERT(i < P->alloc); P->prog[i].in_idx = a_idx; P->prog[i].out_idx = b_idx; fmpz_set(P->prog[i].modulus, v + j); P->length = i + 1; if (link[j + 0] >= 0) _fill_prog(P, link, v, link[j + 0], b_idx); i = P->length; FLINT_ASSERT(i < P->alloc); P->prog[i].in_idx = a_idx; P->prog[i].out_idx = c_idx; fmpz_set(P->prog[i].modulus, v + j + 1); P->length = i + 1; if (link[j + 1] >= 0) _fill_prog(P, link, v, link[j + 1], c_idx); P->localsize = FLINT_MAX(P->localsize, a_idx + 1); } int fmpz_multi_mod_precompute( fmpz_multi_mod_t P, const fmpz * f, slong r) { slong i, j; slong * link; fmpz * v; FLINT_ASSERT(r > 0); _fmpz_multi_mod_fit_length(P, 2*r); P->length = 0; P->localsize = 1; P->moduli_count = r; P->min_modulus_bits = fmpz_bits(f + 0); if (r < 2) { P->good = !fmpz_is_zero(f + 0); if (P->good) { fmpz_abs(P->moduli + 0, f + 0); P->prog[0].in_idx = 0; P->prog[0].out_idx = -1; fmpz_set(P->prog[0].modulus, P->moduli + 0); P->length = 1; } goto done; } link = FLINT_ARRAY_ALLOC(2*r - 2, slong); v = _fmpz_vec_init(2*r - 2); for (i = 0; i < r; i++) { flint_bitcnt_t this_bits = fmpz_bits(f + i); P->min_modulus_bits = FLINT_MIN(P->min_modulus_bits, this_bits); fmpz_abs(v + i, f + i); link[i] = -i - 1; } for (i = r, j = 0; j < 2*r - 4; i++, j += 2) { slong s, minp; const fmpz * mind; minp = j; mind = v + j; for (s = j + 1; s < i; s++) { if (fmpz_cmp(v + s, mind) < 0) { mind = v + s; minp = s; } } fmpz_swap(v + j, v + minp); SLONG_SWAP(link[j], link[minp]); minp = j + 1; mind = v + j + 1; for (s = j + 2; s < i; s++) { if (fmpz_cmp(v + s, mind) < 0) { mind = v + s; minp = s; } } fmpz_swap(v + j + 1, v + minp); SLONG_SWAP(link[j + 1], link[minp]); fmpz_mul(v + i, v + j, v + j + 1); link[i] = j; } P->good = _fill_sort(link, v, 2*r - 4); if (P->good) _fill_prog(P, link, v, 2*r - 4, 0); flint_free(link); _fmpz_vec_clear(v, 2*r - 2); done: P->temp1loc = P->localsize++; if (!P->good) { P->length = 0; } return P->good; }