From 97359ecf4407db4e01a44b6a63e85e894dd5f32e Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Sun, 12 Jan 2025 22:47:40 +0100 Subject: [PATCH 1/2] gr_mpoly: slightly better equality testing code; better test code --- src/gr_mpoly.h | 23 ++----------- src/gr_mpoly/equal.c | 55 ++++++++++++++++++++---------- src/gr_mpoly/is_one.c | 45 +++++++++++++++++++++++++ src/gr_mpoly/is_zero.c | 25 ++++++++++++++ src/gr_mpoly/test/main.c | 4 ++- src/gr_mpoly/test/t-ring.c | 68 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 181 insertions(+), 39 deletions(-) create mode 100644 src/gr_mpoly/is_one.c create mode 100644 src/gr_mpoly/is_zero.c create mode 100644 src/gr_mpoly/test/t-ring.c diff --git a/src/gr_mpoly.h b/src/gr_mpoly.h index 28340e0909..d20be516cc 100644 --- a/src/gr_mpoly.h +++ b/src/gr_mpoly.h @@ -172,15 +172,7 @@ int gr_mpoly_zero(gr_mpoly_t A, gr_mpoly_ctx_t ctx) return GR_SUCCESS; } -GR_MPOLY_INLINE -truth_t gr_mpoly_is_zero(const gr_mpoly_t A, gr_mpoly_ctx_t ctx) -{ - if (A->length == 0) - return T_TRUE; - - /* todo: skip when we have canonical representation */ - return _gr_vec_is_zero(A->coeffs, A->length, GR_MPOLY_CCTX(ctx)); -} +truth_t gr_mpoly_is_zero(const gr_mpoly_t A, gr_mpoly_ctx_t ctx); WARN_UNUSED_RESULT int gr_mpoly_gen(gr_mpoly_t A, slong var, gr_mpoly_ctx_t ctx); truth_t gr_mpoly_is_gen(const gr_mpoly_t A, slong var, gr_mpoly_ctx_t ctx); @@ -231,18 +223,7 @@ int gr_mpoly_one(gr_mpoly_t A, gr_mpoly_ctx_t ctx) return gr_mpoly_set_ui(A, 1, ctx); } -/* todo: efficient version */ -GR_MPOLY_INLINE -truth_t gr_mpoly_is_one(const gr_mpoly_t A, gr_mpoly_ctx_t ctx) -{ - gr_mpoly_t t; - truth_t res = T_UNKNOWN; - gr_mpoly_init(t, ctx); - if (gr_mpoly_one(t, ctx) == GR_SUCCESS) - res = gr_mpoly_equal(A, t, ctx); - gr_mpoly_clear(t, ctx); - return res; -} +truth_t gr_mpoly_is_one(const gr_mpoly_t A, gr_mpoly_ctx_t ctx); /* Coefficient/exponent access */ diff --git a/src/gr_mpoly/equal.c b/src/gr_mpoly/equal.c index 7d2d9fed6b..5874a900b3 100644 --- a/src/gr_mpoly/equal.c +++ b/src/gr_mpoly/equal.c @@ -9,39 +9,60 @@ (at your option) any later version. See . */ +#include "mpoly.h" #include "gr_mpoly.h" -/* todo: proper algorithm */ truth_t gr_mpoly_equal( const gr_mpoly_t A, const gr_mpoly_t B, gr_mpoly_ctx_t ctx) { - truth_t eq; - gr_mpoly_t t; + mpoly_ctx_struct * mctx = GR_MPOLY_MCTX(ctx); + gr_ctx_struct * cctx = GR_MPOLY_CCTX(ctx); + int canonical; if (A == B) return T_TRUE; - /* todo: if canonical representation */ - /* - if (A->length != B->length) - return T_FALSE; + canonical = 1; - ... _gr_vec_equal(A->coeffs, B->coeffs, A->length) ... + if (gr_ctx_is_canonical(cctx) != T_TRUE) + { + slong i, sz = cctx->sizeof_elem; - return (0 == mpoly_monomials_cmp(A->exps, A->bits, B->exps, B->bits, - A->length, ctx->minfo)) ? T_TRUE : T_FALSE; - */ + for (i = 0; canonical && i < A->length; i++) + if (gr_is_zero(GR_ENTRY(A->coeffs, i, sz), cctx) != T_FALSE) + canonical = 0; - gr_mpoly_init(t, ctx); + for (i = 0; canonical && i < B->length; i++) + if (gr_is_zero(GR_ENTRY(B->coeffs, i, sz), cctx) != T_FALSE) + canonical = 0; + } - if (gr_mpoly_sub(t, A, B, ctx) == GR_SUCCESS) - eq = gr_mpoly_is_zero(t, ctx); + if (canonical) + { + if (A->length != B->length) + return T_FALSE; + + if (0 != mpoly_monomials_cmp(A->exps, A->bits, B->exps, B->bits, A->length, mctx)) + return T_FALSE; + + return _gr_vec_equal(A->coeffs, B->coeffs, A->length, cctx); + } else - eq = T_UNKNOWN; + { + /* todo: a better fallback algorithm */ + truth_t eq; + gr_mpoly_t t; + gr_mpoly_init(t, ctx); + + if (gr_mpoly_sub(t, A, B, ctx) == GR_SUCCESS) + eq = gr_mpoly_is_zero(t, ctx); + else + eq = T_UNKNOWN; - gr_mpoly_clear(t, ctx); + gr_mpoly_clear(t, ctx); - return eq; + return eq; + } } diff --git a/src/gr_mpoly/is_one.c b/src/gr_mpoly/is_one.c new file mode 100644 index 0000000000..0d140603c2 --- /dev/null +++ b/src/gr_mpoly/is_one.c @@ -0,0 +1,45 @@ +/* + Copyright (C) 2020 Daniel Schultz + Copyright (C) 2025 Fredrik Johansson + + 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 "mpoly.h" +#include "gr_mpoly.h" + +truth_t gr_mpoly_is_one(const gr_mpoly_t A, gr_mpoly_ctx_t ctx) +{ + if (A->length == 0) + return gr_ctx_is_zero_ring(ctx); + + if (gr_ctx_is_canonical(GR_MPOLY_CCTX(ctx)) == T_TRUE) + { + slong N; + + if (A->length != 1) + return T_FALSE; + + N = mpoly_words_per_exp(A->bits, GR_MPOLY_MCTX(ctx)); + + if (!mpoly_monomial_is_zero(A->exps + N*0, N)) + return T_FALSE; + + return gr_is_one(A->coeffs, GR_MPOLY_CCTX(ctx)); + } + else + { + gr_mpoly_t t; + truth_t res = T_UNKNOWN; + gr_mpoly_init(t, ctx); + if (gr_mpoly_one(t, ctx) == GR_SUCCESS) + res = gr_mpoly_equal(A, t, ctx); + gr_mpoly_clear(t, ctx); + return res; + } +} \ No newline at end of file diff --git a/src/gr_mpoly/is_zero.c b/src/gr_mpoly/is_zero.c new file mode 100644 index 0000000000..feb545639d --- /dev/null +++ b/src/gr_mpoly/is_zero.c @@ -0,0 +1,25 @@ +/* + Copyright (C) 2020 Daniel Schultz + Copyright (C) 2025 Fredrik Johansson + + 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 "mpoly.h" +#include "gr_mpoly.h" + +truth_t gr_mpoly_is_zero(const gr_mpoly_t A, gr_mpoly_ctx_t ctx) +{ + if (A->length == 0) + return T_TRUE; + + if (gr_ctx_is_canonical(GR_MPOLY_CCTX(ctx)) == T_TRUE) + return T_FALSE; + else + return _gr_vec_is_zero(A->coeffs, A->length, GR_MPOLY_CCTX(ctx)); +} diff --git a/src/gr_mpoly/test/main.c b/src/gr_mpoly/test/main.c index 0aac4d51c5..70f8c70966 100644 --- a/src/gr_mpoly/test/main.c +++ b/src/gr_mpoly/test/main.c @@ -16,6 +16,7 @@ #include "t-get_set_coeff.c" #include "t-mul_johnson.c" #include "t-mul_monomial.c" +#include "t-ring.c" /* Array of test functions ***************************************************/ @@ -25,7 +26,8 @@ test_struct tests[] = TEST_FUNCTION(gr_mpoly_gen), TEST_FUNCTION(gr_mpoly_get_set_coeff), TEST_FUNCTION(gr_mpoly_mul_johnson), - TEST_FUNCTION(gr_mpoly_mul_monomial) + TEST_FUNCTION(gr_mpoly_mul_monomial), + TEST_FUNCTION(gr_mpoly_ring) }; /* main function *************************************************************/ diff --git a/src/gr_mpoly/test/t-ring.c b/src/gr_mpoly/test/t-ring.c new file mode 100644 index 0000000000..2c49af5a41 --- /dev/null +++ b/src/gr_mpoly/test/t-ring.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2020 Daniel Schultz + Copyright (C) 2022 Fredrik Johansson + + 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 "gr_mpoly.h" + +FLINT_DLL extern gr_static_method_table _ca_methods; + +TEST_FUNCTION_START(gr_mpoly_ring, state) +{ + slong iter; + + for (iter = 0; iter < 30 * flint_test_multiplier(); iter++) + { + gr_ctx_t cctx; + gr_mpoly_ctx_t ctx; + slong reps; + + gr_ctx_init_random(cctx, state); + + if (gr_ctx_is_finite(cctx) == T_TRUE || + gr_ctx_has_real_prec(cctx) == T_TRUE) + { + gr_mpoly_ctx_init_rand(ctx, state, cctx, 4); + reps = 10; + } + else if (cctx->methods == _ca_methods) /* hack: slow */ + { + gr_mpoly_ctx_init_rand(ctx, state, cctx, 1); + reps = 1; + } + else + { + gr_mpoly_ctx_init_rand(ctx, state, cctx, 2); + reps = 3; + } + + /* Hack: for string conversion tests, make sure we don't have + overlapping generator names. */ + gr_vec_t vec; + gr_vec_init(vec, 0, cctx); + if (gr_gens_recursive(vec, cctx) == GR_SUCCESS) + { + const char * vars[] = { "mv1", "mv2", "mv3", "mv4" }; + + GR_MUST_SUCCEED(gr_ctx_set_gen_names(ctx, vars)); + + } + gr_vec_clear(vec, cctx); + + /* gr_ctx_println(ctx); */ + gr_test_ring(ctx, reps, 0 * GR_TEST_VERBOSE); + + gr_mpoly_ctx_clear(ctx); + gr_ctx_clear(cctx); + } + + TEST_FUNCTION_END(state); +} From a541dadecb8dfe8c8832fcb532198f682f80d930 Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Sun, 12 Jan 2025 23:52:34 +0100 Subject: [PATCH 2/2] improve test coverage --- src/gr_mpoly/ctx.c | 4 ++ src/gr_mpoly/test/t-get_set_coeff.c | 85 +++++++++++++++++++++++++++-- src/python/flint_ctypes.py | 3 + 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/gr_mpoly/ctx.c b/src/gr_mpoly/ctx.c index d8c629bbca..72d01b1507 100644 --- a/src/gr_mpoly/ctx.c +++ b/src/gr_mpoly/ctx.c @@ -193,6 +193,10 @@ gr_method_tab_input _gr_mpoly_methods_input[] = {GR_METHOD_ADD, (gr_funcptr) gr_mpoly_add}, {GR_METHOD_SUB, (gr_funcptr) gr_mpoly_sub}, {GR_METHOD_MUL, (gr_funcptr) gr_mpoly_mul}, + {GR_METHOD_MUL_UI, (gr_funcptr) gr_mpoly_mul_ui}, + {GR_METHOD_MUL_SI, (gr_funcptr) gr_mpoly_mul_si}, + {GR_METHOD_MUL_FMPZ, (gr_funcptr) gr_mpoly_mul_fmpz}, + {GR_METHOD_MUL_FMPQ, (gr_funcptr) gr_mpoly_mul_fmpq}, {0, (gr_funcptr) NULL}, }; diff --git a/src/gr_mpoly/test/t-get_set_coeff.c b/src/gr_mpoly/test/t-get_set_coeff.c index f0a0880da1..d47f2578a5 100644 --- a/src/gr_mpoly/test/t-get_set_coeff.c +++ b/src/gr_mpoly/test/t-get_set_coeff.c @@ -1,6 +1,6 @@ /* Copyright (C) 2020 Daniel Schultz - Copyright (C) 2022 Fredrik Johansson + Copyright (C) 2022, 2025 Fredrik Johansson This file is part of FLINT. @@ -11,6 +11,8 @@ */ #include "test_helpers.h" +#include "fmpz.h" +#include "fmpq.h" #include "fmpz_vec.h" #include "gr_mpoly.h" @@ -28,6 +30,7 @@ TEST_FUNCTION_START(gr_mpoly_get_set_coeff, state) flint_bitcnt_t exp_bits; int status; slong nvars; + int which; gr_ctx_init_random(cctx, state); gr_mpoly_ctx_init_rand(ctx, state, cctx, 20); @@ -48,11 +51,47 @@ TEST_FUNCTION_START(gr_mpoly_get_set_coeff, state) status = GR_SUCCESS; - GR_MUST_SUCCEED(gr_randtest(c, state, cctx)); for (k = 0; k < nvars; k++) exp[k] = n_randtest(state); - status |= gr_mpoly_set_coeff_scalar_ui(f, c, exp, ctx); + which = n_randint(state, 5); + + if (which == 0) + { + GR_MUST_SUCCEED(gr_randtest(c, state, cctx)); + status |= gr_mpoly_set_coeff_scalar_ui(f, c, exp, ctx); + } + else if (which == 1) + { + ulong uc = n_randtest(state); + status |= gr_set_ui(c, uc, cctx); + status |= gr_mpoly_set_coeff_ui_ui(f, uc, exp, ctx); + } + else if (which == 2) + { + slong sc = n_randtest(state); + status |= gr_set_si(c, sc, cctx); + status |= gr_mpoly_set_coeff_si_ui(f, sc, exp, ctx); + } + else if (which == 3) + { + fmpz_t zc; + fmpz_init(zc); + fmpz_randtest(zc, state, 100); + status |= gr_set_fmpz(c, zc, cctx); + status |= gr_mpoly_set_coeff_fmpz_ui(f, zc, exp, ctx); + fmpz_clear(zc); + } + else if (which == 4) + { + fmpq_t qc; + fmpq_init(qc); + fmpq_randtest(qc, state, 100); + status |= gr_set_fmpq(c, qc, cctx); + status |= gr_mpoly_set_coeff_fmpq_ui(f, qc, exp, ctx); + fmpq_clear(qc); + } + gr_mpoly_assert_canonical(f, ctx); status |= gr_mpoly_get_coeff_scalar_ui(d, f, exp, ctx); @@ -79,10 +118,46 @@ TEST_FUNCTION_START(gr_mpoly_get_set_coeff, state) status = GR_SUCCESS; - GR_MUST_SUCCEED(gr_randtest(c, state, cctx)); _fmpz_vec_randtest(exp, state, nvars, exp_bits); - status |= gr_mpoly_set_coeff_scalar_fmpz(f, c, exp, ctx); + which = n_randint(state, 5); + + if (which == 0) + { + GR_MUST_SUCCEED(gr_randtest(c, state, cctx)); + status |= gr_mpoly_set_coeff_scalar_fmpz(f, c, exp, ctx); + } + else if (which == 1) + { + ulong uc = n_randtest(state); + status |= gr_set_ui(c, uc, cctx); + status |= gr_mpoly_set_coeff_ui_fmpz(f, uc, exp, ctx); + } + else if (which == 2) + { + slong sc = n_randtest(state); + status |= gr_set_si(c, sc, cctx); + status |= gr_mpoly_set_coeff_si_fmpz(f, sc, exp, ctx); + } + else if (which == 3) + { + fmpz_t zc; + fmpz_init(zc); + fmpz_randtest(zc, state, 100); + status |= gr_set_fmpz(c, zc, cctx); + status |= gr_mpoly_set_coeff_fmpz_fmpz(f, zc, exp, ctx); + fmpz_clear(zc); + } + else if (which == 4) + { + fmpq_t qc; + fmpq_init(qc); + fmpq_randtest(qc, state, 100); + status |= gr_set_fmpq(c, qc, cctx); + status |= gr_mpoly_set_coeff_fmpq_fmpz(f, qc, exp, ctx); + fmpq_clear(qc); + } + gr_mpoly_assert_canonical(f, ctx); status |= gr_mpoly_get_coeff_scalar_fmpz(d, f, exp, ctx); diff --git a/src/python/flint_ctypes.py b/src/python/flint_ctypes.py index 679e54270d..551344ddeb 100644 --- a/src/python/flint_ctypes.py +++ b/src/python/flint_ctypes.py @@ -8083,6 +8083,9 @@ def test_mpoly(): assert str(FiniteField_fq_nmod(3, 2, "d").gen()) == "d" assert str(FiniteField_fq_zech(3, 2, "e").gen()) == "e^1" + assert str(sum(PolynomialRing_gr_mpoly(ZZi, 20).gens())) == "x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20" + + def test_mpoly_q(): assert str(FractionField_fmpz_mpoly_q(2).gens()) == '[x1, x2]' assert str(FractionField_fmpz_mpoly_q(2, ["a", "b"]).gens()) == '[a, b]'