/*============================================================================= This file is part of FLINT. FLINT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. FLINT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FLINT; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =============================================================================*/ /****************************************************************************** Copyright (C) 2013 Tom Bachmann ******************************************************************************/ #include #include #include #include "fmpq_polyxx.h" #include "fmpz_polyxx.h" #include "nmod_polyxx.h" #include "flintxx/test/helpers.h" using namespace flint; void test_init() { nmod_polyxx p(10); tassert(p.length() == 0); tassert(p.modulus() == 10); nmodxx_ctx_srcref ctx = p.estimate_ctx(); tassert(p == nmod_polyxx::from_ground(nmodxx::red(0, ctx))); p.set_coeff(0, 1); tassert(p == nmod_polyxx::from_ground( (nmodxx::red(1, ctx)) + (nmodxx::red(0, ctx)))); tassert(nmod_polyxx::zero(13).is_zero() && nmod_polyxx::one(13).is_one()); } void test_manipulation() { mp_limb_t M = 31; nmod_polyxx p(M), q(M); nmodxx_ctx_srcref ctx = p.estimate_ctx(); p.set_coeff(5, 17 + M); tassert(p.degree() == 5); q.set_coeff(5, nmodxx::red(17, ctx)); tassert((q + nmod_polyxx(M)).get_coeff(5) == nmodxx::red(17, ctx)); p.set_coeff(0, nmodxx::red(1, ctx)); tassert(p != q); p.set_coeff(0, 0); tassert(p == q); tassert(p.length() == 6); p.realloc(0); tassert(p.is_zero() && !p.is_one()); p.set_coeff(0, 1); tassert(p.is_one()); } void test_assignment() { mp_limb_t M = 31; nmod_polyxx p(M), q(M); p.set_coeff(0, 1); tassert(p != q); p = q; tassert(p == q); p = "4 31 0 0 0 1"; q.set_coeff(3, 1); tassert(p == q); tassert(p == nmod_polyxx("4 31 0 0 0 1")); // TODO XXX this does not always fail? //assert_exception(p = "2 1 2"); assert_exception(p = "2 x 2"); assert_exception(nmod_polyxx("2 x 2")); } void test_conversion() { nmod_polyxx p(31); p.set_coeff(3, 1); tassert(p.to_string() == "4 31 0 0 0 1"); } void test_arithmetic() { mp_limb_t M = 31; nmod_polyxx g(M), h(M); nmodxx_ctx_srcref ctx = g.estimate_ctx(); g.set_coeff(0, 17); h.set_coeff(0, 15); tassert((g + h).get_coeff(0) == nmodxx::red(15 + 17, ctx)); frandxx state; g.set_randtest(state, 10); h.set_randtest(state, 10); tassert(((-g) + g).is_zero()); tassert(g - h == g + (-h)); tassert(g*nmodxx::red(3, ctx) == g + g + g); tassert(g.make_monic() == g*inv(g.get_coeff(g.degree()))); nmod_polyxx f(M);f.set_coeff(0, 15); tassert(f*g == nmodxx::red(15, ctx)*g); tassert(h.mul_classical(g) == h.mul_KS(g) && h.mul_KS(g) == h*g); f = h*g;f.truncate(7); tassert(f == mullow(h, g, 7)); tassert(f == h.mullow_KS(g, 7)); tassert(f == h.mullow_classical(g, 7)); f = (h*g).shift_right(7); tassert(f == h.mulhigh(g, 7).shift_right(7)); tassert(f == h.mulhigh_classical(g, 7).shift_right(7)); f = h / g; tassert(f*g + (h % g) == h); tassert(((h*g) % h).is_zero()); f.set_randtest(state, 10); g %= f; h %= f; tassert(h.mulmod(g, f) == ((h*g) % f)); tassert(h.mulmod(g, f) == h.mulmod_preinv(g, f, f.reverse(f.length()).inv_series(f.length()))); f = "3 31 1 0 1"; nmodxx x = nmodxx::red(7, ctx); tassert(evaluate(f, x) == x*x + nmodxx::red(1, ctx)); f.realloc(0);f.set_coeff(31, 1); tassert(evaluate(f, x) == x); tassert(f(x) == x); nmod_polyxx seven(M); seven.set_coeff(0, x); tassert(compose(f, seven).get_coeff(0) == f(x)); tassert(f(seven).length() == 1); nmod_vecxx vec1(2, ctx), vec2(2, ctx); vec1[0] = nmodxx::red(7, ctx); vec1[1] = nmodxx::red(15, ctx); vec2[0] = f(vec1[0]); vec2[1] = f(vec1[1]); tassert(f(vec1) == vec2); } void test_functions() { mp_limb_t M = 31; nmod_polyxx g(M); nmodxx_ctx_srcref ctx = g.estimate_ctx(); g.set_coeff(5, 15); tassert(g.max_bits() == 4); g.truncate(3); tassert(g.is_zero()); g.set_coeff(15, 1); tassert(g.shift_right(15).is_one()); tassert(g.shift_right(15).shift_left(15) == g); frandxx rand; g.set_randtest(rand, 15); tassert(g.length() <= 15); g.set_randtest_irreducible(rand, 15); tassert(g.length() <= 15); tassert(g.is_squarefree()); tassert(g.is_irreducible()); tassert(g == nmod_polyxx::bit_unpack(g.bit_pack(5u), 5u, ctx)); // multiplication, division, modulo tested in arithmetic tassert(g.pow(3u) == g*g*g); tassert(g.pow(5u) == g.pow_binexp(5u)); nmod_polyxx res(g.pow(15u));res.truncate(12); tassert(res == g.pow_trunc(15u, 12)); tassert(res == g.pow_trunc_binexp(15u, 12)); nmod_polyxx f(M);f.set_randtest(rand, 10); res = g.pow(10u) % f; tassert(res == g.powmod_binexp(10u, f)); tassert(res == g.powmod_binexp_preinv(10u, f, f.reverse(f.length()).inv_series(f.length()))); res = "5 31 1 1 1 1 1"; tassert(res.derivative().to_string() == "4 31 1 2 3 4"); tassert(g.integral().derivative() == g); tassert(f.divrem(g) == ltuple(f / g, f % g)); tassert(f.divrem_basecase(g) == f.divrem(g)); tassert(f.divrem_divconquer(g) == f.divrem(g)); tassert(f.div_basecase(g) == f / g); tassert(f.div_divconquer(g) == f / g); tassert(f.rem_basecase(g) == f % g); f.set_coeff(0, 17); // non-zero mod 31, so a unit res = f*f.inv_series(15);res.truncate(15); tassert(res.is_one()); tassert(f.inv_series(15) == f.inv_series_basecase(15)); tassert(f.inv_series(15) == f.inv_series_newton(15)); res = g * f.inv_series(15);res.truncate(15); tassert(g.div_series(f, 15) == res); f.set_coeff(f.degree(), 12); // unit tassert(g.div_newton(f) == g / f); tassert(g.divrem_newton(f) == g.divrem(f)); tassert(g.divrem(f) == g.divrem_newton_n_preinv(f, f.reverse(f.length()).inv_series(f.length()))); tassert(g /f == g.div_newton_n_preinv(f, f.reverse(f.length()).inv_series(f.length()))); res = "2 31 5 1"; tassert(f.div_root(-nmodxx::red(5, ctx)) == f / res); nmod_vecxx v(10, ctx); _nmod_vec_randtest(v._array(), rand._data(), v.size(), ctx._nmod()); tassert(f.evaluate_fast(v) == f(v)); tassert(f.evaluate_iter(v) == f(v)); nmod_vecxx xs(10, ctx); for(int i = 0;i < xs.size();++i) xs[i] = nmodxx::red(i, ctx); res = nmod_polyxx::interpolate(xs, v); tassert(res.degree() < xs.size()); for(int i = 0;i < xs.size();++i) tassert(res(xs[i]) == v[i]); tassert(nmod_polyxx::interpolate_fast(xs, v) == res); tassert(nmod_polyxx::interpolate_newton(xs, v) == res); tassert(nmod_polyxx::interpolate_barycentric(xs, v) == res); tassert(f(g) == f.compose_divconquer(g)); tassert(f(g) == f.compose_horner(g)); res = "2 31 7 1"; tassert(f(res) == f.taylor_shift(nmodxx::red(7, ctx))); tassert(f(res) == f.taylor_shift_horner(nmodxx::red(7, ctx))); tassert(f(res) == f.taylor_shift_convolution(nmodxx::red(7, ctx))); nmod_polyxx h(M); h.set_randtest(rand, 15); tassert(f.compose_mod(g, h) == f(g) % h); tassert(f.compose_mod(g, h) == f.compose_mod_horner(g, h)); tassert(f.compose_mod(g, h) == f.compose_mod_brent_kung(g, h)); tassert(f.compose_mod(g, h) == f.compose_mod_brent_kung_preinv(g, h, h.reverse(h.length()).inv_series(h.length()))); h.set_randtest_irreducible(rand, 12); tassert(h.gcd(f).is_one()); tassert(f.gcd_euclidean(f) == f.make_monic()); tassert(f.gcd_hgcd(g) == f.gcd(g)); nmod_polyxx R(M), S(M); ltupleref(res, R, S) = f.xgcd(g); tassert(res == R*f + S*g && res == gcd(f, g)); tassert(f.xgcd(g) == f.xgcd_hgcd(g)); tassert(f.xgcd(g) == f.xgcd_euclidean(g)); fmpz_polyxx lift1 = fmpz_polyxx::randtest(rand, 10, 6); fmpz_polyxx lift2 = fmpz_polyxx::randtest(rand, 10, 6); lift1.set_coeff(10, 1); lift2.set_coeff(10, 1); f = nmod_polyxx::reduce(lift1, ctx); for(int i = 0;i < f.length();++i) tassert(f.get_coeff(i) == nmodxx::red(lift1.get_coeff(i), ctx)); g = nmod_polyxx::reduce(lift2, ctx); tassert(f.resultant(g) == nmodxx::red(lift1.resultant(lift2), ctx)); tassert(f.resultant(g) == f.resultant_euclidean(g)); g.set_coeff(0, 0); res = f(g); res.truncate(15); tassert(f.compose_series(g, 15) == res); tassert(f.compose_series_horner(g, 15) == res); tassert(f.compose_series_brent_kung(g, 15) == res); tassert(f.compose_series_divconquer(g, 15) == res); res = "2 31 0 1"; g.set_coeff(1, 17); // unit tassert(g.compose_series(g.revert_series(15), 15) == res); tassert(g.revert_series_newton(15) == g.revert_series(15)); tassert(g.revert_series_lagrange(15) == g.revert_series(15)); tassert(g.revert_series_lagrange_fast(15) == g.revert_series(15)); f.set_coeff(0, 1); tassert(f.sqrt_series(15).pow_trunc(2u, 15) == f); tassert(f.invsqrt_series(15).pow_trunc(2u, 15) == f.inv_series(15)); tassert((f*f).sqrt() == f); res = "1 31 1"; assert_exception((f*f + res).sqrt().evaluate()); f = nmod_polyxx::product_roots(xs); tassert(f.degree() == xs.size()); for(int i = 0;i < xs.size();++i) tassert(f(nmodxx::red(i, ctx)).to() == 0); res = "2 31 0 1"; tassert(f.inflate(5u) == f(res.pow(5u))); tassert(f.inflate(5u).deflate(5u) == f); tassert(f.inflate(5u).deflation() >= 5); tassert(f.deflate(f.deflation()).deflation() == 1); g.set_randtest_irreducible(rand, 4); f.set_randtest_irreducible(rand, 5); res = f*g*g; tassert(res.remove(g) == 2 && res == f); } void test_transcendental_functions() { frandxx state; mp_limb_t M = 1031; // prime nmod_polyxx f(M); nmodxx_ctx_srcref ctx = f.estimate_ctx(); fmpq_polyxx lift = fmpq_polyxx::randtest(state, 10, 9); lift.set_coeff(0, 0); f = nmod_polyxx::reduce(lift, ctx); for(int i = 0;i < f.length();++i) tassert(f.get_coeff(i) == nmodxx::red(lift.get_coeff(i), ctx)); tassert(f.exp_series(15) == nmod_polyxx::reduce(lift.exp_series(15), ctx)); tassert(f.atan_series(15) == nmod_polyxx::reduce(lift.atan_series(15), ctx)); tassert(f.atanh_series(15) == nmod_polyxx::reduce(lift.atanh_series(15), ctx)); tassert(f.asin_series(15) == nmod_polyxx::reduce(lift.asin_series(15), ctx)); tassert(f.asinh_series(15) == nmod_polyxx::reduce(lift.asinh_series(15), ctx)); tassert(f.sin_series(15) == nmod_polyxx::reduce(lift.sin_series(15), ctx)); tassert(f.cos_series(15) == nmod_polyxx::reduce(lift.cos_series(15), ctx)); tassert(f.tan_series(15) == nmod_polyxx::reduce(lift.tan_series(15), ctx)); tassert(f.sinh_series(15) == nmod_polyxx::reduce(lift.sinh_series(15), ctx)); tassert(f.cosh_series(15) == nmod_polyxx::reduce(lift.cosh_series(15), ctx)); tassert(f.tanh_series(15) == nmod_polyxx::reduce(lift.tanh_series(15), ctx)); tassert(f.exp_series_basecase(15) == f.exp_series(15)); f.set_coeff(0, 1); lift.set_coeff(0, 1); tassert(f.log_series(15) == nmod_polyxx::reduce(lift.log_series(15), ctx)); f.realloc(0); nmodxx a = nmodxx::red(7, ctx); f.set_coeff(5, a); tassert(f.exp_series(15) == exp_series_monomial(a, 5u, 15)); f.set_coeff(0, 1); tassert(f.log_series(15) == log_series_monomial(a, 5u, 15)); } // test stuff which we should get automatically - addmul, references etc void test_extras() { // TODO } bool equiv_fac(const nmod_poly_factorxx& fac1, const nmod_poly_factorxx& fac2) { tassert(fac1.size() == 2); if(fac1.exp(0) == fac1.exp(1)) { if(fac2.exp(0) != fac1.exp(0) || fac2.exp(1) != fac1.exp(0)) return false; return (fac1.p(0) == fac2.p(0) && fac1.p(1) == fac2.p(1)) || (fac1.p(1) == fac2.p(0) && fac1.p(0) == fac2.p(1)); } if(fac1.size() != fac2.size()) return false; if(fac1.exp(0) == fac2.exp(0)) return fac1.exp(1) == fac2.exp(1) && fac1.p(0) == fac2.p(0) && fac1.p(1) == fac2.p(1); else return fac1.exp(0) == fac2.exp(1) && fac1.exp(1) == fac2.exp(0) && fac1.p(0) == fac2.p(1) && fac1.p(1) == fac2.p(0); } void test_factoring() { mp_limb_t M = 1031; nmod_polyxx f(M), g(M); frandxx state; f.set_randtest_irreducible(state, 4); f = f.make_monic(); g.set_randtest_irreducible(state, 5); g = g.make_monic(); nmod_poly_factorxx fac = factor(f*f*g); tassert(fac.size() == 2); if(fac.exp(0) == 1) { tassert(fac.p(0) == g); tassert(fac.p(1) == f && fac.exp(1) == 2); } else { tassert(fac.p(0) == f && fac.exp(0) == 2); tassert(fac.p(1) == g && fac.exp(1) == 1); } nmod_poly_factorxx fac2;fac2 = fac;fac2.pow(2); fac.insert(g, 1); fac.insert(f, 2); tassert(fac == fac2); nmod_polyxx prod(f*f*f*g*g); fac = factor(prod); tassert(equiv_fac(fac, factor_cantor_zassenhaus(prod))); tassert(equiv_fac(factor(f*g), factor_berlekamp(f*g))); tassert(equiv_fac(fac, factor_kaltofen_shoup(prod))); tassert(equiv_fac(fac, factor_with_cantor_zassenhaus(prod))); tassert(equiv_fac(fac, factor_with_berlekamp(prod))); tassert(equiv_fac(fac, factor_with_kaltofen_shoup(prod))); std::vector degs(2); fac.realloc(0);fac.set_factor_distinct_deg(f*g, degs); tassert(degs.size() == 2); tassert((degs[0] == f.degree() && degs[1] == g.degree()) || (degs[1] == f.degree() && degs[0] == g.degree())); // TODO test set_factor_equal_deg* if(0) print(fac); // make sure this compiles } void test_reduction_reconstruction() { std::vector primes; primes.push_back(1031); primes.push_back(1033); primes.push_back(1039); mp_limb_t M = primes[0]; nmodxx_ctx ctx(M); frandxx rand; fmpz_polyxx A = fmpz_polyxx::randtest(rand, 10, 8); nmod_polyxx Ap = nmod_polyxx::reduce(A, ctx); for(slong i = 0;i < A.length();++i) tassert(Ap.get_coeff(i) == nmodxx::red(A.get_coeff(i), ctx)); tassert(A == fmpz_polyxx::lift(Ap)); for(slong i = 0;i < A.length();++i) A.coeff(i) = abs(A.coeff(i)); tassert(A == fmpz_polyxx::lift_unsigned(nmod_polyxx::reduce(A, ctx))); A = fmpz_polyxx::randtest(rand, 10, 25); fmpzxx prod(1); fmpz_polyxx res; for(unsigned i = 0;i < primes.size();++i) { res = res.CRT(prod, nmod_polyxx::reduce(A, primes[i]), true); prod *= primes[i]; } tassert(res == A); } void test_randomisation() { frandxx state1, state2; mp_limb_t N = 1031; nmod_polyxx p(N); p.set_randtest(state1, 7); tassert(p == nmod_polyxx::randtest(N, state2, 7)); p.set_randtest_irreducible(state1, 7); tassert(p == nmod_polyxx::randtest_irreducible(N, state2, 7)); } int main() { std::cout << "nmod_polyxx...."; test_init(); test_manipulation(); test_assignment(); test_conversion(); test_arithmetic(); test_functions(); test_transcendental_functions(); test_extras(); test_factoring(); test_reduction_reconstruction(); test_randomisation(); frandxx rand; test_print_read(nmod_polyxx::randtest(7, rand, 5)); std::cout << "PASS" << std::endl; return 0; }