diff --git a/doc/source/fmpz_factor.rst b/doc/source/fmpz_factor.rst index b8bca58aa2..7085a213dc 100644 --- a/doc/source/fmpz_factor.rst +++ b/doc/source/fmpz_factor.rst @@ -176,6 +176,35 @@ A separate ``int`` field holds the sign, which may be `-1`, `0` or `1`. https://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf +Utility +-------------------------------------------------------------------------------- + +.. function:: void fmpz_factor_sort(fmpz_factor_t factor) + + Sorts ``factor`` in-place such that the factors are in ascending order. + +Arithmetic +-------------------------------------------------------------------------------- + +.. function:: void _fmpz_factor_mul(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) + + Sets ``factor`` to the factored representation of `a \times b` assuming ``a`` and ``b`` + are sorted. + +.. function:: void fmpz_factor_mul(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) + + Sets ``factor`` to the factored representation of `a \times b` by first sorting ``a`` and ``b``. + +.. function:: void _fmpz_factor_gcd(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) + + Sets ``factor`` to the factored representation of `\gcd(a, b)` assuming ``a`` and ``b`` + are sorted. + +.. function:: void fmpz_factor_gcd(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) + + Sets ``factor`` to the factored representation of `\gcd(a, b)` by first sorting ``a`` and ``b``. + + Input and output -------------------------------------------------------------------------------- diff --git a/src/fmpz_factor.h b/src/fmpz_factor.h index bfafd94fa1..35cfa56563 100644 --- a/src/fmpz_factor.h +++ b/src/fmpz_factor.h @@ -38,6 +38,16 @@ void _fmpz_factor_append_ui(fmpz_factor_t factor, ulong p, ulong exp); void _fmpz_factor_concat(fmpz_factor_t factor1, fmpz_factor_t factor2, ulong exp); +void fmpz_factor_sort(fmpz_factor_t factor); + +/* Arithmetic ****************************************************************/ + +void _fmpz_factor_mul(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b); +void fmpz_factor_gcd(fmpz_factor_t factor, fmpz_factor_t a, fmpz_factor_t b); + +void _fmpz_factor_gcd(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b); +void fmpz_factor_mul(fmpz_factor_t factor, fmpz_factor_t a, fmpz_factor_t b); + /* I/O ***********************************************************************/ #ifdef FLINT_HAVE_FILE diff --git a/src/fmpz_factor/gcd.c b/src/fmpz_factor/gcd.c new file mode 100644 index 0000000000..7338f9593c --- /dev/null +++ b/src/fmpz_factor/gcd.c @@ -0,0 +1,59 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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_factor.h" + +void +_fmpz_factor_gcd(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) +{ + slong i, j; + ulong num_len; + int cmp; + + num_len = FLINT_MIN(a->num, b->num); + _fmpz_factor_fit_length(factor, num_len); + factor->sign = 1; + factor->num = 0; + + i = 0; + j = 0; + factor->num = 0; + + while ((i < a->num) && (j < b->num)) + { + cmp = fmpz_cmp(a->p + i, b->p + j); + if (cmp < 0) // a[i] < b[i] + { + i++; + } + else if (cmp > 0) // b[i] < a[i] + { + j++; + } + else // b[i] = a[i] + { + fmpz_set(factor->p + factor->num, a->p + i); + factor->exp[factor->num] = FLINT_MIN(a->exp[i], b->exp[j]); + factor->num++; + i++; + j++; + } + } +} + +void fmpz_factor_gcd(fmpz_factor_t factor, fmpz_factor_t a, fmpz_factor_t b) +{ + fmpz_factor_sort(a); + fmpz_factor_sort(b); + + _fmpz_factor_gcd(factor, a, b); +} diff --git a/src/fmpz_factor/mul.c b/src/fmpz_factor/mul.c new file mode 100644 index 0000000000..376e660092 --- /dev/null +++ b/src/fmpz_factor/mul.c @@ -0,0 +1,78 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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_factor.h" + +void +_fmpz_factor_mul(fmpz_factor_t factor, const fmpz_factor_t a, const fmpz_factor_t b) +{ + slong i, j; + int cmp; + + _fmpz_factor_fit_length(factor, a->num + b->num); + factor->sign = a->sign * b->sign; + + i = 0; + j = 0; + factor->num = 0; + + while ((i < a->num) && (j < b->num)) + { + cmp = fmpz_cmp(a->p + i, b->p + j); + if (cmp < 0) // a[i] < b[i] + { + fmpz_set(factor->p + factor->num, a->p + i); + factor->exp[factor->num] = a->exp[i]; + factor->num++; + i++; + } + else if (cmp > 0) // b[i] < a[i] + { + fmpz_set(factor->p + factor->num, b->p + j); + factor->exp[factor->num] = b->exp[j]; + factor->num++; + j++; + } + else // b[i] = a[i] + { + fmpz_set(factor->p + factor->num, a->p + i); + factor->exp[factor->num] = a->exp[i] + b->exp[j]; + factor->num++; + i++; + j++; + } + } + + while (i < a->num) + { + fmpz_set(factor->p + factor->num, a->p + i); + factor->exp[factor->num] = a->exp[i]; + factor->num++; + i++; + } + + while (j < b->num) + { + fmpz_set(factor->p + factor->num, b->p + j); + factor->exp[factor->num] = b->exp[j]; + factor->num++; + j++; + } +} + +void fmpz_factor_mul(fmpz_factor_t factor, fmpz_factor_t a, fmpz_factor_t b) +{ + fmpz_factor_sort(a); + fmpz_factor_sort(b); + + _fmpz_factor_mul(factor, a, b); +} diff --git a/src/fmpz_factor/sort.c b/src/fmpz_factor/sort.c new file mode 100644 index 0000000000..a93b446b92 --- /dev/null +++ b/src/fmpz_factor/sort.c @@ -0,0 +1,43 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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_factor.h" + +// Tiny implem of quick sort, qsort is not usable because we also want to permute exp +// and qsort_r / qsort_s have portability issues +static void _fmpz_factor_sort_part(fmpz* p, ulong* exp, slong n) +{ + slong i, j; + if (n <= 1) return; + + fmpz_swap(p + n / 2, p + n - 1); + FLINT_SWAP(ulong, exp[n / 2], exp[n - 1]); + for (i = 0, j = 0; i < n - 1; i++) + { + if (fmpz_cmp(p + i, p + n - 1) < 0) + { + fmpz_swap(p + i, p + j); + FLINT_SWAP(ulong, exp[i], exp[j]); + j++; + } + } + fmpz_swap(p + j, p + n - 1); + FLINT_SWAP(ulong, exp[j], exp[n - 1]); + + _fmpz_factor_sort_part(p, exp, j); + _fmpz_factor_sort_part(p + j + 1, exp + j + 1, n - j - 1); +} + +void +fmpz_factor_sort(fmpz_factor_t factor) +{ + _fmpz_factor_sort_part(factor->p, factor->exp, factor->num); +} diff --git a/src/fmpz_factor/test/main.c b/src/fmpz_factor/test/main.c index 8a09a8519f..b6b5fe2bac 100644 --- a/src/fmpz_factor/test/main.c +++ b/src/fmpz_factor/test/main.c @@ -22,7 +22,9 @@ #include "t-pollard_brent.c" #include "t-pollard_brent_single.c" #include "t-refine.c" - +#include "t-sort.c" +#include "t-mul.c" +#include "t-gcd.c" /* Array of test functions ***************************************************/ test_struct tests[] = @@ -35,7 +37,10 @@ test_struct tests[] = TEST_FUNCTION(fmpz_factor_trial), TEST_FUNCTION(fmpz_factor_pollard_brent), TEST_FUNCTION(fmpz_factor_pollard_brent_single), - TEST_FUNCTION(fmpz_factor_refine) + TEST_FUNCTION(fmpz_factor_refine), + TEST_FUNCTION(fmpz_factor_sort), + TEST_FUNCTION(fmpz_factor_mul), + TEST_FUNCTION(fmpz_factor_gcd) }; /* main function *************************************************************/ diff --git a/src/fmpz_factor/test/t-gcd.c b/src/fmpz_factor/test/t-gcd.c new file mode 100644 index 0000000000..b7a52108ad --- /dev/null +++ b/src/fmpz_factor/test/t-gcd.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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.h" +#include "fmpz_factor.h" + +TEST_FUNCTION_START(fmpz_factor_gcd, state) +{ + slong i, b; + fmpz_t x, y; + fmpz_factor_t fx, fy, fz; + + fmpz_init(x); + fmpz_init(y); + fmpz_factor_init(fx); + fmpz_factor_init(fy); + fmpz_factor_init(fz); + + for (i = 0; i < 1000 * flint_test_multiplier(); i++) + { + b = n_randint(state, 1000000); + if (n_randint(state, 2)) + b = -b; + fmpz_set_si(x, b); + + b = n_randint(state, 1000000); + if (n_randint(state, 2)) + b = -b; + fmpz_set_si(y, b); + + fmpz_factor(fx, x); + fmpz_factor(fy, y); + + fmpz_factor_gcd(fz, fx, fy); + + fmpz_gcd(x, x, y); + fmpz_factor_expand(y, fz); + + if (fmpz_cmp(x, y) != 0) + { + flint_printf("FAIL (factor gcd)\n"); + fmpz_print(x); flint_printf(" "); + fmpz_print(y); flint_printf("\n"); + fflush(stdout); + flint_abort(); + } + } + + fmpz_clear(x); + fmpz_clear(y); + fmpz_factor_clear(fx); + fmpz_factor_clear(fy); + fmpz_factor_clear(fz); + + TEST_FUNCTION_END(state); +} diff --git a/src/fmpz_factor/test/t-mul.c b/src/fmpz_factor/test/t-mul.c new file mode 100644 index 0000000000..2d23b85cb2 --- /dev/null +++ b/src/fmpz_factor/test/t-mul.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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.h" +#include "fmpz_factor.h" + +TEST_FUNCTION_START(fmpz_factor_mul, state) +{ + slong i, b; + fmpz_t x, y; + fmpz_factor_t fx, fy, fz; + + fmpz_init(x); + fmpz_init(y); + fmpz_factor_init(fx); + fmpz_factor_init(fy); + fmpz_factor_init(fz); + + for (i = 0; i < 1000 * flint_test_multiplier(); i++) + { + b = n_randint(state, 1000000); + if (n_randint(state, 2)) + b = -b; + fmpz_set_si(x, b); + + b = n_randint(state, 1000000); + if (n_randint(state, 2)) + b = -b; + fmpz_set_si(y, b); + + fmpz_factor(fx, x); + fmpz_factor(fy, y); + + fmpz_factor_mul(fz, fx, fy); + + fmpz_mul(x, x, y); + fmpz_factor_expand(y, fz); + + if (fmpz_cmp(x, y) != 0) + { + flint_printf("FAIL (factor mul)\n"); + fmpz_print(x); flint_printf(" "); + fmpz_print(y); flint_printf("\n"); + fflush(stdout); + flint_abort(); + } + } + + fmpz_clear(x); + fmpz_clear(y); + fmpz_factor_clear(fx); + fmpz_factor_clear(fy); + fmpz_factor_clear(fz); + + TEST_FUNCTION_END(state); +} diff --git a/src/fmpz_factor/test/t-sort.c b/src/fmpz_factor/test/t-sort.c new file mode 100644 index 0000000000..4af2953a63 --- /dev/null +++ b/src/fmpz_factor/test/t-sort.c @@ -0,0 +1,64 @@ +/* + Copyright (C) 2025 Mael Hostettler + + 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.h" +#include "fmpz_factor.h" + +TEST_FUNCTION_START(fmpz_factor_sort, state) +{ + slong i, j; + fmpz_t x, y, z; + fmpz_factor_t factor; + + fmpz_init(x); + fmpz_init(y); + fmpz_init(z); + + for (i = 0; i < 1000 * flint_test_multiplier(); i++) + { + fmpz_factor_init(factor); + + for(j = 0; j < 10 * flint_test_multiplier(); j++) + { + fmpz_randprime(x, state, 2 + j, 1); + _fmpz_factor_append(factor, x, 1 + n_randint(state, 5)); + } + fmpz_factor_expand(y, factor); + fmpz_factor_sort(factor); + fmpz_factor_expand(z, factor); + + // check order + int ord = 1; + for (j = 0; j < factor->num - 1; j++) + { + ord &= (fmpz_cmp(factor->p + j, factor->p + j + 1) < 0); + } + + // check equality + if (!fmpz_equal(y, z) || !ord) + { + flint_printf("FAIL (factor sort)\n"); + fmpz_factor_print(factor); flint_printf("\n"); + fmpz_print(y); flint_printf(" "); + fmpz_print(z); flint_printf("\n"); + fflush(stdout); + flint_abort(); + } + fmpz_factor_clear(factor); + } + + fmpz_clear(x); + fmpz_clear(y); + fmpz_clear(z); + + TEST_FUNCTION_END(state); +}