diff --git a/doc/source/gr_ore_poly.rst b/doc/source/gr_ore_poly.rst
index f294571c1c..61a180b5c6 100644
--- a/doc/source/gr_ore_poly.rst
+++ b/doc/source/gr_ore_poly.rst
@@ -319,6 +319,11 @@ Arithmetic
which must be two Ore polynomials in the Ore algebra *ctx*.
The underscore method assumes *res != poly1*, *res != poly2* (no aliasing) and *len1* != 0, *len2* != 0.
+.. function:: int _gr_ore_poly_divrem(gr_ptr Q, gr_ptr R, gr_srcptr U, slong lenU, gr_srcptr V, slong lenV, gr_ore_poly_ctx_t ctx)
+ int gr_ore_poly_divrem(gr_ore_poly_t Q, gr_ore_poly_t R, const gr_ore_poly_t U, gr_ore_poly_t V, gr_ore_poly_ctx_t ctx)
+
+ Sets *(Q, R)* to the unique pair such that `U = QV + R` and `ord(R) < ord(V)`.
+
.. raw:: latex
\newpage
diff --git a/src/gr_ore_poly.h b/src/gr_ore_poly.h
index 4a2df9cb3c..18b7c0019e 100644
--- a/src/gr_ore_poly.h
+++ b/src/gr_ore_poly.h
@@ -280,6 +280,9 @@ WARN_UNUSED_RESULT int gr_ore_poly_lmul_gen(gr_ore_poly_t res, const gr_ore_poly
WARN_UNUSED_RESULT int _gr_ore_poly_mul(gr_ptr res, gr_srcptr poly1, slong len1, gr_srcptr poly2, slong len2, gr_ore_poly_ctx_t ctx);
WARN_UNUSED_RESULT int gr_ore_poly_mul(gr_ore_poly_t res, const gr_ore_poly_t poly1, const gr_ore_poly_t poly2, gr_ore_poly_ctx_t ctx);
+WARN_UNUSED_RESULT int _gr_ore_poly_divrem(gr_ptr Q, gr_ptr R, gr_srcptr U, slong lenU, gr_srcptr V, slong lenV, gr_ore_poly_ctx_t ctx);
+WARN_UNUSED_RESULT int gr_ore_poly_divrem(gr_ore_poly_t Q, gr_ore_poly_t R, const gr_ore_poly_t U, gr_ore_poly_t V, gr_ore_poly_ctx_t ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/gr_ore_poly/divrem.c b/src/gr_ore_poly/divrem.c
new file mode 100644
index 0000000000..a0f845d0be
--- /dev/null
+++ b/src/gr_ore_poly/divrem.c
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2026 Maria Neagoie
+
+ 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 "flint.h"
+#include "gr.h"
+#include "gr_ore_poly.h"
+
+// Returns the unique pair (Q, R) such that U = QV + R and ord(R) < ord(V)
+int _gr_ore_poly_divrem(gr_ptr Q, gr_ptr R, gr_srcptr U, slong lenU, gr_srcptr V, slong lenV, gr_ore_poly_ctx_t ctx)
+{
+ gr_ctx_struct *cctx = GR_ORE_POLY_ELEM_CTX(ctx);
+ slong el_size = gr_ctx_sizeof_elem(cctx);
+ slong lenQ, lenR, ordV, ordR;
+ int status = GR_SUCCESS;
+
+ if (GR_ORE_POLY_CTX(ctx)->sigma_delta == NULL)
+ return GR_UNABLE;
+
+ lenQ = lenU - lenV + 1;
+ lenR = lenU;
+ ordV = lenV - 1;
+ ordR = lenR - 1;
+
+ // Set Q to 0
+ for (slong k = 0; k < lenQ; k++)
+ status |= gr_zero(GR_ENTRY(Q, k, el_size), cctx);
+
+ // Set R to U
+ for (slong k = 0; k < lenU; k++)
+ status |= gr_set(GR_ENTRY(R, k, el_size), GR_ENTRY(U, k, el_size), cctx);
+
+ gr_ptr lcR, lcV, denominator, c;
+ GR_TMP_INIT(lcR, cctx);
+ GR_TMP_INIT(lcV, cctx);
+ GR_TMP_INIT(denominator, cctx);
+ GR_TMP_INIT(c, cctx);
+
+ gr_ptr A = flint_malloc(lenQ * el_size);
+ gr_ptr B = flint_malloc(lenU * el_size);
+ _gr_vec_init(A, lenQ, cctx);
+ _gr_vec_init(B, lenU, cctx);
+
+ while (ordR > ordV)
+ {
+ slong k = ordR - ordV;
+
+ status |= gr_set(lcR, GR_ENTRY(R, ordR, el_size), cctx);
+ status |= gr_set(lcV, GR_ENTRY(V, ordV, el_size), cctx);
+
+ // Compute denominator = sigma ^ k (lc(V))
+ status |= gr_set(denominator, lcV, cctx);
+ for (slong i = 0; i < k; i++)
+ status |= gr_ore_poly_sigma(denominator, denominator, ctx);
+
+ // c = lc(R) / denominator
+ status |= gr_div(c, lcR, denominator, cctx);
+
+ // R -= c * x^k * V. We compute A = c * x^k, then B = A * V
+ // A = c * x^k, so A[k] = c, rest 0
+ slong lenA = k + 1;
+ for (slong i = 0; i < lenA; i++)
+ status |= gr_zero(GR_ENTRY(A, i, el_size), cctx);
+ status |= gr_set(GR_ENTRY(A, k, el_size), c, cctx);
+
+ // B = A * V
+ status |= _gr_ore_poly_mul(B, A, lenA, V, lenV, ctx);
+
+ // R -= B
+ slong lenB = lenA + lenV - 1;
+ for (slong i = 0; i < lenB; i++)
+ status |= gr_sub(GR_ENTRY(R, i, el_size), GR_ENTRY(R, i, el_size), GR_ENTRY(B, i, el_size), cctx);
+
+ // Q += c * x^k, so Q[k] += c
+ status |= gr_add(GR_ENTRY(Q, k, el_size), GR_ENTRY(Q, k, el_size), c, cctx);
+
+ ordR--;
+ }
+
+ gr_clear(lcR, cctx);
+ gr_clear(lcV, cctx);
+ gr_clear(denominator, cctx);
+ gr_clear(c, cctx);
+
+ _gr_vec_clear(A, lenQ, cctx);
+ _gr_vec_clear(B, lenU, cctx);
+ flint_free(A);
+ flint_free(B);
+
+ return status;
+}
+
+int gr_ore_poly_divrem(gr_ore_poly_t Q, gr_ore_poly_t R, const gr_ore_poly_t U, gr_ore_poly_t V, gr_ore_poly_ctx_t ctx)
+{
+ int status = GR_SUCCESS;
+ const slong lenU = U->length;
+ const slong lenV = V->length;
+
+ if (lenV == 0) // division by zero polynomial
+ return GR_DOMAIN;
+
+ if (lenU == 0) // result is 0
+ {
+ status |= gr_ore_poly_zero(Q, ctx);
+ status |= gr_ore_poly_zero(R, ctx);
+ return status;
+ }
+
+ if (lenU < lenV)
+ {
+ gr_ore_poly_t tU;
+ // take care of aliasing case with temp
+ gr_ore_poly_init(tU, ctx);
+ status |= gr_ore_poly_set(tU, U, ctx);
+ status |= gr_ore_poly_zero(Q, ctx);
+ status |= gr_ore_poly_set(R, tU, ctx);
+ gr_ore_poly_clear(tU, ctx);
+ return status;
+ }
+
+ slong lenQ = lenU - lenV + 1;
+
+ if (Q == U || Q == V || R == U || R == V) // treat aliasing case separately
+ {
+ gr_ore_poly_t tQ, tR;
+ gr_ore_poly_init(tQ, ctx);
+ gr_ore_poly_init(tR, ctx);
+
+ gr_ore_poly_fit_length(tQ, lenQ, ctx);
+ gr_ore_poly_fit_length(tR, lenU, ctx);
+ status = _gr_ore_poly_divrem(tQ->coeffs, tR->coeffs, U->coeffs, lenU, V->coeffs, lenV, ctx);
+ _gr_ore_poly_set_length(tQ, lenQ, ctx);
+ _gr_ore_poly_set_length(tR, lenU, ctx);
+ _gr_ore_poly_normalise(tQ, ctx);
+ _gr_ore_poly_normalise(tR, ctx);
+
+ gr_ore_poly_swap(Q, tQ, ctx);
+ gr_ore_poly_swap(R, tR, ctx);
+ gr_ore_poly_clear(tQ, ctx);
+ gr_ore_poly_clear(tR, ctx);
+ }
+ else
+ {
+ gr_ore_poly_fit_length(Q, lenQ, ctx);
+ gr_ore_poly_fit_length(R, lenU, ctx);
+ status = _gr_ore_poly_divrem(Q->coeffs, R->coeffs, U->coeffs, lenU, V->coeffs, lenV, ctx);
+ _gr_ore_poly_set_length(Q, lenQ, ctx);
+ _gr_ore_poly_set_length(R, lenU, ctx);
+ _gr_ore_poly_normalise(Q, ctx);
+ _gr_ore_poly_normalise(R, ctx);
+ }
+ return status;
+}
diff --git a/src/gr_ore_poly/test/main.c b/src/gr_ore_poly/test/main.c
index fbe884c835..79e983f851 100644
--- a/src/gr_ore_poly/test/main.c
+++ b/src/gr_ore_poly/test/main.c
@@ -15,6 +15,7 @@
#include "t-set_str.c"
#include "t-sigma_delta.c"
#include "t-mul.c"
+#include "t-divrem.c"
/* Array of test functions ***************************************************/
@@ -24,6 +25,7 @@ test_struct tests[] =
TEST_FUNCTION(gr_ore_poly_set_str),
TEST_FUNCTION(gr_ore_poly_sigma_delta),
TEST_FUNCTION(gr_ore_poly_mul),
+ TEST_FUNCTION(gr_ore_poly_divrem)
};
/* main function *************************************************************/
diff --git a/src/gr_ore_poly/test/t-divrem.c b/src/gr_ore_poly/test/t-divrem.c
new file mode 100644
index 0000000000..0a6699a0f1
--- /dev/null
+++ b/src/gr_ore_poly/test/t-divrem.c
@@ -0,0 +1,114 @@
+/*
+ Copyright (C) 2026 Maria Neagoie
+
+ 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 "ulong_extras.h"
+#include "gr_ore_poly.h"
+
+TEST_FUNCTION_START(gr_ore_poly_divrem, state)
+{
+ slong iter;
+ // slong success = 0, domain = 0, unable = 0, combined = 0;
+ for (iter = 0; iter < 3000 * flint_test_multiplier(); iter++)
+ {
+ int status = GR_SUCCESS;
+ gr_ctx_t ctx;
+ gr_ore_poly_ctx_t ore_ctx;
+ gr_ore_poly_t A, B, Q, R, QBR;
+
+ gr_ore_poly_ctx_init_randtest2(ctx, ore_ctx, state);
+
+ gr_ore_poly_init(A, ore_ctx);
+ gr_ore_poly_init(B, ore_ctx);
+ gr_ore_poly_init(Q, ore_ctx);
+ gr_ore_poly_init(R, ore_ctx);
+ gr_ore_poly_init(QBR, ore_ctx);
+
+ status |= gr_ore_poly_randtest(A, state, 1 + n_randint(state, 6), ore_ctx);
+ status |= gr_ore_poly_randtest(B, state, 1 + n_randint(state, 6), ore_ctx);
+ status |= gr_ore_poly_randtest(Q, state, 1 + n_randint(state, 6), ore_ctx);
+ status |= gr_ore_poly_randtest(R, state, 1 + n_randint(state, 6), ore_ctx);
+
+ if (n_randint(state, 3) == 0)
+ {
+ status |= gr_ore_poly_mul(A, A, B, ore_ctx);
+ status |= gr_ore_poly_add(A, A, R, ore_ctx);
+ }
+
+ if (status == GR_SUCCESS)
+ {
+ /* test aliasing */
+ switch (n_randint(state, 5))
+ {
+ case 0:
+ status |= gr_ore_poly_set(Q, A, ore_ctx);
+ status |= gr_ore_poly_divrem(Q, R, Q, B, ore_ctx);
+ break;
+ case 1:
+ status |= gr_ore_poly_set(R, A, ore_ctx);
+ status |= gr_ore_poly_divrem(Q, R, R, B, ore_ctx);
+ break;
+ case 2:
+ status |= gr_ore_poly_set(Q, B, ore_ctx);
+ status |= gr_ore_poly_divrem(Q, R, A, Q, ore_ctx);
+ break;
+ case 3:
+ status |= gr_ore_poly_set(R, B, ore_ctx);
+ status |= gr_ore_poly_divrem(Q, R, A, R, ore_ctx);
+ break;
+ default:
+ status |= gr_ore_poly_divrem(Q, R, A, B, ore_ctx);
+ break;
+ }
+
+ if (status == GR_SUCCESS)
+ {
+ status |= gr_ore_poly_mul(QBR, Q, B, ore_ctx);
+ status |= gr_ore_poly_add(QBR, QBR, R, ore_ctx);
+
+ if (status == GR_SUCCESS && gr_ore_poly_equal(QBR, A, ore_ctx) == T_FALSE)
+ {
+ flint_printf("FAIL\n\n");
+ flint_printf("A = "); gr_ore_poly_print(A, ore_ctx); flint_printf("\n");
+ flint_printf("B = "); gr_ore_poly_print(B, ore_ctx); flint_printf("\n");
+ flint_printf("Q = "); gr_ore_poly_print(Q, ore_ctx); flint_printf("\n");
+ flint_printf("R = "); gr_ore_poly_print(R, ore_ctx); flint_printf("\n");
+ flint_printf("Q*B + R = "); gr_ore_poly_print(QBR, ore_ctx); flint_printf("\n");
+ flint_abort();
+ }
+ // success++;
+ }
+ }
+
+ /*if (status == GR_DOMAIN)
+ domain++;
+
+ if (status == GR_UNABLE)
+ unable++;
+
+ if (status == 3)
+ combined++;*/
+
+ gr_ore_poly_clear(A, ore_ctx);
+ gr_ore_poly_clear(B, ore_ctx);
+ gr_ore_poly_clear(Q, ore_ctx);
+ gr_ore_poly_clear(R, ore_ctx);
+ gr_ore_poly_clear(QBR, ore_ctx);
+
+ gr_ore_poly_ctx_clear(ore_ctx);
+ gr_ctx_clear(ctx);
+ }
+ /*flint_printf("GR_SUCCESS = %d\n", success);
+ flint_printf("GR_DOMAIN = %d\n", domain);
+ flint_printf("GR_UNABLE = %d\n", unable);
+ flint_printf("Combined (3) = %d\n", combined);*/
+ TEST_FUNCTION_END(state);
+}