/* Simple expression parser for GMP-ECM. Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2012 Jim Fougeron, Paul Zimmermann and Alexander Kruppa. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "ecm-ecm.h" #ifdef HAVE_STRINGS_H # include /* for strncasecmp */ #endif #ifdef HAVE_CTYPE_H # include #endif /***************************************************************** * Syntax for this VERY simple recursive expression parser: * * * * ( or [ or { along with ) or ] or } are valid for grouping * * Normal "simple" operators: + - * / (. can be used for *) * * Module: n%m 345%11 * * Unary minus is supported: -n -500 * * Exponentation: n^m 2^500 * * Simple factorial: n! 53! == 1*2*3*4...*52*53 * * Multi-factorial: n!m 14!3 == 14.11.8.5.2 * * Simple Primorial: n# 11# == 2*3*5*7*11 * * Reduced Primorial: n#m 17#5 == 5.7.11.13.17 * * * * Supported functions: (case insensitive) * * Phi(n,x) Phi(3,5) == 1+x+x^2 = 31 * * GCD(m,n) GCD(120, 28) = 4 * * U(p,q,n) * * primU(p,q,n) * * TODO: PhiL(k,n), PhiM(k,n) * * only for bases 2,3,5,6,7,10,11 (times a square) * * Note for developers: * * First k-1 arguments are passed as an mpz_t array * * * * NOTE Lines ending in a \ character are "joined" * * NOTE C++ // single line comments (rest of line is a comment) * * * ****************************************************************/ /* value only used by the expression parser */ static mpz_t t, mpOne; static char *expr_str; static void eval_power (mpz_t n, const mpz_t arg, char op); static void eval_product (mpz_t n, const mpz_t arg, char op); static void eval_sum (mpz_t n, const mpz_t arg, char op); static int eval_Phi (mpz_t *params, mpz_t n); static int eval_PhiL (mpz_t *params, mpz_t n); static int eval_PhiM (mpz_t *params, mpz_t n); // static int eval_gcd (mpz_t *params, mpz_t n); static int eval_U (mpz_t *params, mpz_t n); static int eval_primU (mpz_t *params, mpz_t n); static int eval_2 (int bInFuncParams); static int aurif (mpz_t output, mpz_t n, mpz_t base, int sign); #if 0 /* strncasecmp is a required function in configure.in */ #if defined (_MSC_VER) || defined (__MINGW32__) #define strncasecmp strnicmp #endif #endif /***************************************/ /* Main expression evaluation function */ /* This is the function that the app */ /* calls to read the expression line */ /***************************************/ int eval (mpcandi_t *n, FILE *fd, int primetest) { int ret; int nMaxSize = 2000, nCurSize = 0; int c; char *expr = (char *) malloc (nMaxSize + 1); ASSERT_ALWAYS (expr != NULL); JoinLinesLoop: c = fgetc (fd); if (0) { ChompLine: do c = fgetc (fd); while (c != EOF && !IS_NEWLINE(c)); if (IS_NEWLINE(c)) goto JoinLinesLoop; } while (c != EOF && !IS_NEWLINE(c) && c != ';') { if (c == '/') { /* This might be a C++ // comment or it might be a / division operator. Check it out, and if it is a comment, then "eat it" */ int peek_c = fgetc (fd); if (peek_c == '/') /* Got a C++ single line comment, so Chomp the line */ goto ChompLine; /* Put the char back on the file, then allow the code to add the '/' char to the buffer */ ungetc (peek_c, fd); } /* strip space and tabs out here, and then we DON'T have to mess with them in the rest of the parser */ if (!isspace (c) && c != '"' && c != '\'') expr[nCurSize++] = (char) c; if (nCurSize == nMaxSize) { char *cp; nMaxSize += nMaxSize / 2; cp = (char *) realloc (expr, nMaxSize + 1); ASSERT_ALWAYS (cp != NULL); expr = cp; } c = fgetc (fd); } expr[nCurSize] = 0; if (!nCurSize) ret = 0; else { if (c == ';') ungetc (c, fd); mpz_init (t); expr_str = expr; ret = eval_2 (0); if (ret) { char *s; char *cpTmpExpr = expr; s = mpz_get_str (NULL, 10, t); if (!strcmp(s, cpTmpExpr)) cpTmpExpr = NULL; ret = mpcandi_t_add_candidate (n, t, cpTmpExpr, primetest); free (s); /* size strlen (s) + 1 */ } mpz_clear(t); } #if defined (DEBUG_EVALUATOR) gmp_fprintf (stderr, "eval(\"%s\") = %Zd\n", expr, n->n); #endif free(expr); return ret; } int eval_str (mpcandi_t *n, char *cp, int primetest, char **EndChar) { int ret; int nMaxSize=2000, nCurSize=0; char *c; char *expr = (char *) malloc(nMaxSize+1); ASSERT_ALWAYS (expr != NULL); c = cp; JoinLinesLoop: if (*c == '#') { do ++c; while (*c && !IS_NEWLINE(*c)); if (IS_NEWLINE(*c)) goto JoinLinesLoop; } while (*c && !IS_NEWLINE(*c) && *c != ';') { /* strip space and tabs out here, and then we DON'T have to mess with them in the rest of the parser */ if (!isspace((int) *c) && *c != '"' && *c != '\'') expr[nCurSize++] = *c; if (nCurSize == nMaxSize) { char *cp; nMaxSize += nMaxSize / 2; cp = (char *) realloc (expr, nMaxSize + 1); ASSERT_ALWAYS (cp != NULL); expr = cp; } ++c; } expr[nCurSize] = 0; if (!nCurSize) ret = 0; else { if (*c != ';') ++c; mpz_init(t); expr_str = expr; ret = eval_2(0); if (ret) { char *s; char *cpTmpExpr = expr; s = mpz_get_str (NULL, 10, t); if (!strcmp(s, cpTmpExpr)) cpTmpExpr = NULL; ret = mpcandi_t_add_candidate(n, t, cpTmpExpr, primetest); free (s); /* size strlen (s) + 1 */ } mpz_clear(t); } #if defined (DEBUG_EVALUATOR) gmp_fprintf (stderr, "eval_str(\"%s\") = %Zd\n", expr, n->n); #endif free(expr); if (EndChar && *EndChar) *EndChar = c; return ret; } /** * Evaluate "power" style operations. * Arguments * n: output also possibly second argument * arg: first argument to operation * op: operation to perform, no-op if not a power argument */ void eval_power (mpz_t n, const mpz_t arg, char op) { #if defined (DEBUG_EVALUATOR) if ('^'==op || '!'==op || '@'==op || '#'==op || '$'==op) { gmp_fprintf (stderr, "eval power %Zd%c%Zd\n", arg, op, n); } #endif // arg must be positive for everything except power if (mpz_sgn(arg) < 0) { if ('!'==op || '#'==op) { gmp_fprintf (stderr, "\nError - negative argument [%Zd%c]\n", arg, op); exit (EXIT_FAILURE); } if ('@'==op || '$'==op) { gmp_fprintf (stderr, "\nError - negative argument [%Zd%c%Zd]\n", arg, op, n); exit (EXIT_FAILURE); } } // second arg must be positive for everything with two args if ('^'==op || '@'==op || '$'==op) { if (mpz_sgn(n) < 0) { gmp_fprintf (stderr, "\nError - negative argument [%Zd%c%Zd]\n", arg, op, n); exit (EXIT_FAILURE); } } if ('^'==op) mpz_pow_ui(n, arg, mpz_get_ui(n)); else if ('!'==op) /* simple factorial (syntax arg! example: 7! == 1*2*3*4*5*6*7) */ mpz_fac_ui(n, mpz_get_ui(arg)); else if ('@'==op) /* Multi factorial (syntax arg!n Example: 14!3 == 14*11*8*5*2 Note: 14!3 is substituted into 14@3 by the parser */ { /* gmp_printf ("Multi factorial %Zd!%Zd\n", arg, n); */ ASSERT_ALWAYS (mpz_cmp(arg, n) >= 0); // arg >= n mpz_t p, dec; mpz_init_set (p, arg); mpz_init_set (dec, n); mpz_set_ui (n, 1); while (mpz_cmp_ui(p, 1) > 0) { /* Could use factor-tree, not worth the extra code. */ mpz_mul (n, n, p); mpz_sub (p, p, dec); } mpz_clear(p); mpz_clear(dec); } else if ('#'==op) /* simple primorial (syntax arg# example: 11# == 2*3*5*7*11 */ { mpz_primorial_ui(n, mpz_get_ui(arg)); } else if ('$'==op) /* reduced primorial (syntax arg#n example: 13#5 == (5*7*11*13) */ { /* gmp_printf ("Reduced-primorial %Zd#%Zd\n", arg, n); */ ASSERT_ALWAYS (mpz_cmp(arg, n) >= 0); // arg >= n mpz_t p; mpz_init_set(p, n); mpz_set_ui (n, 1); mpz_sub_ui(p, p, 1); mpz_nextprime(p, p); for (; mpz_cmp(p, arg) <= 0; mpz_nextprime(p, p)) /* Could use factor-tree, not worth the extra code. */ mpz_mul (n, n, p); mpz_clear(p); } } /** * Evaluate "product" style operations. * Arguments * n: output also possibly second argument * arg: first argument to operation * op: operation to perform, no-op if not a power argument */ void eval_product (mpz_t n, const mpz_t arg, char op) { #if defined (DEBUG_EVALUATOR) if ('*'==op || '.'==op || '/'==op || '%'==op) { gmp_fprintf (stderr, "eval_product %Zd%c%Zd\n", arg, op, n); } #endif if ('*' == op || '.' == op) mpz_mul (n, arg, n); else if ('/' == op) { mpz_t r; mpz_init (r); mpz_tdiv_qr (n, r, arg, n); if (mpz_cmp_ui (r, 0) != 0) { fprintf (stderr, "Parsing Error: inexact division\n"); exit (EXIT_FAILURE); } mpz_clear (r); } else if ('%' == op) mpz_tdiv_r (n, arg, n); } /** * Evaluate "sum" style operations. * Arguments * n: output also possibly second argument * arg: first argument to operation * op: operation to perform, no-op if not a power argument */ void eval_sum (mpz_t n, const mpz_t arg, char op) { #if defined (DEBUG_EVALUATOR) if ('+'==op || '-'==op) { gmp_fprintf (stderr, "eval_sum %Zd%c%Zd\n", arg, op, n); } #endif if ('+' == op) mpz_add(n, arg, n); else if ('-' == op) mpz_sub(n, arg, n); } int eval_Phi (mpz_t* params, mpz_t n) { /* params[0]=exp, n=base */ int factors[200]; unsigned dwFactors=0, dw; unsigned long B; unsigned long p; mpz_t D, T, org_n; /* deal with trivial cases first */ if (mpz_cmp_ui (params[0], 0) == 0) { mpz_set_ui (n, 1); return 1; } if (mpz_cmp_ui (params[0], 0) < 0) return 0; if (mpz_cmp_ui (params[0], 1) == 0) { mpz_sub_ui (n, n, 1); return 1; } if (mpz_cmp_ui (params[0], 2) == 0) { mpz_add_ui (n, n, 1); return 1; } if (mpz_cmp_ui (n, 0) < 0) /* Convert to positive base; this is always valid when exp>=3 */ { mpz_neg (n, n); if (mpz_congruent_ui_p (params[0], 1, 2)) { mpz_mul_ui(params[0], params[0], 2); } else if (mpz_congruent_ui_p (params[0], 2, 4)) { mpz_divexact_ui(params[0], params[0], 2); } } if (mpz_cmp_ui (n, 1) == 0) { /* return value is p if params[0] is prime power p^k, or 1 otherwise */ int maxpower=mpz_sizeinbase(params[0], 2)+1; mpz_init (T); for (int power=maxpower; power>=1; --power) { if ( mpz_root (T, params[0], power) ) break; } int isPrime = mpz_probab_prime_p (T, PROBAB_PRIME_TESTS); mpz_set (n, isPrime ? T : mpOne); mpz_clear(T); return 1; } /* Ok, do the real h_primative work, since we are not one of the trivial case */ if (mpz_fits_ulong_p (params[0]) == 0) return 0; B = mpz_get_ui (params[0]); /* Obtain the factors of B */ for (p = 2; p <= B; p += 1 + (p>2)) { if (B % p == 0) { /* Add the factor one time */ factors[dwFactors++] = p; /* but be sure to totally remove it */ do { B /= p; } while (B % p == 0); } } B = mpz_get_si (params[0]); mpz_init_set (org_n, n); mpz_set_ui (n, 1); mpz_init_set_ui (D, 1); mpz_init (T); for(dw=0;(dw<(1U<0 ? mpz_addmul : mpz_submul)(output, D, l); // gmp_fprintf (stderr, "Calculated base=%Zd, exp=%Zd, C=%Zd, D=%Zd, output=%Zd\n",base,n,C,D,output); mpz_clears(orig_base,C,D,l,m,NULL); return 1; } int eval_PhiL (mpz_t *params, mpz_t n) { mpz_t aur; int err1,err2; mpz_init(aur); err1=aurif(aur,params[0],n,-1); err2=eval_Phi(params,n); // n now holds Phi(params[0],n) mpz_gcd(n,n,aur); mpz_clear(aur); return err1*err2; } int eval_PhiM (mpz_t *params, mpz_t n) { mpz_t aur; int err1,err2; mpz_init(aur); err1=aurif(aur,params[0],n,1); err2=eval_Phi(params,n); // n now holds Phi(params[0],n) mpz_gcd(n,n,aur); mpz_clear(aur); return err1*err2; } int eval_gcd (mpz_t *params, mpz_t n) { mpz_gcd(n, n, params[0]); return 1; } int eval_U (mpz_t *params, mpz_t n) /* params[0]=P, params[1]=Q */ { unsigned long N; mpz_t U1,U0,org_n,D,T; /* At each step U1 holds U(k), and U0 holds U(k-1) */ long k,l; if (mpz_cmp_si (n, 0) < 0) return 0; if (mpz_cmp_ui (n, 1) == 0) { mpz_set_ui (n, 1); return 1; } if (mpz_cmp_ui (n, 0) == 0) { mpz_set_ui (n, 0); return 1; } if (mpz_fits_ulong_p (n) == 0) return 0; N = mpz_get_ui (n); if (mpz_cmp_ui (params[0], 0) == 0) { if( N%2==0 ) { mpz_set_ui (n, 0); } else { mpz_neg (params[1], params[1]); mpz_pow_ui (n, params[1], (N-1)/2); mpz_neg (params[1], params[1]); } return 1; } mpz_init_set (org_n, n); mpz_init_set_ui (U1, 1); mpz_init_set_ui (U0, 0); mpz_init (D); mpz_init (T); mpz_mul (D, params[0], params[0]); mpz_submul_ui (D, params[1], 4); k=1; for(l=mpz_sizeinbase(org_n,2)-2;l>=0;l--) { mpz_mul (U0, U0, U0); mpz_mul (U1, U1, U1); mpz_mul (U0, U0, params[1]); mpz_sub (U0, U1, U0); // U(2k-1)=U(k)^2-QU(k-1)^2 mpz_pow_ui (T, params[1], k); mpz_mul (U1, U1, D); mpz_addmul_ui (U1, T, 2); mpz_addmul (U1, params[1], U0); // U(2k+1)=DU(k)^2+2Q^k+QU(2k-1) if (mpz_tstbit (org_n, l) ) { k=2*k+1; mpz_mul (U0,U0,params[1]); // U0 is 2k, U1 is 2k+1 mpz_add (U0,U1,U0); mpz_divexact (U0,U0,params[0]); } else { k=2*k; mpz_addmul (U1,U0,params[1]); // U0 is 2k-1, U1 is 2k mpz_divexact (U1,U1,params[0]); } /* gmp_fprintf (stderr, "%d %Zd %Zd\n",k,U0,U1); */ } mpz_set(n, U1); mpz_clear(U0); mpz_clear(U1); mpz_clear(org_n); mpz_clear(D); mpz_clear(T); return 1; } int eval_primU (mpz_t* params, mpz_t n) { int factors[200]; unsigned dwFactors=0, dw; unsigned long N; unsigned long p; mpz_t D, T; if (mpz_cmp_ui (n, 0) <= 0) return 0; if (mpz_cmp_ui (n, 1) == 0) { mpz_set_ui (n, 1); return 1; } /* Ignore the special cases where P^2=0,Q or 4Q*/ if (mpz_cmp_ui (params[0], 0) == 0) { return 0; } mpz_init(D); mpz_mul(D, params[0], params[0]); if (mpz_cmp (D, params[1]) == 0) { return 0; } mpz_submul_ui(D, params[1], 4); if (mpz_cmp_ui (D, 0) == 0) { return 0; } if (mpz_fits_ulong_p (n) == 0) return 0; N = mpz_get_ui (n); /* Obtain the factors of N */ for (p = 2; p <= N; p++) { if (N % p == 0) { /* Add the factor one time */ factors[dwFactors++] = p; /* but be sure to totally remove it */ do { N /= p; } while (N % p == 0); } } N = mpz_get_ui (n); mpz_set_ui (n, 1); mpz_set_ui (D, 1); mpz_init (T); for(dw=0;(dw<(1U<