diff --git a/doc/source/fmpz_mod_mpoly.rst b/doc/source/fmpz_mod_mpoly.rst index 46823749f4..7a40c58829 100644 --- a/doc/source/fmpz_mod_mpoly.rst +++ b/doc/source/fmpz_mod_mpoly.rst @@ -436,6 +436,7 @@ Evaluation Return `1` for success and `0` for failure. .. function:: int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) + int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) int fmpz_mod_mpoly_compose_fmpz_mod_mpoly(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) Set *A* to the evaluation of *B* where the variables are replaced by the corresponding elements of the array *C*. diff --git a/src/fmpq_mpoly/test/t-compose_fmpq_mpoly.c b/src/fmpq_mpoly/test/t-compose_fmpq_mpoly.c index 1ac4529a82..9a33e6c177 100644 --- a/src/fmpq_mpoly/test/t-compose_fmpq_mpoly.c +++ b/src/fmpq_mpoly/test/t-compose_fmpq_mpoly.c @@ -17,13 +17,15 @@ TEST_FUNCTION_START(fmpq_mpoly_compose_fmpq_mpoly, state) slong i, j, v; { - fmpq_mpoly_t A, A1, A2, B; + slong nvarsAC = 2, nvarsB = 3; + fmpq_mpoly_t A, A1, A2, B, B1; fmpq_mpoly_struct * Cp[3]; fmpq_mpoly_struct C[3]; fmpq_mpoly_ctx_t ctxAC, ctxB; + slong * c; - fmpq_mpoly_ctx_init(ctxB, 3, ORD_LEX); - fmpq_mpoly_ctx_init(ctxAC, 2, ORD_LEX); + fmpq_mpoly_ctx_init(ctxB, nvarsB, ORD_LEX); + fmpq_mpoly_ctx_init(ctxAC, nvarsAC, ORD_LEX); fmpq_mpoly_init(B, ctxB); fmpq_mpoly_init(A, ctxAC); @@ -42,39 +44,61 @@ TEST_FUNCTION_START(fmpq_mpoly_compose_fmpq_mpoly, state) fmpq_mpoly_set_str_pretty(C + 1, "x1 - x2", NULL, ctxAC); fmpq_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); if (fmpq_mpoly_compose_fmpq_mpoly(A, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 1\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 1\n"); fmpq_mpoly_set_str_pretty(C + 0, "x1", NULL, ctxAC); fmpq_mpoly_set_str_pretty(C + 1, "2*x2", NULL, ctxAC); fmpq_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); if (fmpq_mpoly_compose_fmpq_mpoly(A, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 2\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 2\n"); fmpq_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); fmpq_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); fmpq_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); if (!fmpq_mpoly_compose_fmpq_mpoly(A, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check example 3\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check example 3\n"); + + /* Aliased generator composition */ + c = (slong *) flint_malloc(nvarsB*sizeof(slong)); + fmpq_mpoly_init(B1, ctxB); + fmpq_mpoly_set(B1, B, ctxB); + for (i = 0; i < nvarsB; i++) + c[i] = i; + + fmpq_mpoly_compose_fmpq_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpq_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with aliased generators\n"); + + /* Reverse the generators, twice */ + for (i = 0; i < nvarsB; i++) + c[i] = nvarsB - i - 1; + + fmpq_mpoly_compose_fmpq_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (fmpq_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with reversed aliased generators\n"); + + fmpq_mpoly_compose_fmpq_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpq_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with un-reversed aliased generators\n"); + + /* Composition with zero polys */ + fmpq_mpoly_zero(B1, ctxB); + + fmpq_mpoly_compose_fmpq_mpoly_gen(A, B1, c, ctxB, ctxAC); + if (!fmpq_mpoly_is_zero(B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with generators of zero poly\n"); + + fmpq_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); + fmpq_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); + fmpq_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!fmpq_mpoly_compose_fmpq_mpoly(A, B1, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check example 4\n"); + + if (!fmpq_mpoly_is_zero(A, ctxAC)) + TEST_FUNCTION_FAIL("Check composition with zero poly\n"); fmpq_mpoly_clear(B, ctxB); fmpq_mpoly_clear(A, ctxAC); - fmpq_mpoly_clear(A1, ctxAC); - fmpq_mpoly_clear(A2, ctxAC); for (i = 0; i < 3; i++) fmpq_mpoly_clear(C + i, ctxAC); @@ -131,22 +155,12 @@ TEST_FUNCTION_START(fmpq_mpoly_compose_fmpq_mpoly, state) fmpq_mpoly_compose_fmpq_mpoly_gen(A, B, c, ctxB, ctxAC); if (!fmpq_mpoly_compose_fmpq_mpoly(A1, B, C, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition success with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success with generators\n" + "i: %wd, j: %wd\n", i, j); if (!fmpq_mpoly_equal(A, A1, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with generators\n" + "i: %wd, j: %wd\n", i, j); fmpq_mpoly_assert_canonical(A, ctxAC); fmpq_mpoly_assert_canonical(A1, ctxAC); @@ -196,20 +210,10 @@ TEST_FUNCTION_START(fmpq_mpoly_compose_fmpq_mpoly, state) coeff_bits = n_randint(state, 100) + 1; fmpq_mpoly_randtest_bits(f, state, len1, coeff_bits, exp_bits, ctx); if (!fmpq_mpoly_compose_fmpq_mpoly(g, f, vals1, ctx, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); if (!fmpq_mpoly_equal(f, g, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition with identity\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with identity\ni: %wd\n", i); fmpq_mpoly_clear(g, ctx); fmpq_mpoly_clear(f, ctx); @@ -276,41 +280,21 @@ TEST_FUNCTION_START(fmpq_mpoly_compose_fmpq_mpoly, state) vals3[v] = (fmpq *) flint_malloc(sizeof(fmpq)); fmpq_init(vals3[v]); if (!fmpq_mpoly_evaluate_all_fmpq(vals3[v], vals1[v], vals2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check evaluation success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check evaluation success\ni: %wd\n", i); } fmpq_mpoly_randtest_bound(f, state, len1, coeff_bits, exp_bound1, ctx1); if (!fmpq_mpoly_compose_fmpq_mpoly(g, f, vals1, ctx1, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); fmpq_mpoly_assert_canonical(g, ctx2); if (!fmpq_mpoly_evaluate_all_fmpq(fe, f, vals3, ctx1) || !fmpq_mpoly_evaluate_all_fmpq(ge, g, vals2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check evaluation success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check evaluation success\ni: %wd\n", i); if (!fmpq_equal(fe, ge)) - { - printf("FAIL\n"); - flint_printf("Check composition and evalall commute\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition and evalall commute\ni: %wd\n", i); for (v = 0; v < nvars1; v++) { diff --git a/src/fmpz_mod_mpoly.h b/src/fmpz_mod_mpoly.h index 4c29b71afe..e103095110 100644 --- a/src/fmpz_mod_mpoly.h +++ b/src/fmpz_mod_mpoly.h @@ -570,10 +570,19 @@ int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC); +int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(fmpz_mod_mpoly_t A, + const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, + const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC); + int fmpz_mod_mpoly_compose_fmpz_mod_mpoly(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC); +void fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(fmpz_mod_mpoly_t A, + const fmpz_mod_mpoly_t B, const slong * c, + const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC); + + /* Multiplication ************************************************************/ void fmpz_mod_mpoly_mul(fmpz_mod_mpoly_t A, diff --git a/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly.c b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly.c index a354e5a693..984cb6e531 100644 --- a/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly.c +++ b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly.c @@ -64,14 +64,14 @@ int fmpz_mod_mpoly_compose_fmpz_mod_mpoly( matrix_no_good: fmpz_mat_clear(M); -/* + for (i = 0; i < ctxB->minfo->nvars; i++) { if (C[i]->length > 1) { - return nmod_mpoly_compose_nmod_mpoly_horner(A, B, C, ctxB, ctxAC); + return fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A, B, C, ctxB, ctxAC); } } -*/ + return fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A, B, C, ctxB, ctxAC); } diff --git a/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_gen.c b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_gen.c new file mode 100644 index 0000000000..4d16f42566 --- /dev/null +++ b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_gen.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2020 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz_mat.h" +#include "mpoly.h" +#include "fmpz_mod_mpoly.h" + +/* evaluate B(x_1,...,x_n) at x_i = y_c[i], y_j are vars of ctxAC */ +void fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(fmpz_mod_mpoly_t A, + const fmpz_mod_mpoly_t B, const slong * c, + const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) +{ + fmpz_mat_t M; + + if (B->length == 0) + { + fmpz_mod_mpoly_zero(A, ctxAC); + return; + } + + fmpz_mat_init(M, ctxAC->minfo->nfields + 1, ctxB->minfo->nfields); + mpoly_compose_mat_gen(M, c, ctxB->minfo, ctxAC->minfo); + + if (A == B) + { + fmpz_mod_mpoly_t T; + fmpz_mod_mpoly_init(T, ctxAC); + _fmpz_mod_mpoly_compose_mat(T, B, M, ctxB, ctxAC); + fmpz_mod_mpoly_swap(A, T, ctxAC); + fmpz_mod_mpoly_clear(T, ctxAC); + } + else + { + _fmpz_mod_mpoly_compose_mat(A, B, M, ctxB, ctxAC); + } + + fmpz_mat_clear(M); + + return; +} diff --git a/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_horner.c b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_horner.c new file mode 100644 index 0000000000..24d01119c3 --- /dev/null +++ b/src/fmpz_mod_mpoly/compose_fmpz_mod_mpoly_horner.c @@ -0,0 +1,439 @@ +/* + Copyright (C) 2018 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "fmpz_vec.h" +#include "mpoly.h" +#include "fmpz_mod_mpoly.h" + +/* +The conversion to Horner form can be stated as recursive. However, the call +stack has depth proportial to the length of the input polynomial in the worst +case. Therefore, we must convert it to an iterative algorithm. + +The procedure is + +HornerForm(f): + + if f is simple to evaluate + + return eval(f) + + else + choose a variable v and the smallest non zero exponent e appearing + in the terms of f + + write f = q * v^e + r where r is independent of the variable v + + return HornerForm(q) * v^e + HornerForm(r) +*/ + +typedef struct +{ + slong f; + slong r; + slong v_var; + fmpz_t v_exp; /* will be managed as stack grows / shrinks */ + int ret; +} stack_entry_struct; + +typedef stack_entry_struct stack_entry_t[1]; + + +/* A = A * X^pow */ +static int _fmpz_mod_mpoly_pmul(fmpz_mod_mpoly_t A, const fmpz_mod_mpoly_t X, + const fmpz_t pow, fmpz_mod_mpoly_t T, const fmpz_mod_mpoly_ctx_t ctx) +{ + ulong p; + FLINT_ASSERT(fmpz_sgn(pow) > 0); + + if (!fmpz_fits_si(pow)) + { + if (!fmpz_mod_mpoly_pow_fmpz(T, X, pow, ctx)) + { + fmpz_mod_mpoly_zero(A, ctx); + return 0; + } + + fmpz_mod_mpoly_mul(A, A, T, ctx); + return 1; + } + + p = fmpz_get_ui(pow); + + if (X->length <= WORD(2) || (slong) (A->length/p) < X->length) + { + if (!fmpz_mod_mpoly_pow_ui(T, X, p, ctx)) + { + fmpz_mod_mpoly_zero(A, ctx); + return 0; + } + + fmpz_mod_mpoly_mul(A, A, T, ctx); + } + else + { + while (p >= 1) + { + fmpz_mod_mpoly_mul(T, A, X, ctx); + fmpz_mod_mpoly_swap(A, T, ctx); + p--; + } + } + + return 1; +} + +/* evaluate B(xbar) at xbar = C */ +int fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(fmpz_mod_mpoly_t A, + const fmpz_mod_mpoly_t B, fmpz_mod_mpoly_struct * const * C, + const fmpz_mod_mpoly_ctx_t ctxB, const fmpz_mod_mpoly_ctx_t ctxAC) +{ + int success = 1; + int ret; + slong nvars = ctxB->minfo->nvars; + slong i, j, k, cur, next, f, r, f_prev, r_prev, v; + slong sp, rp; + stack_entry_struct * stack; + fmpz_mod_mpoly_struct * regs; + fmpz_mod_mpoly_t temp; + slong * rtypes; + ulong totalcounts, maxcounts; + ulong * counts; + slong Blen = B->length; + slong * Blist; + const fmpz * Bcoeff = B->coeffs; + const ulong * Bexp = B->exps; + flint_bitcnt_t Bbits = B->bits; + slong BN = mpoly_words_per_exp(Bbits, ctxB->minfo); + fmpz * Buexp; + fmpz * mdegs; + fmpz_t score, tz; + TMP_INIT; + + if (Blen < 1) + { + fmpz_mod_mpoly_zero(A, ctxAC); + return 1; + } + + if (nvars < 1) + { + FLINT_ASSERT(Blen == 1); + fmpz_mod_mpoly_set_fmpz(A, B->coeffs + 0, ctxAC); + return 1; + } + + FLINT_ASSERT(A != B); + FLINT_ASSERT(Blen > 0); + + TMP_START; + + fmpz_init(score); + fmpz_init(tz); + + /* unpack B exponents */ + Buexp = _fmpz_vec_init(nvars*Blen); + for (i = 0; i < Blen; i++) + mpoly_get_monomial_ffmpz(Buexp + nvars*i, Bexp + BN*i, Bbits, ctxB->minfo); + + counts = (ulong *) TMP_ALLOC(nvars*sizeof(ulong)); + mdegs = _fmpz_vec_init(nvars); + + /* stack */ + sp = -WORD(1); /* start with empty stack */ + stack = (stack_entry_struct *) TMP_ALLOC(nvars*(Blen + 1)*sizeof(stack_entry_struct)); + Blist = (slong *) TMP_ALLOC(Blen*sizeof(slong)); + + /* registers of polynomials */ + rp = 0; + rtypes = (slong *) TMP_ALLOC((nvars + 1)*sizeof(slong)); + regs = (fmpz_mod_mpoly_struct *) TMP_ALLOC(nvars*sizeof(fmpz_mod_mpoly_struct)); + for (i = 0; i < nvars; i++) + fmpz_mod_mpoly_init(regs + i, ctxAC); + fmpz_mod_mpoly_init(temp, ctxAC); + + /* polynomials will be stored as link lists */ + for (i = 0; i + 1 < Blen; i++) + Blist[i] = i + 1; + Blist[i] = -WORD(1); + + sp++; + fmpz_init((stack + sp)->v_exp); + (stack + sp)->ret = 0; + (stack + sp)->f = 0; + +HornerForm: + + f = (stack + sp)->f; + + FLINT_ASSERT(f != -WORD(1)); /* f is not supposed to be zero */ + + /* obtain a count of the number of terms containing each variable */ + for (i = 0; i < nvars; i++) + { + counts[i] = 0; + fmpz_set_si(mdegs + i, -WORD(1)); + } + + for (j = f; j != -WORD(1); j = Blist[j]) + { + for (i = 0; i < nvars; i++) + { + if (!fmpz_is_zero(Buexp + nvars*j + i )) + { + counts[i]++; + if (fmpz_sgn(mdegs + i) < 0 + || fmpz_cmp(mdegs + i, Buexp + nvars*j + i) > 0) + { + fmpz_set(mdegs + i, Buexp + nvars*j + i); + } + } + } + } + + totalcounts = 0; + maxcounts = 0; + v = -WORD(1); + for (i = 0; i < nvars; i++) + { + maxcounts = FLINT_MAX(maxcounts, counts[i]); + totalcounts += counts[i]; + if (counts[i] != 0) + v = i; + } + + /* handle simple cases */ + if (totalcounts == 0) + { + FLINT_ASSERT(Blist[f] == -WORD(1)); /* f should have had only one term */ + rtypes[rp] = f; + goto HornerFormReturn; + } + else if (totalcounts == 1) + { + FLINT_ASSERT(!fmpz_is_zero(Buexp + nvars*f + v)); /* this term should not be a scalar */ + if (!fmpz_mod_mpoly_pow_fmpz(regs + rp, C[v], Buexp + nvars*f + v, ctxAC)) + { + success = 0; + } + fmpz_mod_mpoly_scalar_mul_fmpz(regs + rp, regs + rp, Bcoeff + f, ctxAC); + + if (Blist[f] != -WORD(1)) /* if f has a second term */ + { + /* this term should be a scalar */ + FLINT_ASSERT(fmpz_is_zero(Buexp + nvars*Blist[f] + v)); + fmpz_mod_mpoly_add_fmpz(regs + rp, regs + rp, Bcoeff + Blist[f], ctxAC); + } + + rtypes[rp] = -WORD(1); + + goto HornerFormReturn; + } + + /* pick best power to pull out */ + k = 0; + if (maxcounts == 1) + { + fmpz_set_si(score, -WORD(1)); + for (i = 0; i < nvars; i++) + { + if (counts[i] == 1 && (fmpz_sgn(score) < 0 + || fmpz_cmp(mdegs + i, score) < 0)) + { + FLINT_ASSERT(fmpz_sgn(mdegs + i) > 0); + fmpz_set(score, mdegs + i); + k = i; + } + } + } + else + { + fmpz_zero(score); + for (i = 0; i < nvars; i++) + { + if (counts[i] > 1) + { + FLINT_ASSERT(fmpz_sgn(mdegs + i) > 0); + fmpz_mul_ui(tz, mdegs + i, counts[i] - 1); + if (fmpz_cmp(tz, score) > 0) + { + fmpz_swap(score, tz); + k = i; + } + } + } + } + + /* set variable power v */ + (stack + sp)->v_var = k; + fmpz_set((stack + sp)->v_exp, mdegs + k); + + /* scan f and split into q and v with f = q*v + r then set f = q */ + r = -WORD(1); + cur = f; + f_prev = -WORD(1); + r_prev = -WORD(1); + while (cur != -WORD(1)) + { + next = Blist[cur]; + if (fmpz_is_zero(Buexp + nvars*cur + k)) + { + if (f_prev == -WORD(1)) + f = Blist[cur]; + else + Blist[f_prev] = Blist[cur]; + + if (r_prev == -WORD(1)) + r = cur; + else + Blist[r_prev] = cur; + + Blist[cur] = -WORD(1); + r_prev = cur; + } + else + { + /* mdegs[k] should be minimum non zero exponent */ + fmpz_sub(Buexp + nvars*cur + k, Buexp + nvars*cur + k, mdegs + k); + FLINT_ASSERT(fmpz_sgn(Buexp + nvars*cur + k) >= 0); + f_prev = cur; + } + cur = next; + } + (stack + sp)->r = r; + + /* convert the quotient */ + sp++; + fmpz_init((stack + sp)->v_exp); + (stack + sp)->ret = 1; + (stack + sp)->f = f; + goto HornerForm; + +HornerForm1: + + /* convert the remainder */ + r = (stack + sp)->r; + if (r != -WORD(1)) + { + /* remainder is non zero */ + rp++; + FLINT_ASSERT(0 <= rp && rp <= nvars); + sp++; + fmpz_init((stack + sp)->v_exp); + (stack + sp)->ret = 2; + (stack + sp)->f = r; + goto HornerForm; + +HornerForm2: + + if (rtypes[rp - 1] == -WORD(1) && rtypes[rp] == -WORD(1)) + { + /* both quotient and remainder are polynomials */ + if (!_fmpz_mod_mpoly_pmul(regs + rp - 1, C[(stack + sp)->v_var], + (stack + sp)->v_exp, temp, ctxAC)) + { + success = 0; + } + fmpz_mod_mpoly_add(temp, regs + rp - 1, regs + rp, ctxAC); + fmpz_mod_mpoly_swap(temp, regs + rp - 1, ctxAC); + } + else if (rtypes[rp - 1] == -WORD(1) && rtypes[rp] != -WORD(1)) + { + /* quotient is a polynomial, remainder is a scalar */ + if (!_fmpz_mod_mpoly_pmul(regs + rp - 1, C[(stack + sp)->v_var], + (stack + sp)->v_exp, temp, ctxAC)) + { + success = 0; + } + fmpz_mod_mpoly_add_fmpz(regs + rp - 1, regs + rp - 1, + Bcoeff + rtypes[rp], ctxAC); + } + else if (rtypes[rp - 1] != -WORD(1) && rtypes[rp] == -WORD(1)) + { + /* quotient is a scalar, remainder is a polynomial */ + if (!fmpz_mod_mpoly_pow_fmpz(temp, C[(stack + sp)->v_var], + (stack + sp)->v_exp, ctxAC)) + { + success = 0; + } + fmpz_mod_mpoly_scalar_mul_fmpz(temp, temp, Bcoeff + rtypes[rp - 1], ctxAC); + fmpz_mod_mpoly_add(regs + rp - 1, temp, regs + rp, ctxAC); + } + else + { + /* quotient is a scalar, remainder is a scalar */ + FLINT_ASSERT(0); /* this should have been handled by simple case */ + } + rp--; + FLINT_ASSERT(0 <= rp && rp <= nvars); + } + else + { + /* remainder is zero */ + FLINT_ASSERT(rtypes[rp] == -WORD(1)); /* quotient is not a scalar */ + + /* quotient is a polynomial */ + if (!_fmpz_mod_mpoly_pmul(regs + rp, C[(stack + sp)->v_var], + (stack + sp)->v_exp, temp, ctxAC)) + { + success = 0; + } + } + + rtypes[rp] = -WORD(1); + +HornerFormReturn: + + if (!success) + { + while (sp >= 0) + { + fmpz_clear((stack + sp)->v_exp); + sp--; + } + + goto cleanup; + } + + ret = (stack + sp)->ret; + fmpz_clear((stack + sp)->v_exp); + sp--; + if (ret == 1) goto HornerForm1; + if (ret == 2) goto HornerForm2; + + FLINT_ASSERT(rp == 0); + FLINT_ASSERT(sp == -WORD(1)); + + if (rtypes[rp] == -WORD(1)) + { + fmpz_mod_mpoly_swap(A, regs + rp, ctxAC); + } + else + { + fmpz_mod_mpoly_set_fmpz(A, Bcoeff + rtypes[rp], ctxAC); + } + +cleanup: + + for (i = 0; i < nvars; i++) + fmpz_mod_mpoly_clear(regs + i, ctxAC); + fmpz_mod_mpoly_clear(temp, ctxAC); + + fmpz_clear(score); + fmpz_clear(tz); + _fmpz_vec_clear(mdegs, nvars); + _fmpz_vec_clear(Buexp, nvars*Blen); + + TMP_END; + + return success; +} diff --git a/src/fmpz_mod_mpoly/test/main.c b/src/fmpz_mod_mpoly/test/main.c index c29f6419ec..7331db8fd3 100644 --- a/src/fmpz_mod_mpoly/test/main.c +++ b/src/fmpz_mod_mpoly/test/main.c @@ -15,6 +15,7 @@ #include "t-add_sub_fmpz.c" #include "t-add_sub_si.c" #include "t-cmp.c" +#include "t-compose_fmpz_mod_mpoly.c" #include "t-degree.c" #include "t-degrees_term_exp_fits_ui_si.c" #include "t-derivative.c" @@ -65,6 +66,7 @@ test_struct tests[] = TEST_FUNCTION(fmpz_mod_mpoly_add_sub), TEST_FUNCTION(fmpz_mod_mpoly_add_sub_fmpz), TEST_FUNCTION(fmpz_mod_mpoly_add_sub_si), + TEST_FUNCTION(fmpz_mod_mpoly_compose), TEST_FUNCTION(fmpz_mod_mpoly_cmp), TEST_FUNCTION(fmpz_mod_mpoly_degree), TEST_FUNCTION(fmpz_mod_mpoly_degrees_term_exp_fits_ui_si), diff --git a/src/fmpz_mod_mpoly/test/t-compose_fmpz_mod_mpoly.c b/src/fmpz_mod_mpoly/test/t-compose_fmpz_mod_mpoly.c new file mode 100644 index 0000000000..ce8414f643 --- /dev/null +++ b/src/fmpz_mod_mpoly/test/t-compose_fmpz_mod_mpoly.c @@ -0,0 +1,385 @@ +/* + Copyright (C) 2018 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz_mod_mpoly.h" + +TEST_FUNCTION_START(fmpz_mod_mpoly_compose, state) +{ + slong i, j, v; + + { + fmpz_t p; + slong nvarsAC = 2, nvarsB = 3; + fmpz_mod_mpoly_t A, A1, A2, B, B1; + fmpz_mod_mpoly_struct * Cp[3]; + fmpz_mod_mpoly_struct C[3]; + fmpz_mod_mpoly_ctx_t ctxAC, ctxB; + slong * c; + + fmpz_init(p); + fmpz_randprime(p, state, n_randint(state, 15) + 2, 0); + fmpz_mod_mpoly_ctx_init(ctxB, nvarsB, ORD_LEX, p); + fmpz_mod_mpoly_ctx_init(ctxAC, nvarsAC, ORD_LEX, p); + + fmpz_mod_mpoly_init(B, ctxB); + fmpz_mod_mpoly_init(A, ctxAC); + fmpz_mod_mpoly_init(A1, ctxAC); + fmpz_mod_mpoly_init(A2, ctxAC); + for (i = 0; i < 3; i++) + { + Cp[i] = C + i; + fmpz_mod_mpoly_init(C + i, ctxAC); + } + + fmpz_mod_mpoly_set_str_pretty(B, + "1 + x1*x2^2 + x2^9999999999999999999999999*x3^9", NULL, ctxB); + + fmpz_mod_mpoly_set_str_pretty(C + 0, "x1 + x2", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 1, "x1 - x2", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (fmpz_mod_mpoly_compose_fmpz_mod_mpoly(A, B, Cp, ctxB, ctxAC) || + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check non-example 1\n"); + + fmpz_mod_mpoly_set_str_pretty(C + 0, "x1", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 1, "2*x2", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(A, B, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check non-example 2\n"); + + fmpz_mod_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(A, B, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check example 3\n"); + + /* Aliased generator composition */ + c = (slong *) flint_malloc(nvarsB*sizeof(slong)); + fmpz_mod_mpoly_init(B1, ctxB); + fmpz_mod_mpoly_set(B1, B, ctxB); + for (i = 0; i < nvarsB; i++) + c[i] = i; + + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpz_mod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with aliased generators\n"); + + /* Reverse the generators, twice */ + for (i = 0; i < nvarsB; i++) + c[i] = nvarsB - i - 1; + + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (fmpz_mod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with reversed aliased generators\n"); + + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpz_mod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with un-reversed aliased generators\n"); + + /* Composition with zero polys */ + fmpz_mod_mpoly_zero(B1, ctxB); + + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(A, B1, c, ctxB, ctxAC); + if (!fmpz_mod_mpoly_is_zero(B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with generators of zero poly\n"); + + fmpz_mod_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); + fmpz_mod_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(A, B1, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A1, B1, Cp, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A2, B1, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check example 4\n"); + + if (!fmpz_mod_mpoly_is_zero(A, ctxAC) || + !fmpz_mod_mpoly_is_zero(A1, ctxAC) || + !fmpz_mod_mpoly_is_zero(A2, ctxAC)) + TEST_FUNCTION_FAIL("Check composition with zero poly\n"); + + fmpz_mod_mpoly_clear(B, ctxB); + fmpz_mod_mpoly_clear(A, ctxAC); + fmpz_mod_mpoly_clear(A1, ctxAC); + fmpz_mod_mpoly_clear(A2, ctxAC); + for (i = 0; i < 3; i++) + fmpz_mod_mpoly_clear(C + i, ctxAC); + + fmpz_mod_mpoly_ctx_clear(ctxB); + fmpz_mod_mpoly_ctx_clear(ctxAC); + } + + /* check composition with generators */ + for (i = 0; i < 20*flint_test_multiplier(); i++) + { + fmpz_t p; + fmpz_mod_mpoly_ctx_t ctxB, ctxAC; + fmpz_mod_mpoly_t B, A, A1, A2, A3; + fmpz_mod_mpoly_struct ** C; + slong * c; + slong nvarsB, nvarsAC; + slong len; + flint_bitcnt_t exp_bits; + + fmpz_init(p); + fmpz_randprime(p, state, n_randint(state, 15) + 2, 0); + fmpz_mod_mpoly_ctx_init_rand(ctxB, state, 20, p); + fmpz_mod_mpoly_ctx_init_rand(ctxAC, state, 20, p); + + nvarsB = ctxB->minfo->nvars; + nvarsAC = ctxAC->minfo->nvars; + + fmpz_mod_mpoly_init(B, ctxB); + fmpz_mod_mpoly_init(A, ctxAC); + fmpz_mod_mpoly_init(A1, ctxAC); + fmpz_mod_mpoly_init(A2, ctxAC); + fmpz_mod_mpoly_init(A3, ctxAC); + + c = (slong *) flint_malloc(nvarsB*sizeof(slong)); + C = (fmpz_mod_mpoly_struct **) flint_malloc(nvarsB * sizeof(fmpz_mod_mpoly_struct *)); + for (v = 0; v < nvarsB; v++) + { + C[v] = (fmpz_mod_mpoly_struct *) flint_malloc(sizeof(fmpz_mod_mpoly_struct)); + fmpz_mod_mpoly_init(C[v], ctxAC); + } + + for (j = 0; j < 4; j++) + { + len = n_randint(state, 200); + exp_bits = n_randint(state, 100) + 1; + + for (v = 0; v < nvarsB; v++) + { + c[v] = n_randint(state, nvarsAC + 2) - 2; + if (c[v] >= 0) + fmpz_mod_mpoly_gen(C[v], c[v], ctxAC); + else + fmpz_mod_mpoly_zero(C[v], ctxAC); + } + + fmpz_mod_mpoly_randtest_bits(B, state, len, exp_bits, ctxB); + + fmpz_mod_mpoly_compose_fmpz_mod_mpoly_gen(A, B, c, ctxB, ctxAC); + + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(A1, B, C, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(A2, B, C, ctxB, ctxAC) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(A3, B, C, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check composition success with generators\n" + "i: %wd, j: %wd\n", i, j); + + if (!fmpz_mod_mpoly_equal(A, A1, ctxAC) || + !fmpz_mod_mpoly_equal(A, A2, ctxAC) || + !fmpz_mod_mpoly_equal(A, A3, ctxAC)) + TEST_FUNCTION_FAIL("Check composition with generators\n" + "i: %wd, j: %wd\n", i, j); + + fmpz_mod_mpoly_assert_canonical(A, ctxAC); + fmpz_mod_mpoly_assert_canonical(A1, ctxAC); + fmpz_mod_mpoly_assert_canonical(A2, ctxAC); + fmpz_mod_mpoly_assert_canonical(A3, ctxAC); + } + + for (v = 0; v < nvarsB; v++) + { + fmpz_mod_mpoly_clear(C[v], ctxAC); + flint_free(C[v]); + } + flint_free(C); + flint_free(c); + + fmpz_mod_mpoly_clear(B, ctxB); + fmpz_mod_mpoly_clear(A, ctxAC); + fmpz_mod_mpoly_clear(A1, ctxAC); + fmpz_mod_mpoly_clear(A2, ctxAC); + fmpz_mod_mpoly_clear(A3, ctxAC); + + fmpz_mod_mpoly_ctx_clear(ctxB); + fmpz_mod_mpoly_ctx_clear(ctxAC); + } + + /* Check composition with identity */ + for (i = 0; i < 20*flint_test_multiplier(); i++) + { + fmpz_t p; + slong len1; + flint_bitcnt_t exp_bits; + fmpz_mod_mpoly_struct ** vals1; + fmpz_mod_mpoly_t f, g, g1, g2; + fmpz_mod_mpoly_ctx_t ctx; + + fmpz_init(p); + fmpz_randprime(p, state, n_randint(state, 15) + 2, 0); + fmpz_mod_mpoly_ctx_init_rand(ctx, state, 10, p); + + vals1 = (fmpz_mod_mpoly_struct **) flint_malloc(ctx->minfo->nvars* + sizeof(fmpz_mod_mpoly_struct *)); + for (v = 0; v < ctx->minfo->nvars; v++) + { + vals1[v] = (fmpz_mod_mpoly_struct *) flint_malloc(sizeof(fmpz_mod_mpoly_struct)); + fmpz_mod_mpoly_init(vals1[v], ctx); + fmpz_mod_mpoly_gen(vals1[v], v, ctx); + } + + fmpz_mod_mpoly_init(f, ctx); + fmpz_mod_mpoly_init(g, ctx); + fmpz_mod_mpoly_init(g1, ctx); + fmpz_mod_mpoly_init(g2, ctx); + + len1 = n_randint(state, 200); + exp_bits = n_randint(state, 100) + 1; + fmpz_mod_mpoly_randtest_bits(f, state, len1, exp_bits, ctx); + + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(g, f, vals1, ctx, ctx) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(g1, f, vals1, ctx, ctx) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(g2, f, vals1, ctx, ctx) || + !fmpz_mod_mpoly_equal(g, g1, ctx) || + !fmpz_mod_mpoly_equal(g, g2, ctx)) + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); + + fmpz_mod_mpoly_assert_canonical(g, ctx); + fmpz_mod_mpoly_assert_canonical(g1, ctx); + fmpz_mod_mpoly_assert_canonical(g2, ctx); + + if (!fmpz_mod_mpoly_equal(f, g, ctx)) + TEST_FUNCTION_FAIL("Check composition with identity\ni: %wd\n", i); + + fmpz_mod_mpoly_clear(f, ctx); + fmpz_mod_mpoly_clear(g, ctx); + fmpz_mod_mpoly_clear(g1, ctx); + fmpz_mod_mpoly_clear(g2, ctx); + + for (v = 0; v < ctx->minfo->nvars; v++) + { + fmpz_mod_mpoly_clear(vals1[v], ctx); + flint_free(vals1[v]); + } + flint_free(vals1); + + fmpz_mod_mpoly_ctx_clear(ctx); + } + + /* Check composition and evalall commute */ + for (i = 0; i < 50 * flint_test_multiplier(); i++) + { + fmpz_t p; + fmpz_mod_mpoly_ctx_t ctx1, ctx2; + fmpz_mod_mpoly_t f, g, g1, g2; + fmpz_mod_mpoly_struct ** vals1; + fmpz_t fe, ge; + fmpz ** vals2, ** vals3; + slong nvars1, nvars2; + slong len1, len2; + slong exp_bound1, exp_bound2; + + fmpz_init(p); + fmpz_randprime(p, state, n_randint(state, 15) + 2, 0); + fmpz_mod_mpoly_ctx_init_rand(ctx1, state, 6, p); + fmpz_mod_mpoly_ctx_init_rand(ctx2, state, 6, p); + + nvars1 = ctx1->minfo->nvars; + nvars2 = ctx2->minfo->nvars; + + fmpz_mod_mpoly_init(f, ctx1); + fmpz_mod_mpoly_init(g, ctx2); + fmpz_mod_mpoly_init(g1, ctx2); + fmpz_mod_mpoly_init(g2, ctx2); + fmpz_init(fe); + fmpz_init(ge); + + len1 = n_randint(state, 50/FLINT_MAX(WORD(1), nvars1) + 1) + 1; + len2 = n_randint(state, 10/FLINT_MAX(WORD(1), nvars2) + 1) + 1; + exp_bound1 = n_randint(state, 15/FLINT_MAX(WORD(1), nvars1) + 2) + 2; + exp_bound2 = n_randint(state, 15/FLINT_MAX(WORD(1), nvars2) + 2) + 3; + + vals1 = (fmpz_mod_mpoly_struct **) flint_malloc(nvars1 + * sizeof(fmpz_mod_mpoly_struct *)); + for (v = 0; v < nvars1; v++) + { + vals1[v] = (fmpz_mod_mpoly_struct *) flint_malloc( + sizeof(fmpz_mod_mpoly_struct)); + fmpz_mod_mpoly_init(vals1[v], ctx2); + fmpz_mod_mpoly_randtest_bound(vals1[v], state, len2, exp_bound2, ctx2); + } + + vals2 = (fmpz **) flint_malloc(nvars2*sizeof(fmpz*)); + for (v = 0; v < nvars2; v++) + { + vals2[v] = (fmpz *) flint_malloc(sizeof(fmpz)); + fmpz_init(vals2[v]); + fmpz_randbits(vals2[v], state, 4); + } + + vals3 = (fmpz **) flint_malloc(nvars1*sizeof(fmpz*)); + for (v = 0; v < nvars1; v++) + { + vals3[v] = (fmpz *) flint_malloc(sizeof(fmpz)); + fmpz_init(vals3[v]); + fmpz_mod_mpoly_evaluate_all_fmpz(vals3[v], vals1[v], vals2, ctx2); + } + + fmpz_mod_mpoly_randtest_bound(f, state, len1, exp_bound1, ctx1); + + if (!fmpz_mod_mpoly_compose_fmpz_mod_mpoly(g, f, vals1, ctx1, ctx2) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_horner(g1, f, vals1, ctx1, ctx2) || + !fmpz_mod_mpoly_compose_fmpz_mod_mpoly_geobucket(g2, f, vals1, ctx1, ctx2) || + !fmpz_mod_mpoly_equal(g, g1, ctx2) || + !fmpz_mod_mpoly_equal(g, g2, ctx2)) + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); + + fmpz_mod_mpoly_assert_canonical(g, ctx2); + fmpz_mod_mpoly_assert_canonical(g1, ctx2); + fmpz_mod_mpoly_assert_canonical(g2, ctx2); + + fmpz_mod_mpoly_evaluate_all_fmpz(fe, f, vals3, ctx1); + fmpz_mod_mpoly_evaluate_all_fmpz(ge, g, vals2, ctx2); + + if (!fmpz_equal(fe, ge)) + TEST_FUNCTION_FAIL("Check composition and evalall commute\ni: %wd\n", i); + + for (v = 0; v < nvars1; v++) + { + fmpz_mod_mpoly_clear(vals1[v], ctx2); + flint_free(vals1[v]); + } + flint_free(vals1); + + for (v = 0; v < nvars2; v++) + { + fmpz_clear(vals2[v]); + flint_free(vals2[v]); + } + flint_free(vals2); + + for (v = 0; v < nvars1; v++) + { + fmpz_clear(vals3[v]); + flint_free(vals3[v]); + } + flint_free(vals3); + + fmpz_mod_mpoly_clear(f, ctx1); + fmpz_mod_mpoly_clear(g, ctx2); + fmpz_mod_mpoly_clear(g1, ctx2); + fmpz_mod_mpoly_clear(g2, ctx2); + + fmpz_clear(fe); + fmpz_clear(ge); + + fmpz_mod_mpoly_ctx_clear(ctx1); + fmpz_mod_mpoly_ctx_clear(ctx2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/fmpz_mpoly/test/t-compose_fmpz_mpoly.c b/src/fmpz_mpoly/test/t-compose_fmpz_mpoly.c index af9d6978dc..957f0b18a1 100644 --- a/src/fmpz_mpoly/test/t-compose_fmpz_mpoly.c +++ b/src/fmpz_mpoly/test/t-compose_fmpz_mpoly.c @@ -17,13 +17,15 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) slong i, j, v; { - fmpz_mpoly_t A, A1, A2, B; + slong nvarsAC = 2, nvarsB = 3; + fmpz_mpoly_t A, A1, A2, B, B1; fmpz_mpoly_struct * Cp[3]; fmpz_mpoly_struct C[3]; fmpz_mpoly_ctx_t ctxAC, ctxB; + slong * c; - fmpz_mpoly_ctx_init(ctxB, 3, ORD_LEX); - fmpz_mpoly_ctx_init(ctxAC, 2, ORD_LEX); + fmpz_mpoly_ctx_init(ctxB, nvarsB, ORD_LEX); + fmpz_mpoly_ctx_init(ctxAC, nvarsAC, ORD_LEX); fmpz_mpoly_init(B, ctxB); fmpz_mpoly_init(A, ctxAC); @@ -44,12 +46,7 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) if (fmpz_mpoly_compose_fmpz_mpoly(A, B, Cp, ctxB, ctxAC) || fmpz_mpoly_compose_fmpz_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || fmpz_mpoly_compose_fmpz_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 1\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 1\n"); fmpz_mpoly_set_str_pretty(C + 0, "x1", NULL, ctxAC); fmpz_mpoly_set_str_pretty(C + 1, "2*x2", NULL, ctxAC); @@ -57,12 +54,7 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) if (fmpz_mpoly_compose_fmpz_mpoly(A, B, Cp, ctxB, ctxAC) || fmpz_mpoly_compose_fmpz_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || fmpz_mpoly_compose_fmpz_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 2\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 2\n"); fmpz_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); fmpz_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); @@ -70,12 +62,50 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) if (!fmpz_mpoly_compose_fmpz_mpoly(A, B, Cp, ctxB, ctxAC) || !fmpz_mpoly_compose_fmpz_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || !fmpz_mpoly_compose_fmpz_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check example 3\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check example 3\n"); + + /* Aliased generator composition */ + c = (slong *) flint_malloc(nvarsB*sizeof(slong)); + fmpz_mpoly_init(B1, ctxB); + fmpz_mpoly_set(B1, B, ctxB); + for (i = 0; i < nvarsB; i++) + c[i] = i; + + fmpz_mpoly_compose_fmpz_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpz_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with aliased generators\n"); + + /* Reverse the generators, twice */ + for (i = 0; i < nvarsB; i++) + c[i] = nvarsB - i - 1; + + fmpz_mpoly_compose_fmpz_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (fmpz_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with reversed aliased generators\n"); + + fmpz_mpoly_compose_fmpz_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!fmpz_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with un-reversed aliased generators\n"); + + /* Composition with zero polys */ + fmpz_mpoly_zero(B1, ctxB); + + fmpz_mpoly_compose_fmpz_mpoly_gen(A, B1, c, ctxB, ctxAC); + if (!fmpz_mpoly_is_zero(B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with generators of zero poly\n"); + + fmpz_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); + fmpz_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); + fmpz_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!fmpz_mpoly_compose_fmpz_mpoly(A, B1, Cp, ctxB, ctxAC) || + !fmpz_mpoly_compose_fmpz_mpoly_horner(A1, B1, Cp, ctxB, ctxAC) || + !fmpz_mpoly_compose_fmpz_mpoly_geobucket(A2, B1, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check example 4\n"); + + if (!fmpz_mpoly_is_zero(A, ctxAC) || + !fmpz_mpoly_is_zero(A1, ctxAC) || + !fmpz_mpoly_is_zero(A2, ctxAC)) + TEST_FUNCTION_FAIL("Check composition with zero poly\n"); fmpz_mpoly_clear(B, ctxB); fmpz_mpoly_clear(A, ctxAC); @@ -141,24 +171,14 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) if (!fmpz_mpoly_compose_fmpz_mpoly(A1, B, C, ctxB, ctxAC) || !fmpz_mpoly_compose_fmpz_mpoly_horner(A2, B, C, ctxB, ctxAC) || !fmpz_mpoly_compose_fmpz_mpoly_geobucket(A3, B, C, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition success with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success with generators\n" + "i: %wd, j: %wd\n", i, j); if (!fmpz_mpoly_equal(A, A1, ctxAC) || !fmpz_mpoly_equal(A, A2, ctxAC) || !fmpz_mpoly_equal(A, A3, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with generators\n" + "i: %wd, j: %wd\n", i, j); fmpz_mpoly_assert_canonical(A, ctxAC); fmpz_mpoly_assert_canonical(A1, ctxAC); @@ -219,24 +239,14 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) !fmpz_mpoly_compose_fmpz_mpoly_geobucket(g2, f, vals1, ctx, ctx) || !fmpz_mpoly_equal(g, g1, ctx) || !fmpz_mpoly_equal(g, g2, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); fmpz_mpoly_assert_canonical(g, ctx); fmpz_mpoly_assert_canonical(g1, ctx); fmpz_mpoly_assert_canonical(g2, ctx); if (!fmpz_mpoly_equal(f, g, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition with identity\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with identity\ni: %wd\n", i); fmpz_mpoly_clear(f, ctx); fmpz_mpoly_clear(g, ctx); @@ -311,12 +321,7 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) vals3[v] = (fmpz *) flint_malloc(sizeof(fmpz)); fmpz_init(vals3[v]); if (!fmpz_mpoly_evaluate_all_fmpz(vals3[v], vals1[v], vals2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check evaluation success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check evaluation success\ni: %wd\n", i); } fmpz_mpoly_randtest_bound(f, state, len1, coeff_bits1, exp_bound1, ctx1); @@ -326,12 +331,7 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) !fmpz_mpoly_compose_fmpz_mpoly_geobucket(g2, f, vals1, ctx1, ctx2) || !fmpz_mpoly_equal(g, g1, ctx2) || !fmpz_mpoly_equal(g, g2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); fmpz_mpoly_assert_canonical(g, ctx2); fmpz_mpoly_assert_canonical(g1, ctx2); @@ -339,20 +339,10 @@ TEST_FUNCTION_START(fmpz_mpoly_compose_fmpz_mpoly, state) if (!fmpz_mpoly_evaluate_all_fmpz(fe, f, vals3, ctx1) || !fmpz_mpoly_evaluate_all_fmpz(ge, g, vals2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check evaluation success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check evaluation success\ni: %wd\n", i); if (!fmpz_equal(fe, ge)) - { - printf("FAIL\n"); - flint_printf("Check composition and evalall commute\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition and evalall commute\ni: %wd\n", i); for (v = 0; v < nvars1; v++) { diff --git a/src/nmod_mpoly/test/t-compose_nmod_mpoly.c b/src/nmod_mpoly/test/t-compose_nmod_mpoly.c index 5127a5dc82..e56d175b30 100644 --- a/src/nmod_mpoly/test/t-compose_nmod_mpoly.c +++ b/src/nmod_mpoly/test/t-compose_nmod_mpoly.c @@ -18,13 +18,18 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) slong i, j, v; { - nmod_mpoly_t A, A1, A2, B; + slong nvarsAC = 2, nvarsB = 3; + nmod_mpoly_t A, A1, A2, B, B1; nmod_mpoly_struct * Cp[3]; nmod_mpoly_struct C[3]; nmod_mpoly_ctx_t ctxAC, ctxB; + slong * c; + ulong modulus; - nmod_mpoly_ctx_init(ctxB, 3, ORD_LEX, 13); - nmod_mpoly_ctx_init(ctxAC, 2, ORD_LEX, 13); + modulus = n_randint(state, FLINT_BITS - 1) + 1; + modulus = n_randbits(state, modulus); + nmod_mpoly_ctx_init(ctxB, nvarsB, ORD_LEX, modulus); + nmod_mpoly_ctx_init(ctxAC, nvarsAC, ORD_LEX, modulus); nmod_mpoly_init(B, ctxB); nmod_mpoly_init(A, ctxAC); @@ -45,12 +50,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (nmod_mpoly_compose_nmod_mpoly(A, B, Cp, ctxB, ctxAC) || nmod_mpoly_compose_nmod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || nmod_mpoly_compose_nmod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 1\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 1\n"); nmod_mpoly_set_str_pretty(C + 0, "x1", NULL, ctxAC); nmod_mpoly_set_str_pretty(C + 1, "2*x2", NULL, ctxAC); @@ -58,12 +58,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (!nmod_mpoly_compose_nmod_mpoly(A, B, Cp, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check non-example 2\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check non-example 2\n"); nmod_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); nmod_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); @@ -71,12 +66,50 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (!nmod_mpoly_compose_nmod_mpoly(A, B, Cp, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_horner(A1, B, Cp, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_geobucket(A2, B, Cp, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check example 3\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check example 3\n"); + + /* Aliased generator composition */ + c = (slong *) flint_malloc(nvarsB*sizeof(slong)); + nmod_mpoly_init(B1, ctxB); + nmod_mpoly_set(B1, B, ctxB); + for (i = 0; i < nvarsB; i++) + c[i] = i; + + nmod_mpoly_compose_nmod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!nmod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with aliased generators\n"); + + /* Reverse the generators, twice */ + for (i = 0; i < nvarsB; i++) + c[i] = nvarsB - i - 1; + + nmod_mpoly_compose_nmod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (nmod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with reversed aliased generators\n"); + + nmod_mpoly_compose_nmod_mpoly_gen(B1, B1, c, ctxB, ctxB); + if (!nmod_mpoly_equal(B, B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with un-reversed aliased generators\n"); + + /* Composition with zero polys */ + nmod_mpoly_zero(B1, ctxB); + + nmod_mpoly_compose_nmod_mpoly_gen(A, B1, c, ctxB, ctxAC); + if (!nmod_mpoly_is_zero(B1, ctxB)) + TEST_FUNCTION_FAIL("Check composition with generators of zero poly\n"); + + nmod_mpoly_set_str_pretty(C + 0, "2*x1", NULL, ctxAC); + nmod_mpoly_set_str_pretty(C + 1, "x2", NULL, ctxAC); + nmod_mpoly_set_str_pretty(C + 2, "1", NULL, ctxAC); + if (!nmod_mpoly_compose_nmod_mpoly(A, B1, Cp, ctxB, ctxAC) || + !nmod_mpoly_compose_nmod_mpoly_horner(A1, B1, Cp, ctxB, ctxAC) || + !nmod_mpoly_compose_nmod_mpoly_geobucket(A2, B1, Cp, ctxB, ctxAC)) + TEST_FUNCTION_FAIL("Check example 4\n"); + + if (!nmod_mpoly_is_zero(A, ctxAC) || + !nmod_mpoly_is_zero(A1, ctxAC) || + !nmod_mpoly_is_zero(A2, ctxAC)) + TEST_FUNCTION_FAIL("Check composition with zero poly\n"); nmod_mpoly_clear(B, ctxB); nmod_mpoly_clear(A, ctxAC); @@ -145,24 +178,14 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (!nmod_mpoly_compose_nmod_mpoly(A1, B, C, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_horner(A2, B, C, ctxB, ctxAC) || !nmod_mpoly_compose_nmod_mpoly_geobucket(A3, B, C, ctxB, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition success with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success with generators\n" + "i: %wd, j: %wd\n", i, j); if (!nmod_mpoly_equal(A, A1, ctxAC) || !nmod_mpoly_equal(A, A2, ctxAC) || !nmod_mpoly_equal(A, A3, ctxAC)) - { - printf("FAIL\n"); - flint_printf("Check composition with generators\n" - "i: %wd, j: %wd\n", i, j); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with generators\n" + "i: %wd, j: %wd\n", i, j); nmod_mpoly_assert_canonical(A, ctxAC); nmod_mpoly_assert_canonical(A1, ctxAC); @@ -225,24 +248,14 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) !nmod_mpoly_compose_nmod_mpoly_geobucket(g2, f, vals1, ctx, ctx) || !nmod_mpoly_equal(g, g1, ctx) || !nmod_mpoly_equal(g, g2, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); nmod_mpoly_assert_canonical(g, ctx); nmod_mpoly_assert_canonical(g1, ctx); nmod_mpoly_assert_canonical(g2, ctx); if (!nmod_mpoly_equal(f, g, ctx)) - { - printf("FAIL\n"); - flint_printf("Check composition with identity\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with identity\ni: %wd\n", i); nmod_mpoly_clear(f, ctx); nmod_mpoly_clear(g, ctx); @@ -319,12 +332,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) !nmod_mpoly_compose_nmod_mpoly_geobucket(g2, f, vals1, ctx1, ctx2) || !nmod_mpoly_equal(g, g1, ctx2) || !nmod_mpoly_equal(g, g2, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); nmod_mpoly_assert_canonical(g, ctx2); nmod_mpoly_assert_canonical(g1, ctx2); @@ -334,12 +342,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) ge = nmod_mpoly_evaluate_all_ui(g, vals2, ctx2); if (fe != ge) - { - printf("FAIL\n"); - flint_printf("Check composition and evalall commute\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition and evalall commute\ni: %wd\n", i); for (v = 0; v < nvars1; v++) { @@ -404,12 +407,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (!nmod_mpoly_compose_nmod_mpoly(g, f, vals1, ctx1, ctx2) || !nmod_mpoly_compose_nmod_mpoly_geobucket(g1, f, vals1, ctx1, ctx2) || !nmod_mpoly_compose_nmod_mpoly_horner(g2, f, vals1, ctx1, ctx2)) - { - printf("FAIL\n"); - flint_printf("Check composition success\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition success\ni: %wd\n", i); nmod_mpoly_assert_canonical(g, ctx2); nmod_mpoly_assert_canonical(g1, ctx2); @@ -417,12 +415,7 @@ TEST_FUNCTION_START(nmod_mpoly_compose_nmod_mpoly, state) if (!nmod_mpoly_is_ui(g, ctx2) || nmod_mpoly_get_ui(g, ctx2) != nmod_mpoly_evaluate_all_ui(f, vals2, ctx1)) - { - printf("FAIL\n"); - flint_printf("Check composition with constants matches evalall\ni: %wd\n", i); - fflush(stdout); - flint_abort(); - } + TEST_FUNCTION_FAIL("Check composition with constants matches evalall\ni: %wd\n", i); for (v = 0; v < nvars1; v++) {