diff --git a/doc/source/fmpz_mpoly_q.rst b/doc/source/fmpz_mpoly_q.rst index 727deb5dcc..44211a0077 100644 --- a/doc/source/fmpz_mpoly_q.rst +++ b/doc/source/fmpz_mpoly_q.rst @@ -121,11 +121,24 @@ Special values Input and output ------------------------------------------------------------------------------- +The variable strings in *x* start with the variable of most significance at index `0`. If *x* is ``NULL``, the variables are named ``x1``, ``x2``, etc. + .. function:: void fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, const fmpz_mpoly_ctx_t ctx) Prints *res* to standard output. If *x* is not *NULL*, the strings in *x* are used as the symbols for the variables. +.. function:: char * fmpz_mpoly_q_get_str_pretty(const fmpz_mpoly_q_t f, const char ** x, const fmpz_mpoly_ctx_t ctx) + + Return a string, which the user is responsible for cleaning up, representing *f*, given an array of variable strings *x*. + +.. function:: int fmpz_mpoly_q_set_str_pretty(fmpz_mpoly_q_t res, const char * s, const char ** x, fmpz_mpoly_ctx_t ctx) + + Set *res* to the fraction in the null-terminated string *str* given an array *x* of variable strings. + If parsing *str* fails, *res* is set to zero, and `-1` is returned. Otherwise, `0` is returned. + The operations ``+``, ``-``, ``*``, and ``/`` are permitted along with integers and the variables in *x*. + The character ``^`` must be immediately followed by the (integer) exponent. + If division by zero occurs, parsing fails. Random generation ------------------------------------------------------------------------------- diff --git a/doc/source/gr_generic.rst b/doc/source/gr_generic.rst index 41995a3b61..16c414d6b7 100644 --- a/doc/source/gr_generic.rst +++ b/doc/source/gr_generic.rst @@ -39,6 +39,30 @@ .. function:: int gr_generic_set_other(gr_ptr res, gr_srcptr x, gr_ctx_t xctx, gr_ctx_t ctx) int gr_generic_set_fmpq(gr_ptr res, const fmpq_t y, gr_ctx_t ctx) +Generic string parsing +----------------------------------------------------------------------------------------- + +.. macro :: GR_PARSE_BALANCE_ADDITIONS + +.. macro :: GR_PARSE_RING_EXPONENTS + +.. function:: int gr_generic_set_str_expr(gr_ptr res, const char * s, int flags, gr_ctx_t ctx) + int gr_generic_set_str(gr_ptr res, const char * s, gr_ctx_t ctx) + int gr_generic_set_str_balance_additions(gr_ptr res, const char * s, gr_ctx_t ctx) + int gr_generic_set_str_ring_exponents(gr_ptr res, const char * s, gr_ctx_t ctx) + + Parses expression string. Generators returned by :func:`gr_gens_recursive` are handled + automatically. We have the following flags: + + * ``GR_PARSE_RING_EXPONENTS`` - by default, only (nonnegative) integer literals are allowed + for exponents. If this flag is set, exponents are parsed as arbitrary subexpressions + within the same ring. + * ``GR_PARSE_BALANCE_ADDITIONS`` - attempt to improve performance for huge sums + by reording additions (useful for polynomials) + +Generic arithmetic +----------------------------------------------------------------------------------------- + .. function:: int gr_generic_add_fmpz(gr_ptr res, gr_srcptr x, const fmpz_t y, gr_ctx_t ctx) int gr_generic_add_ui(gr_ptr res, gr_srcptr x, ulong y, gr_ctx_t ctx) int gr_generic_add_si(gr_ptr res, gr_srcptr x, slong y, gr_ctx_t ctx) diff --git a/doc/source/nmod_poly.rst b/doc/source/nmod_poly.rst index 40a108b4cc..d528e90394 100644 --- a/doc/source/nmod_poly.rst +++ b/doc/source/nmod_poly.rst @@ -2310,6 +2310,7 @@ Special polynomials :func:`_nmod_poly_conway` will always succeed. Here, ``type`` can be the following values: + * ``0`` for which there is a bijection between the image of this function and the database of Conway polynomials, * ``1`` returns a random prime found in the database and sets ``degree`` to diff --git a/src/fmpz_mpoly_q.h b/src/fmpz_mpoly_q.h index a8405ed324..9a701ae43e 100644 --- a/src/fmpz_mpoly_q.h +++ b/src/fmpz_mpoly_q.h @@ -112,6 +112,8 @@ fmpz_mpoly_q_gen(fmpz_mpoly_q_t res, slong i, const fmpz_mpoly_ctx_t ctx) /* Input and output */ void fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, const fmpz_mpoly_ctx_t ctx); +char * fmpz_mpoly_q_get_str_pretty(const fmpz_mpoly_q_t f, const char ** vars, const fmpz_mpoly_ctx_t ctx); +int fmpz_mpoly_q_set_str_pretty(fmpz_mpoly_q_t res, const char * s, const char ** vars, fmpz_mpoly_ctx_t ctx); /* Random generation */ diff --git a/src/fmpz_mpoly_q/print_pretty.c b/src/fmpz_mpoly_q/print_pretty.c index 0486b1af0d..d7f439f634 100644 --- a/src/fmpz_mpoly_q/print_pretty.c +++ b/src/fmpz_mpoly_q/print_pretty.c @@ -10,6 +10,7 @@ */ #include "fmpz_mpoly_q.h" +#include "gr.h" void fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, const fmpz_mpoly_ctx_t ctx) @@ -34,3 +35,32 @@ fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, const fmpz_mp flint_printf(")"); } } + +char * fmpz_mpoly_q_get_str_pretty(const fmpz_mpoly_q_t f, const char ** vars, const fmpz_mpoly_ctx_t ctx) +{ + gr_ctx_t grctx; + char * s; + + gr_ctx_init_fmpz_mpoly_q(grctx, ctx->minfo->nvars, ctx->minfo->ord); + if (vars != NULL) + GR_MUST_SUCCEED(gr_ctx_set_gen_names(grctx, vars)); + GR_MUST_SUCCEED(gr_get_str(&s, f, grctx)); + gr_ctx_clear(grctx); + + return s; +} + +int +fmpz_mpoly_q_set_str_pretty(fmpz_mpoly_q_t res, const char * s, const char ** vars, fmpz_mpoly_ctx_t ctx) +{ + gr_ctx_t grctx; + int ret; + + gr_ctx_init_fmpz_mpoly_q(grctx, ctx->minfo->nvars, ctx->minfo->ord); + if (vars != NULL) + GR_MUST_SUCCEED(gr_ctx_set_gen_names(grctx, vars)); + ret = (GR_SUCCESS == gr_set_str(res, s, grctx)) ? 0 : -1; + + gr_ctx_clear(grctx); + return ret; +} diff --git a/src/fmpz_mpoly_q/test/main.c b/src/fmpz_mpoly_q/test/main.c index f07bb2afde..651381bfad 100644 --- a/src/fmpz_mpoly_q/test/main.c +++ b/src/fmpz_mpoly_q/test/main.c @@ -20,6 +20,7 @@ #include "t-div.c" #include "t-div_fmpq.c" #include "t-div_fmpz.c" +#include "t-get_set_str.c" #include "t-inv.c" #include "t-mul.c" #include "t-mul_fmpq.c" @@ -39,6 +40,7 @@ test_struct tests[] = TEST_FUNCTION(fmpz_mpoly_q_div), TEST_FUNCTION(fmpz_mpoly_q_div_fmpq), TEST_FUNCTION(fmpz_mpoly_q_div_fmpz), + TEST_FUNCTION(fmpz_mpoly_q_get_set_str), TEST_FUNCTION(fmpz_mpoly_q_inv), TEST_FUNCTION(fmpz_mpoly_q_mul), TEST_FUNCTION(fmpz_mpoly_q_mul_fmpq), diff --git a/src/fmpz_mpoly_q/test/t-get_set_str.c b/src/fmpz_mpoly_q/test/t-get_set_str.c new file mode 100644 index 0000000000..6585b8d1a4 --- /dev/null +++ b/src/fmpz_mpoly_q/test/t-get_set_str.c @@ -0,0 +1,54 @@ +/* + Copyright (C) 2024 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 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz_mpoly_q.h" + +TEST_FUNCTION_START(fmpz_mpoly_q_get_set_str, state) +{ + slong iter; + + for (iter = 0; iter < 1000 * 0.1 * flint_test_multiplier(); iter++) + { + fmpz_mpoly_ctx_t ctx; + fmpz_mpoly_q_t A, B; + char * s; + char * vars[] = { "x", "y", "z", "t", "u" }; + int use_vars, ok; + + fmpz_mpoly_ctx_init(ctx, 1 + n_randint(state, 4), ORD_LEX); + fmpz_mpoly_q_init(A, ctx); + fmpz_mpoly_q_init(B, ctx); + + fmpz_mpoly_q_randtest(A, state, 10, 2 + n_randint(state, 100), 5, ctx); + fmpz_mpoly_q_randtest(B, state, 10, 2 + n_randint(state, 100), 5, ctx); + use_vars = n_randint(state, 2); + + s = fmpz_mpoly_q_get_str_pretty(A, use_vars ? (const char **) vars : NULL, ctx); + ok = !fmpz_mpoly_q_set_str_pretty(B, s, use_vars ? (const char **) vars : NULL, ctx); + ok = ok && fmpz_mpoly_q_equal(A, B, ctx); + + if (!ok) + { + flint_printf("FAIL\n"); + flint_printf("A = "); fmpz_mpoly_q_print_pretty(A, NULL, ctx); flint_printf("\n\n"); + flint_printf("B = "); fmpz_mpoly_q_print_pretty(B, NULL, ctx); flint_printf("\n\n"); + flint_abort(); + } + + flint_free(s); + fmpz_mpoly_q_clear(A, ctx); + fmpz_mpoly_q_clear(B, ctx); + fmpz_mpoly_ctx_clear(ctx); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/gr.h b/src/gr.h index 0fe0f3b4b7..c7889d3803 100644 --- a/src/gr.h +++ b/src/gr.h @@ -167,6 +167,8 @@ typedef enum GR_METHOD_WRITE, GR_METHOD_WRITE_N, + _GR_METHOD_LENGTH, + GR_METHOD_RANDTEST, GR_METHOD_RANDTEST_NOT_ZERO, GR_METHOD_RANDTEST_SMALL, @@ -851,6 +853,7 @@ typedef int ((*gr_method_poly_binary_op)(gr_ptr, gr_srcptr, slong, gr_srcptr, sl typedef int ((*gr_method_poly_binary_binary_op)(gr_ptr, gr_ptr, gr_srcptr, slong, gr_srcptr, slong, gr_ctx_ptr)); typedef int ((*gr_method_poly_binary_trunc_op)(gr_ptr, gr_srcptr, slong, gr_srcptr, slong, slong, gr_ctx_ptr)); typedef int ((*gr_method_vec_ctx_op)(gr_vec_t, gr_ctx_ptr)); +typedef slong ((*_gr_method_get_si_op)(gr_srcptr, gr_ctx_ptr)); #ifdef FEXPR_H typedef int ((*gr_method_get_fexpr_op)(fexpr_t, gr_srcptr, gr_ctx_ptr)); @@ -945,6 +948,7 @@ typedef int ((*gr_method_set_fexpr_op)(gr_ptr, fexpr_vec_t, gr_vec_t, const fexp #define GR_POLY_BINARY_BINARY_OP(ctx, NAME) (((gr_method_poly_binary_binary_op *) ctx->methods)[GR_METHOD_ ## NAME]) #define GR_POLY_BINARY_TRUNC_OP(ctx, NAME) (((gr_method_poly_binary_trunc_op *) ctx->methods)[GR_METHOD_ ## NAME]) #define GR_VEC_CTX_OP(ctx, NAME) (((gr_method_vec_ctx_op *) ctx->methods)[GR_METHOD_ ## NAME]) +#define _GR_GET_SI_OP(ctx, NAME) (((_gr_method_get_si_op *) ctx->methods)[_GR_METHOD_ ## NAME]) #ifdef FEXPR_H #define GR_GET_FEXPR_OP(ctx, NAME) (((gr_method_get_fexpr_op *) ctx->methods)[GR_METHOD_ ## NAME]) #define GR_SET_FEXPR_OP(ctx, NAME) (((gr_method_set_fexpr_op *) ctx->methods)[GR_METHOD_ ## NAME]) @@ -993,6 +997,8 @@ GR_INLINE void gr_clear(gr_ptr res, gr_ctx_t ctx) { GR_INIT_CLEAR_OP(ctx, CLEAR) GR_INLINE void gr_swap(gr_ptr x, gr_ptr y, gr_ctx_t ctx) { GR_SWAP_OP(ctx, SWAP)(x, y, ctx); } GR_INLINE void gr_set_shallow(gr_ptr res, gr_srcptr x, gr_ctx_t ctx) { GR_VOID_UNARY_OP(ctx, SET_SHALLOW)(res, x, ctx); } +GR_INLINE void _gr_length(gr_srcptr x, gr_ctx_t ctx) { _GR_GET_SI_OP(ctx, LENGTH)(x, ctx); } + GR_INLINE WARN_UNUSED_RESULT int gr_randtest(gr_ptr x, flint_rand_t state, gr_ctx_t ctx) { return GR_RANDTEST(ctx, RANDTEST)(x, state, ctx); } GR_INLINE WARN_UNUSED_RESULT int gr_randtest_not_zero(gr_ptr x, flint_rand_t state, gr_ctx_t ctx) { return GR_RANDTEST(ctx, RANDTEST_NOT_ZERO)(x, state, ctx); } GR_INLINE WARN_UNUSED_RESULT int gr_randtest_small(gr_ptr x, flint_rand_t state, gr_ctx_t ctx) { return GR_RANDTEST(ctx, RANDTEST_SMALL)(x, state, ctx); } diff --git a/src/gr/acb.c b/src/gr/acb.c index c6eacd575c..b317a6cfce 100644 --- a/src/gr/acb.c +++ b/src/gr/acb.c @@ -2026,6 +2026,7 @@ gr_method_tab_input _acb_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_acb_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_acb_set_fmpq}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_acb_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_ring_exponents}, {GR_METHOD_SET_D, (gr_funcptr) _gr_acb_set_d}, {GR_METHOD_GET_SI, (gr_funcptr) _gr_acb_get_si}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_acb_get_ui}, diff --git a/src/gr/acf.c b/src/gr/acf.c index 1425f81cd4..93b95dc4d0 100644 --- a/src/gr/acf.c +++ b/src/gr/acf.c @@ -1182,7 +1182,7 @@ gr_method_tab_input _acf_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_acf_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_acf_set_fmpq}, {GR_METHOD_SET_D, (gr_funcptr) _gr_acf_set_d}, -/* {GR_METHOD_SET_STR, (gr_funcptr) _gr_acf_set_str}, */ + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_ring_exponents}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_acf_set_other}, {GR_METHOD_GET_FMPZ, (gr_funcptr) _gr_acf_get_fmpz}, diff --git a/src/gr/arb.c b/src/gr/arb.c index 1cd3e1233c..97c44aea23 100644 --- a/src/gr/arb.c +++ b/src/gr/arb.c @@ -151,12 +151,12 @@ _gr_arb_set_fmpq(arb_t res, const fmpq_t v, const gr_ctx_t ctx) } int -_gr_arb_set_str(arb_t res, const char * x, const gr_ctx_t ctx) +_gr_arb_set_str(arb_t res, const char * x, gr_ctx_t ctx) { - if (arb_set_str(res, x, ARB_CTX_PREC(ctx))) - return GR_DOMAIN; + if (!arb_set_str(res, x, ARB_CTX_PREC(ctx))) + return GR_SUCCESS; - return GR_SUCCESS; + return gr_generic_set_str_ring_exponents(res, x, ctx); } int diff --git a/src/gr/arf.c b/src/gr/arf.c index c509dcf131..6a9bde1d8c 100644 --- a/src/gr/arf.c +++ b/src/gr/arf.c @@ -16,6 +16,7 @@ #include "gr.h" #include "gr_vec.h" #include "gr_poly.h" +#include "gr_generic.h" typedef struct { @@ -152,14 +153,25 @@ _gr_arf_set(arf_t res, const arf_t x, const gr_ctx_t ctx) } int -_gr_arf_set_str(arf_t res, const char * x, const gr_ctx_t ctx) +_gr_arf_set_str(arf_t res, const char * x, gr_ctx_t ctx) { + int status; + arb_t t; arb_init(t); - arb_set_str(t, x, ARF_CTX_PREC(ctx) + 20); - arf_set_round(res, arb_midref(t), ARF_CTX_PREC(ctx), ARF_CTX_RND(ctx)); + + if (!arb_set_str(t, x, ARF_CTX_PREC(ctx) + 20)) + { + arf_set_round(res, arb_midref(t), ARF_CTX_PREC(ctx), ARF_CTX_RND(ctx)); + status = GR_SUCCESS; + } + else + { + status = gr_generic_set_str_ring_exponents(res, x, ctx); + } + arb_clear(t); - return GR_SUCCESS; + return status; } diff --git a/src/gr/ca.c b/src/gr/ca.c index 3a5a508fa3..10854bf0fa 100644 --- a/src/gr/ca.c +++ b/src/gr/ca.c @@ -1689,6 +1689,7 @@ gr_method_tab_input _ca_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_ca_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_ca_set_fmpq}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_ca_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_ring_exponents}, {GR_METHOD_GET_SI, (gr_funcptr) _gr_ca_get_si}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_ca_get_ui}, diff --git a/src/gr/fmpq_poly.c b/src/gr/fmpq_poly.c index c2bf1c6c11..467fd51138 100644 --- a/src/gr/fmpq_poly.c +++ b/src/gr/fmpq_poly.c @@ -696,7 +696,7 @@ gr_method_tab_input _fmpq_poly_methods_input[] = {GR_METHOD_SET_UI, (gr_funcptr) _gr_fmpq_poly_set_ui}, {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_fmpq_poly_set_fmpz}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_fmpq_poly_set_other}, -/* {GR_METHOD_SET_STR, (gr_funcptr) _gr_fmpq_poly_set_str}, */ + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_fmpq_poly_get_ui}, {GR_METHOD_GET_SI, (gr_funcptr) _gr_fmpq_poly_get_si}, {GR_METHOD_GET_FMPZ, (gr_funcptr) _gr_fmpq_poly_get_fmpz}, diff --git a/src/gr/fmpz.c b/src/gr/fmpz.c index 90079804fa..80ec5dbb1c 100644 --- a/src/gr/fmpz.c +++ b/src/gr/fmpz.c @@ -169,12 +169,12 @@ _gr_fmpz_set_other(fmpz_t res, gr_srcptr x, gr_ctx_t x_ctx, gr_ctx_t ctx) } int -_gr_fmpz_set_str(fmpz_t res, const char * x, const gr_ctx_t ctx) +_gr_fmpz_set_str(fmpz_t res, const char * x, gr_ctx_t ctx) { - if (fmpz_set_str(res, x, 10)) - return GR_DOMAIN; + if (!fmpz_set_str(res, x, 10)) + return GR_SUCCESS; - return GR_SUCCESS; + return gr_generic_set_str(res, x, ctx); } int diff --git a/src/gr/fmpz_mpoly.c b/src/gr/fmpz_mpoly.c index 845f0cbc7a..a0e5285985 100644 --- a/src/gr/fmpz_mpoly.c +++ b/src/gr/fmpz_mpoly.c @@ -131,6 +131,12 @@ _gr_fmpz_mpoly_randtest_small(fmpz_mpoly_t res, flint_rand_t state, gr_ctx_t ctx return GR_SUCCESS; } +slong +_gr_fmpz_mpoly_length(const fmpz_mpoly_t x, gr_ctx_t ctx) +{ + return x->length; +} + int _gr_fmpz_mpoly_write(gr_stream_t out, fmpz_mpoly_t poly, gr_ctx_t ctx) { @@ -545,6 +551,7 @@ gr_method_tab_input _gr_fmpz_mpoly_methods_input[] = {GR_METHOD_SET_SHALLOW, (gr_funcptr) _gr_fmpz_mpoly_set_shallow}, {GR_METHOD_RANDTEST, (gr_funcptr) _gr_fmpz_mpoly_randtest}, {GR_METHOD_RANDTEST_SMALL, (gr_funcptr) _gr_fmpz_mpoly_randtest_small}, + {_GR_METHOD_LENGTH, (gr_funcptr) _gr_fmpz_mpoly_length}, {GR_METHOD_WRITE, (gr_funcptr) _gr_fmpz_mpoly_write}, {GR_METHOD_ZERO, (gr_funcptr) _gr_fmpz_mpoly_zero}, {GR_METHOD_ONE, (gr_funcptr) _gr_fmpz_mpoly_one}, @@ -558,6 +565,7 @@ gr_method_tab_input _gr_fmpz_mpoly_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_fmpz_mpoly_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_fmpz_mpoly_set_fmpq}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_fmpz_mpoly_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_NEG, (gr_funcptr) _gr_fmpz_mpoly_neg}, {GR_METHOD_ADD, (gr_funcptr) _gr_fmpz_mpoly_add}, {GR_METHOD_ADD_SI, (gr_funcptr) _gr_fmpz_mpoly_add_si}, diff --git a/src/gr/fmpz_mpoly_q.c b/src/gr/fmpz_mpoly_q.c index c162723b61..63eda04bb2 100644 --- a/src/gr/fmpz_mpoly_q.c +++ b/src/gr/fmpz_mpoly_q.c @@ -16,6 +16,7 @@ #include "fmpz_mpoly.h" #include "fmpz_mpoly_q.h" #include "fmpz_mpoly_factor.h" +#include "gr_generic.h" typedef struct { @@ -94,6 +95,12 @@ _gr_fmpz_mpoly_q_randtest_small(fmpz_mpoly_q_t res, flint_rand_t state, gr_ctx_t return GR_SUCCESS; } +slong +_gr_fmpz_mpoly_q_length(const fmpz_mpoly_q_t x, gr_ctx_t ctx) +{ + return fmpz_mpoly_q_numref(x)->length + fmpz_mpoly_q_denref(x)->length; +} + int _gr_fmpz_mpoly_q_write(gr_stream_t out, fmpz_mpoly_q_t f, gr_ctx_t ctx) { @@ -533,6 +540,7 @@ gr_method_tab_input _gr_fmpz_mpoly_q_methods_input[] = {GR_METHOD_SET_SHALLOW, (gr_funcptr) _gr_fmpz_mpoly_q_set_shallow}, {GR_METHOD_RANDTEST, (gr_funcptr) _gr_fmpz_mpoly_q_randtest}, {GR_METHOD_RANDTEST_SMALL, (gr_funcptr) _gr_fmpz_mpoly_q_randtest_small}, + {_GR_METHOD_LENGTH, (gr_funcptr) _gr_fmpz_mpoly_q_length}, {GR_METHOD_WRITE, (gr_funcptr) _gr_fmpz_mpoly_q_write}, {GR_METHOD_ZERO, (gr_funcptr) _gr_fmpz_mpoly_q_zero}, {GR_METHOD_ONE, (gr_funcptr) _gr_fmpz_mpoly_q_one}, @@ -545,6 +553,7 @@ gr_method_tab_input _gr_fmpz_mpoly_q_methods_input[] = {GR_METHOD_SET_SI, (gr_funcptr) _gr_fmpz_mpoly_q_set_si}, {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_fmpz_mpoly_q_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_fmpz_mpoly_q_set_fmpq}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_NEG, (gr_funcptr) _gr_fmpz_mpoly_q_neg}, {GR_METHOD_ADD, (gr_funcptr) _gr_fmpz_mpoly_q_add}, {GR_METHOD_ADD_SI, (gr_funcptr) _gr_fmpz_mpoly_q_add_si}, diff --git a/src/gr/fmpz_poly.c b/src/gr/fmpz_poly.c index 0351ba097f..399850b2b5 100644 --- a/src/gr/fmpz_poly.c +++ b/src/gr/fmpz_poly.c @@ -21,6 +21,7 @@ #include "gr_poly.h" #include "gr_generic.h" #include "fmpz_poly_factor.h" +#include "fmpz_mpoly.h" #define FMPZ_POLY_CTX(ctx) POLYNOMIAL_CTX(ctx) #define FMPZ_POLY_CTX_VAR(ctx) (FMPZ_POLY_CTX(ctx)->var) @@ -214,16 +215,30 @@ _gr_fmpz_poly_set_other(fmpz_poly_t res, gr_srcptr x, gr_ctx_t x_ctx, const gr_c return GR_UNABLE; } -/* int _gr_fmpz_poly_set_str(fmpz_poly_t res, const char * x, const gr_ctx_t ctx) { - if (fmpz_poly_set_str(res, x)) - return GR_DOMAIN; + fmpz_mpoly_ctx_t fctx; + fmpz_mpoly_t f; + int status; + const char * vars[] = { "x" }; - return GR_SUCCESS; + fmpz_mpoly_ctx_init(fctx, 1, ORD_LEX); + fmpz_mpoly_init(f, fctx); + if (!fmpz_mpoly_set_str_pretty(f, x, vars, fctx)) + { + fmpz_mpoly_get_fmpz_poly(res, f, 0, fctx); + status = GR_SUCCESS; + } + else + { + status = GR_UNABLE; + } + fmpz_mpoly_clear(f, fctx); + fmpz_mpoly_ctx_clear(fctx); + + return status; } -*/ int _gr_fmpz_poly_get_ui(ulong * res, const fmpz_poly_t x, const gr_ctx_t ctx) @@ -799,7 +814,7 @@ gr_method_tab_input _fmpz_poly_methods_input[] = {GR_METHOD_SET_UI, (gr_funcptr) _gr_fmpz_poly_set_ui}, {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_fmpz_poly_set_fmpz}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_fmpz_poly_set_other}, -/* {GR_METHOD_SET_STR, (gr_funcptr) _gr_fmpz_poly_set_str}, */ + {GR_METHOD_SET_STR, (gr_funcptr) _gr_fmpz_poly_set_str}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_fmpz_poly_get_ui}, {GR_METHOD_GET_SI, (gr_funcptr) _gr_fmpz_poly_get_si}, {GR_METHOD_GET_FMPZ, (gr_funcptr) _gr_fmpz_poly_get_fmpz}, diff --git a/src/gr/fmpzi.c b/src/gr/fmpzi.c index ed266a8c35..54cde58da2 100644 --- a/src/gr/fmpzi.c +++ b/src/gr/fmpzi.c @@ -942,7 +942,6 @@ gr_method_tab_input _fmpzi_methods_input[] = {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_fmpzi_set_fmpq}, {GR_METHOD_SET_D, (gr_funcptr) _gr_fmpzi_set_d}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_fmpzi_set_other}, -/* {GR_METHOD_SET_STR, (gr_funcptr) _gr_fmpzi_set_str}, */ {GR_METHOD_GET_FMPZ, (gr_funcptr) _gr_fmpzi_get_fmpz}, {GR_METHOD_GET_FMPQ, (gr_funcptr) _gr_fmpzi_get_fmpq}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_fmpzi_get_ui}, diff --git a/src/gr/mpoly.c b/src/gr/mpoly.c index 2191359257..758baf15e7 100644 --- a/src/gr/mpoly.c +++ b/src/gr/mpoly.c @@ -13,6 +13,7 @@ #include "gr.h" #include "gr_mpoly.h" +#include "gr_generic.h" typedef struct { @@ -143,6 +144,12 @@ _gr_gr_mpoly_randtest(gr_mpoly_t res, flint_rand_t state, gr_ctx_t ctx) return gr_mpoly_randtest_bits(res, state, n_randint(state, 5), 1 + n_randint(state, 3), MPOLYNOMIAL_MCTX(ctx), MPOLYNOMIAL_ELEM_CTX(ctx)); } +slong +_gr_gr_mpoly_length(const gr_mpoly_t x, gr_ctx_t ctx) +{ + return x->length; +} + int _gr_gr_mpoly_write(gr_stream_t out, gr_mpoly_t poly, gr_ctx_t ctx) { @@ -312,6 +319,7 @@ gr_method_tab_input _gr__gr_gr_mpoly_methods_input[] = {GR_METHOD_SWAP, (gr_funcptr) _gr_gr_mpoly_swap}, {GR_METHOD_SET_SHALLOW, (gr_funcptr) _gr_gr_mpoly_set_shallow}, {GR_METHOD_RANDTEST, (gr_funcptr) _gr_gr_mpoly_randtest}, + {_GR_METHOD_LENGTH, (gr_funcptr) _gr_gr_mpoly_length}, {GR_METHOD_WRITE, (gr_funcptr) _gr_gr_mpoly_write}, {GR_METHOD_GENS, (gr_funcptr) _gr_gr_mpoly_gens}, {GR_METHOD_GENS_RECURSIVE, (gr_funcptr) _gr_gr_mpoly_gens_recursive}, @@ -325,6 +333,7 @@ gr_method_tab_input _gr__gr_gr_mpoly_methods_input[] = {GR_METHOD_SET_SI, (gr_funcptr) _gr_gr_mpoly_set_si}, {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_gr_mpoly_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_gr_mpoly_set_fmpq}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_NEG, (gr_funcptr) _gr_gr_mpoly_neg}, {GR_METHOD_ADD, (gr_funcptr) _gr_gr_mpoly_add}, {GR_METHOD_SUB, (gr_funcptr) _gr_gr_mpoly_sub}, diff --git a/src/gr/nf.c b/src/gr/nf.c index a1e39df332..a1e850c803 100644 --- a/src/gr/nf.c +++ b/src/gr/nf.c @@ -554,11 +554,11 @@ gr_method_tab_input _nf_methods_input[] = {GR_METHOD_SET_UI, (gr_funcptr) _gr_nf_set_ui}, {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_nf_set_fmpz}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_nf_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_SET_FEXPR, (gr_funcptr) _gr_nf_set_fexpr}, {GR_METHOD_GET_FEXPR, (gr_funcptr) _gr_nf_get_fexpr}, - {GR_METHOD_NEG, (gr_funcptr) _gr_nf_neg}, {GR_METHOD_ADD, (gr_funcptr) _gr_nf_add}, diff --git a/src/gr/nmod32.c b/src/gr/nmod32.c index 169e517ba3..24d48ba9fa 100644 --- a/src/gr/nmod32.c +++ b/src/gr/nmod32.c @@ -74,7 +74,7 @@ nmod32_randtest(nmod32_t res, flint_rand_t state, const gr_ctx_t ctx) int nmod32_write(gr_stream_t out, const nmod32_t x, const gr_ctx_t ctx) { - gr_stream_write_si(out, x[0]); + gr_stream_write_ui(out, x[0]); return GR_SUCCESS; } diff --git a/src/gr/nmod8.c b/src/gr/nmod8.c index e464aceb17..6a2dc2403c 100644 --- a/src/gr/nmod8.c +++ b/src/gr/nmod8.c @@ -74,7 +74,7 @@ nmod8_randtest(nmod8_t res, flint_rand_t state, const gr_ctx_t ctx) int nmod8_write(gr_stream_t out, const nmod8_t x, const gr_ctx_t ctx) { - gr_stream_write_si(out, x[0]); + gr_stream_write_ui(out, x[0]); return GR_SUCCESS; } diff --git a/src/gr/polynomial.c b/src/gr/polynomial.c index 71769eba5c..5f044b8ea3 100644 --- a/src/gr/polynomial.c +++ b/src/gr/polynomial.c @@ -442,6 +442,9 @@ gr_method_tab_input _gr_poly_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) polynomial_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) polynomial_set_fmpq}, {GR_METHOD_SET_OTHER, (gr_funcptr) polynomial_set_other}, + /* todo: we actually want parse using sparse polynomials + before converting to the dense representation, to avoid O(n^2) behavior */ + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_NEG, (gr_funcptr) polynomial_neg}, {GR_METHOD_ADD, (gr_funcptr) polynomial_add}, {GR_METHOD_SUB, (gr_funcptr) polynomial_sub}, diff --git a/src/gr/qqbar.c b/src/gr/qqbar.c index a481811def..b3975a2375 100644 --- a/src/gr/qqbar.c +++ b/src/gr/qqbar.c @@ -1167,6 +1167,7 @@ gr_method_tab_input _qqbar_methods_input[] = {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_qqbar_set_fmpq}, {GR_METHOD_SET_D, (gr_funcptr) _gr_qqbar_set_d}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_qqbar_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_ring_exponents}, {GR_METHOD_GET_SI, (gr_funcptr) _gr_qqbar_get_si}, {GR_METHOD_GET_UI, (gr_funcptr) _gr_qqbar_get_ui}, diff --git a/src/gr/series.c b/src/gr/series.c index 9189dc3f97..2e908abc96 100644 --- a/src/gr/series.c +++ b/src/gr/series.c @@ -1981,6 +1981,7 @@ gr_method_tab_input _gr_series_methods_input[] = {GR_METHOD_SET_FMPZ, (gr_funcptr) _gr_gr_series_set_fmpz}, {GR_METHOD_SET_FMPQ, (gr_funcptr) _gr_gr_series_set_fmpq}, {GR_METHOD_SET_OTHER, (gr_funcptr) _gr_gr_series_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str_balance_additions}, {GR_METHOD_NEG, (gr_funcptr) _gr_gr_series_neg}, {GR_METHOD_ADD, (gr_funcptr) _gr_gr_series_add}, {GR_METHOD_SUB, (gr_funcptr) _gr_gr_series_sub}, diff --git a/src/gr_generic.h b/src/gr_generic.h index dc174b4e0d..139c698f91 100644 --- a/src/gr_generic.h +++ b/src/gr_generic.h @@ -106,6 +106,14 @@ WARN_UNUSED_RESULT int gr_generic_neg_one(gr_ptr res, gr_ctx_t ctx); WARN_UNUSED_RESULT int gr_generic_set_other(gr_ptr res, gr_srcptr x, gr_ctx_t xctx, gr_ctx_t ctx); WARN_UNUSED_RESULT int gr_generic_set_fmpq(gr_ptr res, const fmpq_t y, gr_ctx_t ctx); +#define GR_PARSE_BALANCE_ADDITIONS 1 +#define GR_PARSE_RING_EXPONENTS 2 + +WARN_UNUSED_RESULT int gr_generic_set_str_expr(gr_ptr res, const char * s, int flags, gr_ctx_t ctx); +WARN_UNUSED_RESULT int gr_generic_set_str(gr_ptr res, const char * s, gr_ctx_t ctx); +WARN_UNUSED_RESULT int gr_generic_set_str_balance_additions(gr_ptr res, const char * s, gr_ctx_t ctx); +WARN_UNUSED_RESULT int gr_generic_set_str_ring_exponents(gr_ptr res, const char * s, gr_ctx_t ctx); + #ifdef FEXPR_H WARN_UNUSED_RESULT int gr_generic_set_fexpr(gr_ptr res, fexpr_vec_t inputs, gr_vec_t outputs, const fexpr_t expr, gr_ctx_t ctx); #endif diff --git a/src/gr_generic/generic.c b/src/gr_generic/generic.c index b164a1d0fb..89cbdbfcc8 100644 --- a/src/gr_generic/generic.c +++ b/src/gr_generic/generic.c @@ -141,6 +141,12 @@ int gr_generic_randtest_small(gr_ptr x, flint_rand_t state, gr_ctx_t ctx) return status; } +slong _gr_generic_length(gr_srcptr x, gr_ctx_t ctx) +{ + return 0; +} + + int gr_generic_gens(gr_vec_t vec, gr_ctx_t ctx) { gr_vec_set_length(vec, 0, ctx); @@ -2493,6 +2499,8 @@ const gr_method_tab_input _gr_generic_methods[] = {GR_METHOD_SWAP, (gr_funcptr) gr_generic_swap}, {GR_METHOD_SET_SHALLOW, (gr_funcptr) gr_generic_set_shallow}, + {_GR_METHOD_LENGTH, (gr_funcptr) _gr_generic_length}, + {GR_METHOD_WRITE, (gr_funcptr) gr_generic_write}, {GR_METHOD_WRITE_N, (gr_funcptr) gr_generic_write_n}, @@ -2521,6 +2529,8 @@ const gr_method_tab_input _gr_generic_methods[] = {GR_METHOD_SET_OTHER, (gr_funcptr) gr_generic_set_other}, + {GR_METHOD_SET_STR, (gr_funcptr) gr_generic_set_str}, + {GR_METHOD_GET_FEXPR_SERIALIZE, (gr_funcptr) gr_generic_get_fexpr_serialize}, {GR_METHOD_SET_FEXPR, (gr_funcptr) gr_generic_set_fexpr}, diff --git a/src/gr_generic/set_str_expr.c b/src/gr_generic/set_str_expr.c new file mode 100644 index 0000000000..71ac5cdffd --- /dev/null +++ b/src/gr_generic/set_str_expr.c @@ -0,0 +1,713 @@ +/* + Copyright (C) 2020 Daniel Schultz + Copyright (C) 2024 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 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "mpoly.h" +#include "gr.h" +#include "gr_special.h" +#include "gr_vec.h" +#include "gr_generic.h" + +#define PREC_LOWEST 0 +#define PREC_PLUS 1 +#define PREC_MINUS 1 +#define PREC_TIMES 2 +#define PREC_DIVIDES 2 +#define PREC_UPLUS 3 +#define PREC_UMINUS 3 +#define PREC_POWER 4 +#define PREC_HIGHEST 255 + +#define OP_TIMES 0 +#define OP_PLUS 1 +#define OP_MINUS 2 +#define OP_DIVIDES 3 +#define OP_LROUND 4 +#define OP_POWER 5 + +#define FIX_INFIX 0 +#define FIX_PREFIX 1 +#define FIX_POSTFIX 2 +#define FIX_MATCHFIX 3 + +typedef struct { + gr_ctx_struct * R; + slong * stack; + slong stack_len; + slong stack_alloc; + char * estore; + slong estore_len; + slong estore_alloc; + void * tmp; + string_with_length_struct * terminal_strings; + char * terminal_values; + slong terminals_alloc; + slong terminals_len; + int flags; + _gr_method_get_si_op size_func; +} gr_parse_struct; + +typedef gr_parse_struct gr_parse_t[1]; + +void _gr_parse_init(gr_parse_t E); +void _gr_parse_clear(gr_parse_t E); +void _gr_parse_add_terminal(gr_parse_t E, const char * s, const void * v); +int _gr_parse_parse(gr_parse_t E, void * res, const char * s, slong len); + +FLINT_FORCE_INLINE int _is_op(slong a) +{ + return a >= 0; +} + +FLINT_FORCE_INLINE slong _op_make(slong name, slong fix, slong prec) +{ + return (prec << 10) + (fix << 8) + (name << 0); +} + +FLINT_FORCE_INLINE slong _op_prec(slong a) +{ + return (ulong)(a) >> 10; +} + +FLINT_FORCE_INLINE slong _op_fix(slong a) +{ + return ((ulong)(a) >> 8) & 3; +} + +FLINT_FORCE_INLINE slong _op_name(slong a) +{ + return a&255; +} + +/* initialize the R member first */ +void _gr_parse_init(gr_parse_t E) +{ + slong i; + + E->flags = 0; + E->size_func = (_gr_method_get_si_op) _gr_length; + + E->stack_len = 0; + E->stack_alloc = 20; + E->stack = FLINT_ARRAY_ALLOC(E->stack_alloc, slong); + + E->estore_len = 0; + E->estore_alloc = 10; + E->estore = gr_heap_init_vec(E->estore_alloc, E->R); + + E->terminals_len = 0; + E->terminals_alloc = 5; + E->terminal_values = gr_heap_init_vec(E->terminals_alloc, E->R); + E->terminal_strings = FLINT_ARRAY_ALLOC(E->terminals_alloc, string_with_length_struct); + + for (i = 0; i < E->terminals_alloc; i++) + { + E->terminal_strings[i].str = NULL; + E->terminal_strings[i].str_len = 0; + } + +} + +void _gr_parse_clear(gr_parse_t E) +{ + slong i; + + flint_free(E->stack); + + gr_heap_clear_vec(E->estore, E->estore_alloc, E->R); + gr_heap_clear_vec(E->terminal_values, E->terminals_alloc, E->R); + + for (i = 0; i < E->terminals_alloc; i++) + flint_free(E->terminal_strings[i].str); + + flint_free(E->terminal_strings); +} + +void _gr_parse_add_terminal(gr_parse_t E, const char * s, const void * val) +{ + slong l, n = E->terminals_len; + slong sz = E->R->sizeof_elem; + + if (n + 1 > E->terminals_alloc) + { + slong i = E->terminals_alloc; + slong new_alloc = FLINT_MAX(n + 1, i + i/2); + + E->terminal_strings = (string_with_length_struct *) flint_realloc( + E->terminal_strings, new_alloc* + sizeof(string_with_length_struct)); + + E->terminal_values = (char *) flint_realloc(E->terminal_values, sz * new_alloc); + for ( ; i < new_alloc; i++) + { + E->terminal_strings[i].str = NULL; + E->terminal_strings[i].str_len = 0; + gr_init(GR_ENTRY(E->terminal_values, i, sz), E->R); + } + + E->terminals_alloc = new_alloc; + } + + l = strlen(s); + E->terminal_strings[n].str_len = l; + + E->terminal_strings[n].str = (char *) flint_realloc(E->terminal_strings[n].str, l + 1); + memcpy(E->terminal_strings[n].str, s, l + 1); + + GR_MUST_SUCCEED(gr_set(GR_ENTRY(E->terminal_values, n, sz), val, E->R)); + + E->terminals_len = n + 1; + + while (n > 0 && E->terminal_strings[n-1].str_len < E->terminal_strings[n].str_len) + { + FLINT_SWAP(char *, E->terminal_strings[n-1].str, E->terminal_strings[n].str); + FLINT_SWAP(slong, E->terminal_strings[n-1].str_len, E->terminal_strings[n].str_len); + gr_swap(GR_ENTRY(E->terminal_values, n - 1, sz), GR_ENTRY(E->terminal_values, n, sz), E->R); + n--; + } +} + +static int gr_parse_top_is_expr(const gr_parse_t E) +{ + return E->stack_len > 0 && !_is_op(E->stack[E->stack_len - 1]); +} + +static void * gr_parse_top_expr(gr_parse_t E) +{ + slong sz = E->R->sizeof_elem; + + FLINT_ASSERT(E->stack_len > 0); + FLINT_ASSERT(E->stack[E->stack_len - 1] < 0); + + return GR_ENTRY(E->estore, -1 - E->stack[E->stack_len - 1], sz); +} + +static void _gr_parse_push_op(gr_parse_t E, slong op) +{ + FLINT_ASSERT(_is_op(op)); + _slong_array_fit_length(&E->stack, &E->stack_alloc, E->stack_len + 1); + E->stack[E->stack_len] = op; + E->stack_len++; +} + +/* if the top is not an expr, push the tmp, otherwise fail */ +static int _gr_parse_push_expr(gr_parse_t E) +{ + slong sz = E->R->sizeof_elem; + + if (gr_parse_top_is_expr(E)) + return -1; + + if (E->estore_len + 1 > E->estore_alloc) + { + slong i = E->estore_alloc; + slong new_alloc = FLINT_MAX(E->estore_len + 1, i + i/2); + E->estore = flint_realloc(E->estore, new_alloc*sz); + for ( ; i < new_alloc; i++) + gr_init(GR_ENTRY(E->estore, i, sz), E->R); + E->estore_alloc = new_alloc; + } + + _slong_array_fit_length(&E->stack, &E->stack_alloc, E->stack_len + 1); + E->stack[E->stack_len] = -1 - E->estore_len; + E->stack_len++; + gr_swap(GR_ENTRY(E->estore, E->estore_len, sz), E->tmp, E->R); + E->estore_len++; + return 0; +} + +/* if the top is an expr, pop it, otherwise fail */ +static int _gr_parse_pop_expr(gr_parse_t E) +{ + slong sz = E->R->sizeof_elem; + + if (!gr_parse_top_is_expr(E)) + return -1; + + gr_swap(E->tmp, GR_ENTRY(E->estore, -1 - E->stack[E->stack_len - 1], sz), E->R); + E->estore_len--; + E->stack_len--; + return 0; +} + +/* if the top is an operation op, pop it, otherwise fail */ +static int _gr_parse_pop_op(gr_parse_t E, slong op) +{ + slong n = E->stack_len - 1; + + if (n < 0 || !_is_op(E->stack[n]) || _op_name(E->stack[n]) != op) + return -1; + + E->stack_len = n; + return 0; +} + +/* pop ops with precedence > prec */ +static int _gr_parse_pop_prec(gr_parse_t E, slong prec) +{ + slong n, n1, n2, n3, p, l1, l3; + slong sz = E->R->sizeof_elem; + + if (E->stack_len < 1) + return -1; + +again: + + n = E->stack_len; + if (n < 2) + return 0; + + n1 = E->stack[n-1]; + n2 = E->stack[n-2]; + + if (_is_op(n1) || !_is_op(n2)) + return 0; + + n1 = -1-n1; + + p = _op_prec(n2); + + if (p < prec) + return 0; + + if (_op_fix(n2) == FIX_INFIX) + { + n3 = E->stack[n-3]; + FLINT_ASSERT(!_is_op(n3)); + n3 = -1 - n3; + + FLINT_ASSERT(n1 == n3 + 1); + + if (_op_name(n2) == OP_TIMES) + { + if (GR_SUCCESS != gr_mul(E->tmp, GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + gr_swap(GR_ENTRY(E->estore, n3, sz), E->tmp, E->R); + E->estore_len -= 1; + E->stack_len -= 2; + } + else if (_op_name(n2) == OP_PLUS) + { + if (E->flags & GR_PARSE_BALANCE_ADDITIONS) + { + l1 = E->size_func(GR_ENTRY(E->estore, n1, sz), E->R); + l3 = E->size_func(GR_ENTRY(E->estore, n3, sz), E->R); + } + else + { + l1 = l3 = 0; + } + + do_plus: + + if (l1 > l3) + { + FLINT_SWAP(slong, l3, l1); + gr_swap(GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n1, sz), E->R); + } + + if (p > prec || 2*l1 >= l3) + { + if (GR_SUCCESS != gr_add(GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + E->estore_len -= 1; + E->stack_len -= 2; + } + else + { + return 0; + } + } + else if (_op_name(n2) == OP_MINUS) + { + if (E->flags & GR_PARSE_BALANCE_ADDITIONS) + { + l1 = E->size_func(GR_ENTRY(E->estore, n1, sz), E->R); + l3 = E->size_func(GR_ENTRY(E->estore, n3, sz), E->R); + } + else + { + l1 = l3 = 0; + } + + if (4*l1 >= l3 || 4*l3 >= l1) + { + if (GR_SUCCESS != gr_sub(GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n3, sz), + GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + E->estore_len -= 1; + E->stack_len -= 2; + } + else + { + if (GR_SUCCESS != gr_neg(GR_ENTRY(E->estore, n1, sz), GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + E->stack[n-2] = _op_make(OP_PLUS, FIX_INFIX, PREC_PLUS); + goto do_plus; + } + } + else if (_op_name(n2) == OP_DIVIDES) + { + /* + NOTE: if divides and times have the same precedence and the + multiplications were to be delayed as the addition are, + then there would have to be more shenenigans here. + */ + if (GR_SUCCESS != gr_div(E->tmp, GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + gr_swap(GR_ENTRY(E->estore, n3, sz), E->tmp, E->R); + E->estore_len -= 1; + E->stack_len -= 2; + } + else if (_op_name(n2) == OP_POWER) + { + if (GR_SUCCESS != gr_pow(E->tmp, GR_ENTRY(E->estore, n3, sz), GR_ENTRY(E->estore, n1, sz), E->R)) + { + return -1; + } + + gr_swap(GR_ENTRY(E->estore, n3, sz), E->tmp, E->R); + E->estore_len -= 1; + E->stack_len -= 2; + } + else + { + flint_throw(FLINT_ERROR, "_pop_stack: internal error"); + } + + goto again; + } + else if (_op_fix(n2) == FIX_PREFIX) + { + if (_op_name(n2) == OP_MINUS) + GR_MUST_SUCCEED(gr_neg(GR_ENTRY(E->estore, n1, sz), GR_ENTRY(E->estore, n1, sz), E->R)); + + E->stack[n-2] = -1-n1; + E->stack_len -= 1; + goto again; + } + else + { + return 0; + } +} + +static const char * _parse_int(fmpz_t c, const char * s, const char * end) +{ + char * buffer, * v; + const char * send = s + 1; + TMP_INIT; + + while (send < end && '0' <= *send && *send <= '9') + send++; + + switch (send - s) + { + case 1: + fmpz_set_ui(c, s[0] - '0'); + s += 1; + break; + case 2: + fmpz_set_ui(c, (s[0] - '0') * 10 + (s[1] - '0')); + s += 2; + break; + case 3: + fmpz_set_ui(c, (s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0')); + s += 3; + break; + default: + TMP_START; + v = buffer = (char *) TMP_ALLOC((send - s + 1)*sizeof(char)); + while (s < send) + *v++ = *s++; + *v++ = '\0'; + fmpz_set_str(c, buffer, 10); + TMP_END; + } + + return s; +} + +int _gr_parse_parse(gr_parse_t E, void * poly, const char * s, slong slen) +{ + const char * send = s + slen; + fmpz_t c; + int ret; + + fmpz_init(c); + E->tmp = poly; + + while (s < send) + { + if ('0' <= *s && *s <= '9') + { + s = _parse_int(c, s, send); + + GR_MUST_SUCCEED(gr_set_fmpz(E->tmp, c, E->R)); + if (_gr_parse_push_expr(E)) + goto failed; + } + else if (*s == '^') + { + if (E->flags & GR_PARSE_RING_EXPONENTS) + { + if (!gr_parse_top_is_expr(E)) + goto failed; + + if (_gr_parse_pop_prec(E, PREC_POWER)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_POWER, FIX_INFIX, PREC_POWER)); + s++; + } + else + { + if (++s >= send || !('0' <= *s && *s <= '9')) + goto failed; + + s = _parse_int(c, s, send); + + if (_gr_parse_pop_prec(E, PREC_POWER)) + goto failed; + + if (!gr_parse_top_is_expr(E)) + goto failed; + + if (GR_SUCCESS != gr_pow_fmpz(gr_parse_top_expr(E), gr_parse_top_expr(E), c, E->R)) + goto failed; + } + } + else if (*s == '*') + { + if (!gr_parse_top_is_expr(E)) + goto failed; + + if (_gr_parse_pop_prec(E, PREC_TIMES)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_TIMES, FIX_INFIX, PREC_TIMES)); + s++; + } + else if (*s == '+') + { + if (!gr_parse_top_is_expr(E)) + { + _gr_parse_push_op(E, _op_make(OP_PLUS, FIX_PREFIX, PREC_UPLUS)); + } + else + { + if (_gr_parse_pop_prec(E, PREC_PLUS)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_PLUS, FIX_INFIX, PREC_PLUS)); + } + s++; + } + else if (*s == '-') + { + if (!gr_parse_top_is_expr(E)) + { + _gr_parse_push_op(E, _op_make(OP_MINUS, FIX_PREFIX, PREC_UMINUS)); + } + else + { + if (_gr_parse_pop_prec(E, PREC_MINUS)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_MINUS, FIX_INFIX, PREC_MINUS)); + } + s++; + } + else if (*s == '/') + { + if (!gr_parse_top_is_expr(E)) + goto failed; + + if (_gr_parse_pop_prec(E, PREC_DIVIDES)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_DIVIDES, FIX_INFIX, PREC_DIVIDES)); + s++; + } + else if (*s == ' ') + { + s++; + } + else if (*s == '(') + { + if (gr_parse_top_is_expr(E)) + goto failed; + + _gr_parse_push_op(E, _op_make(OP_LROUND, FIX_MATCHFIX, PREC_LOWEST)); + s++; + } + else if (*s == ')') + { + if (_gr_parse_pop_prec(E, PREC_LOWEST)) + goto failed; + + if (_gr_parse_pop_expr(E)) + goto failed; + + if (_gr_parse_pop_op(E, OP_LROUND)) + goto failed; + + if (_gr_parse_push_expr(E)) + goto failed; + + s++; + } + else + { + slong k; + for (k = 0; k < E->terminals_len; k++) + { + slong l = E->terminal_strings[k].str_len; + if (0 == strncmp(s, E->terminal_strings[k].str, l)) + { + GR_MUST_SUCCEED(gr_set(E->tmp, GR_ENTRY(E->terminal_values, k, E->R->sizeof_elem), E->R)); + if (_gr_parse_push_expr(E)) + goto failed; + + s += l; + goto continue_outer; + } + } + + /* some builtin constants (for R and C) */ + /* todo: builtin functions */ + + if (0 == strncmp(s, "pi", 2) || 0 == strncmp(s, "Pi", 2)) + { + if (GR_SUCCESS != gr_pi(E->tmp, E->R)) + goto failed; + if (_gr_parse_push_expr(E)) + goto failed; + s += 2; + goto continue_outer; + } + + if (0 == strncmp(s, "i", 1) || 0 == strncmp(s, "I", 1)) + { + if (GR_SUCCESS != gr_i(E->tmp, E->R)) + goto failed; + if (_gr_parse_push_expr(E)) + goto failed; + s += 1; + goto continue_outer; + } + + goto failed; + } + continue_outer:; + } + + if (_gr_parse_pop_prec(E, PREC_LOWEST)) + goto failed; + + if (_gr_parse_pop_expr(E)) + goto failed; + + if (E->stack_len != 0) + goto failed; + + ret = 0; + +done: + + fmpz_clear(c); + return ret; + +failed: + + ret = -1; + goto done; +} + +int +gr_generic_set_str_expr(gr_ptr res, const char * s, int flags, gr_ctx_t ctx) +{ + gr_parse_t parse; + gr_vec_t gens; + slong i; + char * g; + int status; + + /* Quickly see if we simply have an integer literal, e.g. 0 */ + fmpz_t c; + fmpz_init(c); + if (!fmpz_set_str(c, s, 10)) + { + status = gr_set_fmpz(res, c, ctx); + } + else + { + parse->R = ctx; + _gr_parse_init(parse); + parse->flags = flags; + + gr_vec_init(gens, 0, ctx); + if (gr_gens_recursive(gens, ctx) == GR_SUCCESS) + { + for (i = 0; i < gens->length; i++) + { + GR_MUST_SUCCEED(gr_get_str(&g, gr_vec_entry_srcptr(gens, i, ctx), ctx)); + /* todo: version that consumes s and x */ + _gr_parse_add_terminal(parse, g, gr_vec_entry_srcptr(gens, i, ctx)); + flint_free(g); + } + } + + gr_vec_clear(gens, ctx); + + status = _gr_parse_parse(parse, res, s, strlen(s)) ? GR_UNABLE : GR_SUCCESS; + + _gr_parse_clear(parse); + } + + fmpz_clear(c); + + return status; +} + +int +gr_generic_set_str(gr_ptr res, const char * s, gr_ctx_t ctx) +{ + return gr_generic_set_str_expr(res, s, 0, ctx); +} + +int +gr_generic_set_str_balance_additions(gr_ptr res, const char * s, gr_ctx_t ctx) +{ + return gr_generic_set_str_expr(res, s, GR_PARSE_BALANCE_ADDITIONS, ctx); +} + +int +gr_generic_set_str_ring_exponents(gr_ptr res, const char * s, gr_ctx_t ctx) +{ + return gr_generic_set_str_expr(res, s, GR_PARSE_RING_EXPONENTS, ctx); +} diff --git a/src/python/flint_ctypes.py b/src/python/flint_ctypes.py index b643724015..12f86213e9 100644 --- a/src/python/flint_ctypes.py +++ b/src/python/flint_ctypes.py @@ -4451,33 +4451,39 @@ def order(self): class FiniteField_fq(FiniteField_base): - def __init__(self, p, n): + def __init__(self, p, n, var=None): gr_ctx.__init__(self) p = ZZ(p) n = int(n) assert p.is_prime() assert n >= 1 - libgr.gr_ctx_init_fq(self._ref, p._ref, n, None) + if var is not None: + var = ctypes.c_char_p(str(var).encode('ascii')) + libgr.gr_ctx_init_fq(self._ref, p._ref, n, var) self._elem_type = fq class FiniteField_fq_nmod(FiniteField_base): - def __init__(self, p, n): + def __init__(self, p, n, var=None): gr_ctx.__init__(self) - p = ZZ(p) + p = self._as_ui(p) n = int(n) - assert p.is_prime() + assert ZZ(p).is_prime() assert n >= 1 - libgr.gr_ctx_init_fq_nmod(self._ref, p._ref, n, None) + if var is not None: + var = ctypes.c_char_p(str(var).encode('ascii')) + libgr.gr_ctx_init_fq_nmod(self._ref, p, n, var) self._elem_type = fq_nmod class FiniteField_fq_zech(FiniteField_base): - def __init__(self, p, n): + def __init__(self, p, n, var=None): gr_ctx.__init__(self) - p = ZZ(p) + p = self._as_ui(p) n = int(n) - assert p.is_prime() + assert ZZ(p).is_prime() assert n >= 1 - libgr.gr_ctx_init_fq_zech(self._ref, p._ref, n, None) + if var is not None: + var = ctypes.c_char_p(str(var).encode('ascii')) + libgr.gr_ctx_init_fq_zech(self._ref, p, n, var) self._elem_type = fq_zech @@ -4512,12 +4518,14 @@ class fq_zech(fq_elem): class NumberField_nf(gr_ctx): - def __init__(self, pol): + def __init__(self, pol, var=None): pol = ZZx(pol) # assert pol.is_irreducible() gr_ctx.__init__(self) libgr.gr_ctx_init_nf_fmpz_poly(self._ref, pol._ref) self._elem_type = nf_elem + if var is not None: + self._set_gen_name(var) class nf_elem(gr_elem): _struct_type = nf_elem_struct @@ -7521,11 +7529,36 @@ def test_mpoly(): assert str((x+1)*y**2 + (x+2)*y) assert str((x+1)*y**2 - (x+2)*y) == "(x+1)*y^2 + (-x-2)*y" + assert str(FiniteField_fq(3, 2, "c").gen()) == "c" + assert str(FiniteField_fq_nmod(3, 2, "d").gen()) == "d" + assert str(FiniteField_fq_zech(3, 2, "e").gen()) == "e^1" 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]' +def test_set_str(): + assert RR("1/4") == RR(1)/4 + v = RR("pi ^ (1 / 2)") + assert 1.77 < v < 1.78 + assert CC("1/2 + i/4") == 0.5+0.25j + assert CC("(-1)^(1/2)") == 1j + assert CF("(-1)^(1/2)") == 1j + assert abs(RF("2^(1/2)") ** 2) - 2 < 1e-14 + assert CC_ca("i*pi/2").exp() == CC_ca.i() + + assert ZZ("1 + 2^10") == 1025 + x = ZZx.gen(); R = PolynomialRing_gr_mpoly(NumberField(x**3+x+1), 3, ["x", "y", "z"]) + a, x, y, z = R.gens(recursive=True) + assert R("((a-x+1)*y + (a^2+1)*y^2 + z)^2") == ((a-x+1)*y + (a**2+1)*y**2 + z)**2 + x = ZZx.gen() + R = NumberField(x**2+1, "b") + assert R("b-1") == R.gen()-1 + + R = FractionField_fmpz_mpoly_q(2, ["x", "y"]) + x, y = R.gens() + assert R("(4+4*x-y*(-4))^2 / (1+x+y) / 16") == 1+x+y + def test_ca_notebook_examples(): # algebraic number identity NumberI = fexpr("NumberI") @@ -7567,6 +7600,8 @@ def test_ca_notebook_examples(): for fname in dir(): if fname.startswith("test_"): print(fname + "...", end="") + import sys + sys.stdout.flush() t1 = time() globals()[fname]() t2 = time()