/*
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 2.1 of the License, or
(at your option) any later version. See .
*/
#include
#include
#include "mpoly.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 FIX_INFIX 0
#define FIX_PREFIX 1
#define FIX_POSTFIX 2
#define FIX_MATCHFIX 3
static int _is_op(slong a)
{
return a >= 0;
}
static slong _op_make(slong name, slong fix, slong prec)
{
return (prec << 10) + (fix << 8) + (name << 0);
}
static slong _op_prec(slong a)
{
return (ulong)(a) >> 10;
}
static slong _op_fix(slong a)
{
return ((ulong)(a) >> 8) & 3;
}
static slong _op_name(slong a)
{
return a&255;
}
/* initialize the R member first */
void mpoly_parse_init(mpoly_parse_t E)
{
slong i;
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 = flint_malloc(E->estore_alloc*E->R->elem_size);
for (i = 0; i < E->estore_alloc; i++)
E->R->init(E->estore + i*E->R->elem_size, E->R->ctx);
E->terminals_len = 0;
E->terminals_alloc = 5;
E->terminal_strings = FLINT_ARRAY_ALLOC(E->terminals_alloc, string_with_length_struct);
E->terminal_values = FLINT_ARRAY_ALLOC(E->terminals_alloc*E->R->elem_size, char);
for (i = 0; i < E->terminals_alloc; i++)
{
E->terminal_strings[i].str = NULL;
E->terminal_strings[i].str_len = 0;
E->R->init(E->terminal_values + E->R->elem_size*i, E->R->ctx);
}
}
void mpoly_parse_clear(mpoly_parse_t E)
{
slong i;
flint_free(E->stack);
for (i = 0; i < E->estore_alloc; i++)
E->R->clear(E->estore + E->R->elem_size*i, E->R->ctx);
flint_free(E->estore);
for (i = 0; i < E->terminals_alloc; i++)
{
flint_free(E->terminal_strings[i].str);
E->R->clear(E->terminal_values + E->R->elem_size*i, E->R->ctx);
}
flint_free(E->terminal_strings);
flint_free(E->terminal_values);
}
#define PTR_SWAP(T, a, b) \
do { \
T * _tmp_ = a; \
a = b; \
b = _tmp_; \
} while (0);
void mpoly_parse_add_terminal(mpoly_parse_t E, const char * s, const void * val)
{
slong l, n = E->terminals_len;
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,
E->R->elem_size*new_alloc);
for ( ; i < new_alloc; i++)
{
E->terminal_strings[i].str = NULL;
E->terminal_strings[i].str_len = 0;
E->R->init(E->terminal_values + E->R->elem_size*i, E->R->ctx);
}
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);
E->R->set(E->terminal_values + E->R->elem_size*n, val, E->R->ctx);
E->terminals_len = n + 1;
while (n > 0 && E->terminal_strings[n-1].str_len < E->terminal_strings[n].str_len)
{
PTR_SWAP(char, E->terminal_strings[n-1].str, E->terminal_strings[n].str);
SLONG_SWAP(E->terminal_strings[n-1].str_len, E->terminal_strings[n].str_len);
E->R->swap(E->terminal_values + E->R->elem_size*(n-1), E->terminal_values + E->R->elem_size*n, E->R->ctx);
n--;
}
}
static int mpoly_parse_top_is_expr(const mpoly_parse_t E)
{
return E->stack_len > 0 && !_is_op(E->stack[E->stack_len - 1]);
}
static void * mpoly_parse_top_expr(mpoly_parse_t E)
{
FLINT_ASSERT(E->stack_len > 0);
FLINT_ASSERT(E->stack[E->stack_len - 1] < 0);
return E->estore + E->R->elem_size*(-1 - E->stack[E->stack_len - 1]);
}
static void mpoly_parse_push_op(mpoly_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 mpoly_parse_push_expr(mpoly_parse_t E)
{
if (mpoly_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*E->R->elem_size);
for ( ; i < new_alloc; i++)
E->R->init(E->estore + E->R->elem_size*i, E->R->ctx);
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++;
E->R->swap(E->estore + E->R->elem_size*E->estore_len, E->tmp, E->R->ctx);
E->estore_len++;
return 0;
}
/* if the top is an expr, pop it, otherwise fail */
static int mpoly_parse_pop_expr(mpoly_parse_t E)
{
if (!mpoly_parse_top_is_expr(E))
return -1;
E->R->swap(E->tmp, E->estore + E->R->elem_size*(-1 - E->stack[E->stack_len - 1]), E->R->ctx);
E->estore_len--;
E->stack_len--;
return 0;
}
/* if the top is an operation op, pop it, otherwise fail */
static int mpoly_parse_pop_op(mpoly_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 mpoly_parse_pop_prec(mpoly_parse_t E, slong prec)
{
slong n, n1, n2, n3, p, l1, l3;
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)
{
E->R->mul(E->tmp, E->estore + E->R->elem_size*n3, E->estore + E->R->elem_size*n1, E->R->ctx);
E->R->swap(E->estore + E->R->elem_size*n3, E->tmp, E->R->ctx);
E->estore_len -= 1;
E->stack_len -= 2;
}
else if (_op_name(n2) == OP_PLUS)
{
l1 = E->R->length(E->estore + E->R->elem_size*n1, E->R->ctx);
l3 = E->R->length(E->estore + E->R->elem_size*n3, E->R->ctx);
do_plus:
if (l1 > l3)
{
SLONG_SWAP(l3, l1);
E->R->swap(E->estore + E->R->elem_size*n3, E->estore + E->R->elem_size*n1, E->R->ctx);
}
if (p > prec || 2*l1 >= l3)
{
E->R->add(E->estore + E->R->elem_size*n3, E->estore + E->R->elem_size*n3,
E->estore + E->R->elem_size*n1, E->R->ctx);
E->estore_len -= 1;
E->stack_len -= 2;
}
else
{
return 0;
}
}
else if (_op_name(n2) == OP_MINUS)
{
l1 = E->R->length(E->estore + E->R->elem_size*n1, E->R->ctx);
l3 = E->R->length(E->estore + E->R->elem_size*n3, E->R->ctx);
if (4*l1 >= l3 || 4*l3 >= l1)
{
E->R->sub(E->estore + E->R->elem_size*n3, E->estore + E->R->elem_size*n3,
E->estore + E->R->elem_size*n1, E->R->ctx);
E->estore_len -= 1;
E->stack_len -= 2;
}
else
{
E->R->neg(E->estore + E->R->elem_size*n1, E->estore + E->R->elem_size*n1, E->R->ctx);
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 (!E->R->divides(E->tmp, E->estore + E->R->elem_size*n3,
E->estore + E->R->elem_size*n1, E->R->ctx))
{
return -1;
}
E->R->swap(E->estore + E->R->elem_size*n3, E->tmp, E->R->ctx);
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)
E->R->neg(E->estore + E->R->elem_size*n1, E->estore + E->R->elem_size*n1, E->R->ctx);
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++;
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 mpoly_parse_parse(mpoly_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);
E->R->set_fmpz(E->tmp, c, E->R->ctx);
if (mpoly_parse_push_expr(E))
goto failed;
}
else if (*s == '^')
{
if (++s >= send || !('0' <= *s && *s <= '9'))
goto failed;
s = _parse_int(c, s, send);
if (mpoly_parse_pop_prec(E, PREC_POWER))
goto failed;
if (!mpoly_parse_top_is_expr(E))
goto failed;
if (!E->R->pow_fmpz(mpoly_parse_top_expr(E), mpoly_parse_top_expr(E), c, E->R->ctx))
goto failed;
}
else if (*s == '*')
{
if (!mpoly_parse_top_is_expr(E))
goto failed;
if (mpoly_parse_pop_prec(E, PREC_TIMES))
goto failed;
mpoly_parse_push_op(E, _op_make(OP_TIMES, FIX_INFIX, PREC_TIMES));
s++;
}
else if (*s == '+')
{
if (!mpoly_parse_top_is_expr(E))
{
mpoly_parse_push_op(E, _op_make(OP_PLUS, FIX_PREFIX, PREC_UPLUS));
}
else
{
if (mpoly_parse_pop_prec(E, PREC_PLUS))
goto failed;
mpoly_parse_push_op(E, _op_make(OP_PLUS, FIX_INFIX, PREC_PLUS));
}
s++;
}
else if (*s == '-')
{
if (!mpoly_parse_top_is_expr(E))
{
mpoly_parse_push_op(E, _op_make(OP_MINUS, FIX_PREFIX, PREC_UMINUS));
}
else
{
if (mpoly_parse_pop_prec(E, PREC_MINUS))
goto failed;
mpoly_parse_push_op(E, _op_make(OP_MINUS, FIX_INFIX, PREC_MINUS));
}
s++;
}
else if (*s == '/')
{
if (!mpoly_parse_top_is_expr(E))
goto failed;
if (mpoly_parse_pop_prec(E, PREC_DIVIDES))
goto failed;
mpoly_parse_push_op(E, _op_make(OP_DIVIDES, FIX_INFIX, PREC_DIVIDES));
s++;
}
else if (*s == ' ')
{
s++;
}
else if (*s == '(')
{
if (mpoly_parse_top_is_expr(E))
goto failed;
mpoly_parse_push_op(E, _op_make(OP_LROUND, FIX_MATCHFIX, PREC_LOWEST));
s++;
}
else if (*s == ')')
{
if (mpoly_parse_pop_prec(E, PREC_LOWEST))
goto failed;
if (mpoly_parse_pop_expr(E))
goto failed;
if (mpoly_parse_pop_op(E, OP_LROUND))
goto failed;
if (mpoly_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))
{
E->R->set(E->tmp, E->terminal_values + E->R->elem_size*k, E->R->ctx);
if (mpoly_parse_push_expr(E))
goto failed;
s += l;
goto continue_outer;
}
}
goto failed;
}
continue_outer:;
}
if (mpoly_parse_pop_prec(E, PREC_LOWEST))
goto failed;
if (mpoly_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;
}