/* Simple expression parser for GMP-ECM. Copyright 2003, 2004, 2005 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "ecm-ecm.h" #if HAVE_STRINGS_H # include /* for strncasecmp */ #endif #if 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 15!3 == 15.12.9.6.3 * * Simple Primorial: n# 11# == 2*3*5*7*11 * * Reduced Primorial: n#m 17#5 == 5.7.11.13.17 * * * * Adding (working on these at least: * * Phi(x,n) * * * * NOTE Lines ending in a \ character are "joined" * * NOTE Lines starting with #are comments * * 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 prior_n, mpz_t n,char op); static void eval_product (mpz_t prior_n, mpz_t n,char op); static void eval_sum (mpz_t prior_n, mpz_t n,char op); static int eval_Phi (mpz_t prior_n, mpz_t n, int ParamCnt); static int eval_2 (int bInFuncParams); #if 0 /* strncasecmp is a required function in configure.in */ #if defined (_MSC_VER) || defined (__MINGW32__) #define strncasecmp strnicmp #endif #endif /**************************************/ /* Main expression evalation 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); if (expr == NULL) { fprintf (stderr, "Error: not enough memory\n"); exit (EXIT_FAILURE); } /* Lines ending in '\\' are "joined" as a single longer line */ JoinLinesLoop:; c = fgetc (fd); if (c == '#') { 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)) expr[nCurSize++] = (char) c; if (nCurSize == nMaxSize) { char *cp; nMaxSize += 5000; cp = (char *) realloc (expr, nMaxSize + 1); if (!cp) { free (expr); fprintf (stderr, "Severe warning!, out of core memory reading number!\n"); exit (EXIT_FAILURE); } expr = cp; } c = fgetc (fd); } expr[nCurSize] = 0; if (!nCurSize) ret = 0; else { if (expr[nCurSize-1] == '\\') { /* remove the '\\' char, and then process the next line */ expr[--nCurSize] = 0; goto JoinLinesLoop; } 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, strlen (s) + 1); } mpz_clear(t); } 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); if (expr == NULL) { fprintf (stderr, "Error: not enough memory\n"); exit (EXIT_FAILURE); } /* Lines ending in '\\' are "joined" as a single longer line */ 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(*c)) expr[nCurSize++] = *c; if (nCurSize == nMaxSize) { char *cp; nMaxSize += 5000; cp = (char *) realloc (expr, nMaxSize + 1); if (!cp) { free(expr); fprintf(stderr, "Severe warning!, out of core memory reading number!\n"); exit (EXIT_FAILURE); } expr = cp; } ++c; } expr[nCurSize] = 0; if (!nCurSize) ret = 0; else { if (expr[nCurSize-1] == '\\') { /* remove the '\\' char, and then process the next line */ expr[--nCurSize] = 0; goto JoinLinesLoop; } 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, strlen (s) + 1); } mpz_clear(t); } free(expr); if (EndChar && *EndChar) *EndChar = c; return ret; } void eval_power (mpz_t prior_n, mpz_t n,char op) { #if defined (DEBUG_EVALUATOR) if ('#'==op || '^'==op || '!'==op || '@'==op || '$'==op) { fprintf (stderr, "eval_power "); mpz_out_str(stderr, 10, prior_n); fprintf (stderr, "%c", op); mpz_out_str(stderr, 10, n); fprintf (stderr, "\n"); } #endif if ('^'==op) mpz_pow_ui(n,prior_n,mpz_get_ui(n)); else if ('!'==op) /* simple factorial (syntax n! example: 7! == 1*2*3*4*5*6*7) */ mpz_fac_ui(n,mpz_get_ui(n)); else if ('@'==op) /* Multi factorial (syntax n!prior_n. example: 15!3 == 15*12*9*6*3) */ { long nCur; unsigned long nDecr; nCur = mpz_get_si(prior_n); nDecr = mpz_get_ui(n); mpz_set_ui(n,1); /*printf ("Multi-factorial %ld!%ld\n", nCur, nDecr);*/ while (nCur > 1) { /* This could be done much more efficiently (bunching mults using smaller "built-ins"), but I am not going to bother for now */ mpz_mul_ui(n,n,nCur); nCur -= nDecr; } } else if ('#'==op) /* simple primorial (syntax n# example: 11# == 2*3*5*7*11 */ { long nMax; double p; nMax = mpz_get_si(n); mpz_set_ui(n,1); getprime (FREE_PRIME_TABLE); /* free the prime tables, and reinitialize */ for (p = 2.0; p <= nMax; p = getprime (p)) /* This could be done much more efficiently (bunching mults using smaller "built-ins"), but I am not going to bother for now */ mpz_mul_ui(n,n,(unsigned)p); } else if ('$'==op) /* reduced primorial (syntax n#prior_n example: 13#5 == (5*7*11*13) */ { double p; long nMax; unsigned long nStart; nMax = mpz_get_si(prior_n); nStart = mpz_get_ui(n); mpz_set_ui(n,1); getprime (FREE_PRIME_TABLE); /* free the prime tables, and reinitialize */ p = getprime (nStart); /*printf ("Reduced-primorial %ld#%ld\n", nMax, nStart);*/ for (; p <= nMax; p = getprime (p)) { /* Unfortunately, the SoE within GMP-ECM does not always start correctly, so we have to skip the low end stuff by hand */ if (p >= nStart) /* This could be done much more efficiently (bunching mults using smaller "built-ins"), but I am not going to bother for now */ mpz_mul_ui(n,n,(unsigned)p); } } } void eval_product (mpz_t prior_n, mpz_t n,char op) { #if defined (DEBUG_EVALUATOR) if ('*'==op || '.'==op || '/'==op || '%'==op) { fprintf (stderr, "eval_product "); mpz_out_str(stderr, 10, prior_n); fprintf (stderr, "%c", op); mpz_out_str(stderr, 10, n); fprintf (stderr, "\n"); } #endif if ('*'==op || '.'==op) mpz_mul(n,prior_n,n); else if ('/'==op) mpz_tdiv_q(n,prior_n,n); else if ('%'==op) mpz_tdiv_r(n,prior_n,n); } void eval_sum (mpz_t prior_n, mpz_t n,char op) { #if defined (DEBUG_EVALUATOR) if ('+'==op || '-'==op) { fprintf (stderr, "eval_sum "); mpz_out_str(stderr, 10, prior_n); fprintf (stderr, "%c", op); mpz_out_str(stderr, 10, n); fprintf (stderr, "\n"); } #endif if ('+' == op) mpz_add(n,prior_n,n); else if ('-' == op) mpz_sub(n,prior_n,n); } int eval_Phi (mpz_t b, mpz_t n, int ParamCnt) { int factors[200]; unsigned dwFactors=0, dw; int B; double p; mpz_t D, T, org_n; if (ParamCnt == 0) { fprintf (stderr, "\nParsing Error - the Phi function (in ECM) requires 2 parameters\n"); return 0; } if (mpz_cmp_ui(n, 1) == 0) { /* return value is 1 if b is composite, or b if b is prime */ int isPrime = mpz_probab_prime_p (b, PROBAB_PRIME_TESTS); if (isPrime) mpz_set(n, b); else mpz_set(n, mpOne); return 1; } if (mpz_cmp_si(n, -1) == 0) { /* this is actually INVALID, but it is easier to simply */ fprintf (stderr, "\nParsing Error - Invalid parameter passed to the Phi function\n"); return 0; } /* OK parse the Phi out now */ if (mpz_cmp_ui(b, 0) == 0) { /* this is valid, but return that it is NOT */ mpz_set(n, mpOne); return 0; } if (mpz_cmp_ui(b, 1) == 0) { if (mpz_cmp_ui(n, 1) != 0) mpz_sub_ui(n, n, 1); return 1; } /* Ok, do the real h_primative work, since we are not one of the trivial case */ B = mpz_get_si(b); if (mpz_cmp_ui(b, B)) { fprintf (stderr, "\nParsing Error - Invalid parameter passed to the Phi function (first param B too high)\n"); return 0; } /* Obtain the factors of B */ getprime (FREE_PRIME_TABLE); /* free the prime tables, and reinitialize */ for (p = 2.0; p <= B; p = getprime (p)) { if (B % (int) p == 0) { /* Add the factor one time */ factors[dwFactors++] = (int) p; /* but be sure to totally remove it */ do { B /= (int) p; } while (B % (int) p == 0); } } B = mpz_get_si(b); 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<