Blame demos/pexpr.c

Packit 5c3484
/* Program for computing integer expressions using the GNU Multiple Precision
Packit 5c3484
   Arithmetic Library.
Packit 5c3484
Packit 5c3484
Copyright 1997, 1999-2002, 2005, 2008, 2012, 2015 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This program is free software; you can redistribute it and/or modify it under
Packit 5c3484
the terms of the GNU General Public License as published by the Free Software
Packit 5c3484
Foundation; either version 3 of the License, or (at your option) any later
Packit 5c3484
version.
Packit 5c3484
Packit 5c3484
This program is distributed in the hope that it will be useful, but WITHOUT ANY
Packit 5c3484
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
Packit 5c3484
PARTICULAR PURPOSE.  See the GNU General Public License for more details.
Packit 5c3484
Packit 5c3484
You should have received a copy of the GNU General Public License along with
Packit 5c3484
this program.  If not, see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* This expressions evaluator works by building an expression tree (using a
Packit 5c3484
   recursive descent parser) which is then evaluated.  The expression tree is
Packit 5c3484
   useful since we want to optimize certain expressions (like a^b % c).
Packit 5c3484
Packit 5c3484
   Usage: pexpr [options] expr ...
Packit 5c3484
   (Assuming you called the executable `pexpr' of course.)
Packit 5c3484
Packit 5c3484
   Command line options:
Packit 5c3484
Packit 5c3484
   -b        print output in binary
Packit 5c3484
   -o        print output in octal
Packit 5c3484
   -d        print output in decimal (the default)
Packit 5c3484
   -x        print output in hexadecimal
Packit 5c3484
   -b<NUM>   print output in base NUM
Packit 5c3484
   -t        print timing information
Packit 5c3484
   -html     output html
Packit 5c3484
   -wml      output wml
Packit 5c3484
   -split    split long lines each 80th digit
Packit 5c3484
*/
Packit 5c3484
Packit 5c3484
/* Define LIMIT_RESOURCE_USAGE if you want to make sure the program doesn't
Packit 5c3484
   use up extensive resources (cpu, memory).  Useful for the GMP demo on the
Packit 5c3484
   GMP web site, since we cannot load the server too much.  */
Packit 5c3484
Packit 5c3484
#include "pexpr-config.h"
Packit 5c3484
Packit 5c3484
#include <string.h>
Packit 5c3484
#include <stdio.h>
Packit 5c3484
#include <stdlib.h>
Packit 5c3484
#include <setjmp.h>
Packit 5c3484
#include <signal.h>
Packit 5c3484
#include <ctype.h>
Packit 5c3484
Packit 5c3484
#include <time.h>
Packit 5c3484
#include <sys/types.h>
Packit 5c3484
#include <sys/time.h>
Packit 5c3484
#if HAVE_SYS_RESOURCE_H
Packit 5c3484
#include <sys/resource.h>
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
Packit 5c3484
/* SunOS 4 and HPUX 9 don't define a canonical SIGSTKSZ, use a default. */
Packit 5c3484
#ifndef SIGSTKSZ
Packit 5c3484
#define SIGSTKSZ  4096
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
Packit 5c3484
#define TIME(t,func)							\
Packit 5c3484
  do { int __t0, __tmp;							\
Packit 5c3484
    __t0 = cputime ();							\
Packit 5c3484
    {func;}								\
Packit 5c3484
    __tmp = cputime () - __t0;						\
Packit 5c3484
    (t) = __tmp;							\
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
/* GMP version 1.x compatibility.  */
Packit 5c3484
#if ! (__GNU_MP_VERSION >= 2)
Packit 5c3484
typedef MP_INT __mpz_struct;
Packit 5c3484
typedef __mpz_struct mpz_t[1];
Packit 5c3484
typedef __mpz_struct *mpz_ptr;
Packit 5c3484
#define mpz_fdiv_q	mpz_div
Packit 5c3484
#define mpz_fdiv_r	mpz_mod
Packit 5c3484
#define mpz_tdiv_q_2exp	mpz_div_2exp
Packit 5c3484
#define mpz_sgn(Z) ((Z)->size < 0 ? -1 : (Z)->size > 0)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
/* GMP version 2.0 compatibility.  */
Packit 5c3484
#if ! (__GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1)
Packit 5c3484
#define mpz_swap(a,b) \
Packit 5c3484
  do { __mpz_struct __t; __t = *a; *a = *b; *b = __t;} while (0)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
jmp_buf errjmpbuf;
Packit 5c3484
Packit 5c3484
enum op_t {NOP, LIT, NEG, NOT, PLUS, MINUS, MULT, DIV, MOD, REM, INVMOD, POW,
Packit 5c3484
	   AND, IOR, XOR, SLL, SRA, POPCNT, HAMDIST, GCD, LCM, SQRT, ROOT, FAC,
Packit 5c3484
	   LOG, LOG2, FERMAT, MERSENNE, FIBONACCI, RANDOM, NEXTPRIME, BINOM,
Packit 5c3484
	   TIMING};
Packit 5c3484
Packit 5c3484
/* Type for the expression tree.  */
Packit 5c3484
struct expr
Packit 5c3484
{
Packit 5c3484
  enum op_t op;
Packit 5c3484
  union
Packit 5c3484
  {
Packit 5c3484
    struct {struct expr *lhs, *rhs;} ops;
Packit 5c3484
    mpz_t val;
Packit 5c3484
  } operands;
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
typedef struct expr *expr_t;
Packit 5c3484
Packit 5c3484
void cleanup_and_exit (int);
Packit 5c3484
Packit 5c3484
char *skipspace (char *);
Packit 5c3484
void makeexp (expr_t *, enum op_t, expr_t, expr_t);
Packit 5c3484
void free_expr (expr_t);
Packit 5c3484
char *expr (char *, expr_t *);
Packit 5c3484
char *term (char *, expr_t *);
Packit 5c3484
char *power (char *, expr_t *);
Packit 5c3484
char *factor (char *, expr_t *);
Packit 5c3484
int match (char *, char *);
Packit 5c3484
int matchp (char *, char *);
Packit 5c3484
int cputime (void);
Packit 5c3484
Packit 5c3484
void mpz_eval_expr (mpz_ptr, expr_t);
Packit 5c3484
void mpz_eval_mod_expr (mpz_ptr, expr_t, mpz_ptr);
Packit 5c3484
Packit 5c3484
char *error;
Packit 5c3484
int flag_print = 1;
Packit 5c3484
int print_timing = 0;
Packit 5c3484
int flag_html = 0;
Packit 5c3484
int flag_wml = 0;
Packit 5c3484
int flag_splitup_output = 0;
Packit 5c3484
char *newline = "";
Packit 5c3484
gmp_randstate_t rstate;
Packit 5c3484
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* cputime() returns user CPU time measured in milliseconds.  */
Packit 5c3484
#if ! HAVE_CPUTIME
Packit 5c3484
#if HAVE_GETRUSAGE
Packit 5c3484
int
Packit 5c3484
cputime (void)
Packit 5c3484
{
Packit 5c3484
  struct rusage rus;
Packit 5c3484
Packit 5c3484
  getrusage (0, &rus;;
Packit 5c3484
  return rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000;
Packit 5c3484
}
Packit 5c3484
#else
Packit 5c3484
#if HAVE_CLOCK
Packit 5c3484
int
Packit 5c3484
cputime (void)
Packit 5c3484
{
Packit 5c3484
  if (CLOCKS_PER_SEC < 100000)
Packit 5c3484
    return clock () * 1000 / CLOCKS_PER_SEC;
Packit 5c3484
  return clock () / (CLOCKS_PER_SEC / 1000);
Packit 5c3484
}
Packit 5c3484
#else
Packit 5c3484
int
Packit 5c3484
cputime (void)
Packit 5c3484
{
Packit 5c3484
  return 0;
Packit 5c3484
}
Packit 5c3484
#endif
Packit 5c3484
#endif
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
stack_downwards_helper (char *xp)
Packit 5c3484
{
Packit 5c3484
  char  y;
Packit 5c3484
  return &y < xp;
Packit 5c3484
}
Packit 5c3484
int
Packit 5c3484
stack_downwards_p (void)
Packit 5c3484
{
Packit 5c3484
  char  x;
Packit 5c3484
  return stack_downwards_helper (&x);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
setup_error_handler (void)
Packit 5c3484
{
Packit 5c3484
#if HAVE_SIGACTION
Packit 5c3484
  struct sigaction act;
Packit 5c3484
  act.sa_handler = cleanup_and_exit;
Packit 5c3484
  sigemptyset (&(act.sa_mask));
Packit 5c3484
#define SIGNAL(sig)  sigaction (sig, &act, NULL)
Packit 5c3484
#else
Packit 5c3484
  struct { int sa_flags; } act;
Packit 5c3484
#define SIGNAL(sig)  signal (sig, cleanup_and_exit)
Packit 5c3484
#endif
Packit 5c3484
  act.sa_flags = 0;
Packit 5c3484
Packit 5c3484
  /* Set up a stack for signal handling.  A typical cause of error is stack
Packit 5c3484
     overflow, and in such situation a signal can not be delivered on the
Packit 5c3484
     overflown stack.  */
Packit 5c3484
#if HAVE_SIGALTSTACK
Packit 5c3484
  {
Packit 5c3484
    /* AIX uses stack_t, MacOS uses struct sigaltstack, various other
Packit 5c3484
       systems have both. */
Packit 5c3484
#if HAVE_STACK_T
Packit 5c3484
    stack_t s;
Packit 5c3484
#else
Packit 5c3484
    struct sigaltstack s;
Packit 5c3484
#endif
Packit 5c3484
    s.ss_sp = malloc (SIGSTKSZ);
Packit 5c3484
    s.ss_size = SIGSTKSZ;
Packit 5c3484
    s.ss_flags = 0;
Packit 5c3484
    if (sigaltstack (&s, NULL) != 0)
Packit 5c3484
      perror("sigaltstack");
Packit 5c3484
    act.sa_flags = SA_ONSTACK;
Packit 5c3484
  }
Packit 5c3484
#else
Packit 5c3484
#if HAVE_SIGSTACK
Packit 5c3484
  {
Packit 5c3484
    struct sigstack s;
Packit 5c3484
    s.ss_sp = malloc (SIGSTKSZ);
Packit 5c3484
    if (stack_downwards_p ())
Packit 5c3484
      s.ss_sp += SIGSTKSZ;
Packit 5c3484
    s.ss_onstack = 0;
Packit 5c3484
    if (sigstack (&s, NULL) != 0)
Packit 5c3484
      perror("sigstack");
Packit 5c3484
    act.sa_flags = SA_ONSTACK;
Packit 5c3484
  }
Packit 5c3484
#else
Packit 5c3484
#endif
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#ifdef LIMIT_RESOURCE_USAGE
Packit 5c3484
  {
Packit 5c3484
    struct rlimit limit;
Packit 5c3484
Packit 5c3484
    limit.rlim_cur = limit.rlim_max = 0;
Packit 5c3484
    setrlimit (RLIMIT_CORE, &limit);
Packit 5c3484
Packit 5c3484
    limit.rlim_cur = 3;
Packit 5c3484
    limit.rlim_max = 4;
Packit 5c3484
    setrlimit (RLIMIT_CPU, &limit);
Packit 5c3484
Packit 5c3484
    limit.rlim_cur = limit.rlim_max = 16 * 1024 * 1024;
Packit 5c3484
    setrlimit (RLIMIT_DATA, &limit);
Packit 5c3484
Packit 5c3484
    getrlimit (RLIMIT_STACK, &limit);
Packit 5c3484
    limit.rlim_cur = 4 * 1024 * 1024;
Packit 5c3484
    setrlimit (RLIMIT_STACK, &limit);
Packit 5c3484
Packit 5c3484
    SIGNAL (SIGXCPU);
Packit 5c3484
  }
Packit 5c3484
#endif /* LIMIT_RESOURCE_USAGE */
Packit 5c3484
Packit 5c3484
  SIGNAL (SIGILL);
Packit 5c3484
  SIGNAL (SIGSEGV);
Packit 5c3484
#ifdef SIGBUS /* not in mingw */
Packit 5c3484
  SIGNAL (SIGBUS);
Packit 5c3484
#endif
Packit 5c3484
  SIGNAL (SIGFPE);
Packit 5c3484
  SIGNAL (SIGABRT);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
main (int argc, char **argv)
Packit 5c3484
{
Packit 5c3484
  struct expr *e;
Packit 5c3484
  int i;
Packit 5c3484
  mpz_t r;
Packit 5c3484
  int errcode = 0;
Packit 5c3484
  char *str;
Packit 5c3484
  int base = 10;
Packit 5c3484
Packit 5c3484
  setup_error_handler ();
Packit 5c3484
Packit 5c3484
  gmp_randinit (rstate, GMP_RAND_ALG_LC, 128);
Packit 5c3484
Packit 5c3484
  {
Packit 5c3484
#if HAVE_GETTIMEOFDAY
Packit 5c3484
    struct timeval tv;
Packit 5c3484
    gettimeofday (&tv, NULL);
Packit 5c3484
    gmp_randseed_ui (rstate, tv.tv_sec + tv.tv_usec);
Packit 5c3484
#else
Packit 5c3484
    time_t t;
Packit 5c3484
    time (&t);
Packit 5c3484
    gmp_randseed_ui (rstate, t);
Packit 5c3484
#endif
Packit 5c3484
  }
Packit 5c3484
Packit 5c3484
  mpz_init (r);
Packit 5c3484
Packit 5c3484
  while (argc > 1 && argv[1][0] == '-')
Packit 5c3484
    {
Packit 5c3484
      char *arg = argv[1];
Packit 5c3484
Packit 5c3484
      if (arg[1] >= '0' && arg[1] <= '9')
Packit 5c3484
	break;
Packit 5c3484
Packit 5c3484
      if (arg[1] == 't')
Packit 5c3484
	print_timing = 1;
Packit 5c3484
      else if (arg[1] == 'b' && arg[2] >= '0' && arg[2] <= '9')
Packit 5c3484
	{
Packit 5c3484
	  base = atoi (arg + 2);
Packit 5c3484
	  if (base < 2 || base > 62)
Packit 5c3484
	    {
Packit 5c3484
	      fprintf (stderr, "error: invalid output base\n");
Packit 5c3484
	      exit (-1);
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
      else if (arg[1] == 'b' && arg[2] == 0)
Packit 5c3484
	base = 2;
Packit 5c3484
      else if (arg[1] == 'x' && arg[2] == 0)
Packit 5c3484
	base = 16;
Packit 5c3484
      else if (arg[1] == 'X' && arg[2] == 0)
Packit 5c3484
	base = -16;
Packit 5c3484
      else if (arg[1] == 'o' && arg[2] == 0)
Packit 5c3484
	base = 8;
Packit 5c3484
      else if (arg[1] == 'd' && arg[2] == 0)
Packit 5c3484
	base = 10;
Packit 5c3484
      else if (arg[1] == 'v' && arg[2] == 0)
Packit 5c3484
	{
Packit 5c3484
	  printf ("pexpr linked to gmp %s\n", __gmp_version);
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (arg, "-html") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_html = 1;
Packit 5c3484
	  newline = "
";
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (arg, "-wml") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_wml = 1;
Packit 5c3484
	  newline = "
";
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (arg, "-split") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_splitup_output = 1;
Packit 5c3484
	}
Packit 5c3484
      else if (strcmp (arg, "-noprint") == 0)
Packit 5c3484
	{
Packit 5c3484
	  flag_print = 0;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  fprintf (stderr, "error: unknown option `%s'\n", arg);
Packit 5c3484
	  exit (-1);
Packit 5c3484
	}
Packit 5c3484
      argv++;
Packit 5c3484
      argc--;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  for (i = 1; i < argc; i++)
Packit 5c3484
    {
Packit 5c3484
      int s;
Packit 5c3484
      int jmpval;
Packit 5c3484
Packit 5c3484
      /* Set up error handler for parsing expression.  */
Packit 5c3484
      jmpval = setjmp (errjmpbuf);
Packit 5c3484
      if (jmpval != 0)
Packit 5c3484
	{
Packit 5c3484
	  fprintf (stderr, "error: %s%s\n", error, newline);
Packit 5c3484
	  fprintf (stderr, "       %s%s\n", argv[i], newline);
Packit 5c3484
	  if (! flag_html)
Packit 5c3484
	    {
Packit 5c3484
	      /* ??? Dunno how to align expression position with arrow in
Packit 5c3484
		 HTML ??? */
Packit 5c3484
	      fprintf (stderr, "       ");
Packit 5c3484
	      for (s = jmpval - (long) argv[i]; --s >= 0; )
Packit 5c3484
		putc (' ', stderr);
Packit 5c3484
	      fprintf (stderr, "^\n");
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  errcode |= 1;
Packit 5c3484
	  continue;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      str = expr (argv[i], &e);
Packit 5c3484
Packit 5c3484
      if (str[0] != 0)
Packit 5c3484
	{
Packit 5c3484
	  fprintf (stderr,
Packit 5c3484
		   "error: garbage where end of expression expected%s\n",
Packit 5c3484
		   newline);
Packit 5c3484
	  fprintf (stderr, "       %s%s\n", argv[i], newline);
Packit 5c3484
	  if (! flag_html)
Packit 5c3484
	    {
Packit 5c3484
	      /* ??? Dunno how to align expression position with arrow in
Packit 5c3484
		 HTML ??? */
Packit 5c3484
	      fprintf (stderr, "        ");
Packit 5c3484
	      for (s = str - argv[i]; --s; )
Packit 5c3484
		putc (' ', stderr);
Packit 5c3484
	      fprintf (stderr, "^\n");
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  errcode |= 1;
Packit 5c3484
	  free_expr (e);
Packit 5c3484
	  continue;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* Set up error handler for evaluating expression.  */
Packit 5c3484
      if (setjmp (errjmpbuf))
Packit 5c3484
	{
Packit 5c3484
	  fprintf (stderr, "error: %s%s\n", error, newline);
Packit 5c3484
	  fprintf (stderr, "       %s%s\n", argv[i], newline);
Packit 5c3484
	  if (! flag_html)
Packit 5c3484
	    {
Packit 5c3484
	      /* ??? Dunno how to align expression position with arrow in
Packit 5c3484
		 HTML ??? */
Packit 5c3484
	      fprintf (stderr, "       ");
Packit 5c3484
	      for (s = str - argv[i]; --s >= 0; )
Packit 5c3484
		putc (' ', stderr);
Packit 5c3484
	      fprintf (stderr, "^\n");
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  errcode |= 2;
Packit 5c3484
	  continue;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      if (print_timing)
Packit 5c3484
	{
Packit 5c3484
	  int t;
Packit 5c3484
	  TIME (t, mpz_eval_expr (r, e));
Packit 5c3484
	  printf ("computation took %d ms%s\n", t, newline);
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	mpz_eval_expr (r, e);
Packit 5c3484
Packit 5c3484
      if (flag_print)
Packit 5c3484
	{
Packit 5c3484
	  size_t out_len;
Packit 5c3484
	  char *tmp, *s;
Packit 5c3484
Packit 5c3484
	  out_len = mpz_sizeinbase (r, base >= 0 ? base : -base) + 2;
Packit 5c3484
#ifdef LIMIT_RESOURCE_USAGE
Packit 5c3484
	  if (out_len > 100000)
Packit 5c3484
	    {
Packit 5c3484
	      printf ("result is about %ld digits, not printing it%s\n",
Packit 5c3484
		      (long) out_len - 3, newline);
Packit 5c3484
	      exit (-2);
Packit 5c3484
	    }
Packit 5c3484
#endif
Packit 5c3484
	  tmp = malloc (out_len);
Packit 5c3484
Packit 5c3484
	  if (print_timing)
Packit 5c3484
	    {
Packit 5c3484
	      int t;
Packit 5c3484
	      printf ("output conversion ");
Packit 5c3484
	      TIME (t, mpz_get_str (tmp, base, r));
Packit 5c3484
	      printf ("took %d ms%s\n", t, newline);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    mpz_get_str (tmp, base, r);
Packit 5c3484
Packit 5c3484
	  out_len = strlen (tmp);
Packit 5c3484
	  if (flag_splitup_output)
Packit 5c3484
	    {
Packit 5c3484
	      for (s = tmp; out_len > 80; s += 80)
Packit 5c3484
		{
Packit 5c3484
		  fwrite (s, 1, 80, stdout);
Packit 5c3484
		  printf ("%s\n", newline);
Packit 5c3484
		  out_len -= 80;
Packit 5c3484
		}
Packit 5c3484
Packit 5c3484
	      fwrite (s, 1, out_len, stdout);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    {
Packit 5c3484
	      fwrite (tmp, 1, out_len, stdout);
Packit 5c3484
	    }
Packit 5c3484
Packit 5c3484
	  free (tmp);
Packit 5c3484
	  printf ("%s\n", newline);
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  printf ("result is approximately %ld digits%s\n",
Packit 5c3484
		  (long) mpz_sizeinbase (r, base >= 0 ? base : -base),
Packit 5c3484
		  newline);
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      free_expr (e);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  mpz_clear (r);
Packit 5c3484
Packit 5c3484
  exit (errcode);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
expr (char *str, expr_t *e)
Packit 5c3484
{
Packit 5c3484
  expr_t e2;
Packit 5c3484
Packit 5c3484
  str = skipspace (str);
Packit 5c3484
  if (str[0] == '+')
Packit 5c3484
    {
Packit 5c3484
      str = term (str + 1, e);
Packit 5c3484
    }
Packit 5c3484
  else if (str[0] == '-')
Packit 5c3484
    {
Packit 5c3484
      str = term (str + 1, e);
Packit 5c3484
      makeexp (e, NEG, *e, NULL);
Packit 5c3484
    }
Packit 5c3484
  else if (str[0] == '~')
Packit 5c3484
    {
Packit 5c3484
      str = term (str + 1, e);
Packit 5c3484
      makeexp (e, NOT, *e, NULL);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      str = term (str, e);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      str = skipspace (str);
Packit 5c3484
      switch (str[0])
Packit 5c3484
	{
Packit 5c3484
	case 'p':
Packit 5c3484
	  if (match ("plus", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = term (str + 4, &e2;;
Packit 5c3484
	      makeexp (e, PLUS, *e, e2);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    return str;
Packit 5c3484
	  break;
Packit 5c3484
	case 'm':
Packit 5c3484
	  if (match ("minus", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = term (str + 5, &e2;;
Packit 5c3484
	      makeexp (e, MINUS, *e, e2);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    return str;
Packit 5c3484
	  break;
Packit 5c3484
	case '+':
Packit 5c3484
	  str = term (str + 1, &e2;;
Packit 5c3484
	  makeexp (e, PLUS, *e, e2);
Packit 5c3484
	  break;
Packit 5c3484
	case '-':
Packit 5c3484
	  str = term (str + 1, &e2;;
Packit 5c3484
	  makeexp (e, MINUS, *e, e2);
Packit 5c3484
	  break;
Packit 5c3484
	default:
Packit 5c3484
	  return str;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
term (char *str, expr_t *e)
Packit 5c3484
{
Packit 5c3484
  expr_t e2;
Packit 5c3484
Packit 5c3484
  str = power (str, e);
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      str = skipspace (str);
Packit 5c3484
      switch (str[0])
Packit 5c3484
	{
Packit 5c3484
	case 'm':
Packit 5c3484
	  if (match ("mul", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 3, &e2;;
Packit 5c3484
	      makeexp (e, MULT, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  if (match ("mod", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 3, &e2;;
Packit 5c3484
	      makeexp (e, MOD, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  return str;
Packit 5c3484
	case 'd':
Packit 5c3484
	  if (match ("div", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 3, &e2;;
Packit 5c3484
	      makeexp (e, DIV, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  return str;
Packit 5c3484
	case 'r':
Packit 5c3484
	  if (match ("rem", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 3, &e2;;
Packit 5c3484
	      makeexp (e, REM, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  return str;
Packit 5c3484
	case 'i':
Packit 5c3484
	  if (match ("invmod", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 6, &e2;;
Packit 5c3484
	      makeexp (e, REM, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  return str;
Packit 5c3484
	case 't':
Packit 5c3484
	  if (match ("times", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 5, &e2;;
Packit 5c3484
	      makeexp (e, MULT, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  if (match ("thru", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 4, &e2;;
Packit 5c3484
	      makeexp (e, DIV, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  if (match ("through", str))
Packit 5c3484
	    {
Packit 5c3484
	      str = power (str + 7, &e2;;
Packit 5c3484
	      makeexp (e, DIV, *e, e2);
Packit 5c3484
	      break;
Packit 5c3484
	    }
Packit 5c3484
	  return str;
Packit 5c3484
	case '*':
Packit 5c3484
	  str = power (str + 1, &e2;;
Packit 5c3484
	  makeexp (e, MULT, *e, e2);
Packit 5c3484
	  break;
Packit 5c3484
	case '/':
Packit 5c3484
	  str = power (str + 1, &e2;;
Packit 5c3484
	  makeexp (e, DIV, *e, e2);
Packit 5c3484
	  break;
Packit 5c3484
	case '%':
Packit 5c3484
	  str = power (str + 1, &e2;;
Packit 5c3484
	  makeexp (e, MOD, *e, e2);
Packit 5c3484
	  break;
Packit 5c3484
	default:
Packit 5c3484
	  return str;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
power (char *str, expr_t *e)
Packit 5c3484
{
Packit 5c3484
  expr_t e2;
Packit 5c3484
Packit 5c3484
  str = factor (str, e);
Packit 5c3484
  while (str[0] == '!')
Packit 5c3484
    {
Packit 5c3484
      str++;
Packit 5c3484
      makeexp (e, FAC, *e, NULL);
Packit 5c3484
    }
Packit 5c3484
  str = skipspace (str);
Packit 5c3484
  if (str[0] == '^')
Packit 5c3484
    {
Packit 5c3484
      str = power (str + 1, &e2;;
Packit 5c3484
      makeexp (e, POW, *e, e2);
Packit 5c3484
    }
Packit 5c3484
  return str;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
match (char *s, char *str)
Packit 5c3484
{
Packit 5c3484
  char *ostr = str;
Packit 5c3484
  int i;
Packit 5c3484
Packit 5c3484
  for (i = 0; s[i] != 0; i++)
Packit 5c3484
    {
Packit 5c3484
      if (str[i] != s[i])
Packit 5c3484
	return 0;
Packit 5c3484
    }
Packit 5c3484
  str = skipspace (str + i);
Packit 5c3484
  return str - ostr;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
matchp (char *s, char *str)
Packit 5c3484
{
Packit 5c3484
  char *ostr = str;
Packit 5c3484
  int i;
Packit 5c3484
Packit 5c3484
  for (i = 0; s[i] != 0; i++)
Packit 5c3484
    {
Packit 5c3484
      if (str[i] != s[i])
Packit 5c3484
	return 0;
Packit 5c3484
    }
Packit 5c3484
  str = skipspace (str + i);
Packit 5c3484
  if (str[0] == '(')
Packit 5c3484
    return str - ostr + 1;
Packit 5c3484
  return 0;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
struct functions
Packit 5c3484
{
Packit 5c3484
  char *spelling;
Packit 5c3484
  enum op_t op;
Packit 5c3484
  int arity; /* 1 or 2 means real arity; 0 means arbitrary.  */
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
struct functions fns[] =
Packit 5c3484
{
Packit 5c3484
  {"sqrt", SQRT, 1},
Packit 5c3484
#if __GNU_MP_VERSION >= 2
Packit 5c3484
  {"root", ROOT, 2},
Packit 5c3484
  {"popc", POPCNT, 1},
Packit 5c3484
  {"hamdist", HAMDIST, 2},
Packit 5c3484
#endif
Packit 5c3484
  {"gcd", GCD, 0},
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
  {"lcm", LCM, 0},
Packit 5c3484
#endif
Packit 5c3484
  {"and", AND, 0},
Packit 5c3484
  {"ior", IOR, 0},
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
  {"xor", XOR, 0},
Packit 5c3484
#endif
Packit 5c3484
  {"plus", PLUS, 0},
Packit 5c3484
  {"pow", POW, 2},
Packit 5c3484
  {"minus", MINUS, 2},
Packit 5c3484
  {"mul", MULT, 0},
Packit 5c3484
  {"div", DIV, 2},
Packit 5c3484
  {"mod", MOD, 2},
Packit 5c3484
  {"rem", REM, 2},
Packit 5c3484
#if __GNU_MP_VERSION >= 2
Packit 5c3484
  {"invmod", INVMOD, 2},
Packit 5c3484
#endif
Packit 5c3484
  {"log", LOG, 2},
Packit 5c3484
  {"log2", LOG2, 1},
Packit 5c3484
  {"F", FERMAT, 1},
Packit 5c3484
  {"M", MERSENNE, 1},
Packit 5c3484
  {"fib", FIBONACCI, 1},
Packit 5c3484
  {"Fib", FIBONACCI, 1},
Packit 5c3484
  {"random", RANDOM, 1},
Packit 5c3484
  {"nextprime", NEXTPRIME, 1},
Packit 5c3484
  {"binom", BINOM, 2},
Packit 5c3484
  {"binomial", BINOM, 2},
Packit 5c3484
  {"fac", FAC, 1},
Packit 5c3484
  {"fact", FAC, 1},
Packit 5c3484
  {"factorial", FAC, 1},
Packit 5c3484
  {"time", TIMING, 1},
Packit 5c3484
  {"", NOP, 0}
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
factor (char *str, expr_t *e)
Packit 5c3484
{
Packit 5c3484
  expr_t e1, e2;
Packit 5c3484
Packit 5c3484
  str = skipspace (str);
Packit 5c3484
Packit 5c3484
  if (isalpha (str[0]))
Packit 5c3484
    {
Packit 5c3484
      int i;
Packit 5c3484
      int cnt;
Packit 5c3484
Packit 5c3484
      for (i = 0; fns[i].op != NOP; i++)
Packit 5c3484
	{
Packit 5c3484
	  if (fns[i].arity == 1)
Packit 5c3484
	    {
Packit 5c3484
	      cnt = matchp (fns[i].spelling, str);
Packit 5c3484
	      if (cnt != 0)
Packit 5c3484
		{
Packit 5c3484
		  str = expr (str + cnt, &e1;;
Packit 5c3484
		  str = skipspace (str);
Packit 5c3484
		  if (str[0] != ')')
Packit 5c3484
		    {
Packit 5c3484
		      error = "expected `)'";
Packit 5c3484
		      longjmp (errjmpbuf, (int) (long) str);
Packit 5c3484
		    }
Packit 5c3484
		  makeexp (e, fns[i].op, e1, NULL);
Packit 5c3484
		  return str + 1;
Packit 5c3484
		}
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      for (i = 0; fns[i].op != NOP; i++)
Packit 5c3484
	{
Packit 5c3484
	  if (fns[i].arity != 1)
Packit 5c3484
	    {
Packit 5c3484
	      cnt = matchp (fns[i].spelling, str);
Packit 5c3484
	      if (cnt != 0)
Packit 5c3484
		{
Packit 5c3484
		  str = expr (str + cnt, &e1;;
Packit 5c3484
		  str = skipspace (str);
Packit 5c3484
Packit 5c3484
		  if (str[0] != ',')
Packit 5c3484
		    {
Packit 5c3484
		      error = "expected `,' and another operand";
Packit 5c3484
		      longjmp (errjmpbuf, (int) (long) str);
Packit 5c3484
		    }
Packit 5c3484
Packit 5c3484
		  str = skipspace (str + 1);
Packit 5c3484
		  str = expr (str, &e2;;
Packit 5c3484
		  str = skipspace (str);
Packit 5c3484
Packit 5c3484
		  if (fns[i].arity == 0)
Packit 5c3484
		    {
Packit 5c3484
		      while (str[0] == ',')
Packit 5c3484
			{
Packit 5c3484
			  makeexp (&e1, fns[i].op, e1, e2);
Packit 5c3484
			  str = skipspace (str + 1);
Packit 5c3484
			  str = expr (str, &e2;;
Packit 5c3484
			  str = skipspace (str);
Packit 5c3484
			}
Packit 5c3484
		    }
Packit 5c3484
Packit 5c3484
		  if (str[0] != ')')
Packit 5c3484
		    {
Packit 5c3484
		      error = "expected `)'";
Packit 5c3484
		      longjmp (errjmpbuf, (int) (long) str);
Packit 5c3484
		    }
Packit 5c3484
Packit 5c3484
		  makeexp (e, fns[i].op, e1, e2);
Packit 5c3484
		  return str + 1;
Packit 5c3484
		}
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (str[0] == '(')
Packit 5c3484
    {
Packit 5c3484
      str = expr (str + 1, e);
Packit 5c3484
      str = skipspace (str);
Packit 5c3484
      if (str[0] != ')')
Packit 5c3484
	{
Packit 5c3484
	  error = "expected `)'";
Packit 5c3484
	  longjmp (errjmpbuf, (int) (long) str);
Packit 5c3484
	}
Packit 5c3484
      str++;
Packit 5c3484
    }
Packit 5c3484
  else if (str[0] >= '0' && str[0] <= '9')
Packit 5c3484
    {
Packit 5c3484
      expr_t res;
Packit 5c3484
      char *s, *sc;
Packit 5c3484
Packit 5c3484
      res = malloc (sizeof (struct expr));
Packit 5c3484
      res -> op = LIT;
Packit 5c3484
      mpz_init (res->operands.val);
Packit 5c3484
Packit 5c3484
      s = str;
Packit 5c3484
      while (isalnum (str[0]))
Packit 5c3484
	str++;
Packit 5c3484
      sc = malloc (str - s + 1);
Packit 5c3484
      memcpy (sc, s, str - s);
Packit 5c3484
      sc[str - s] = 0;
Packit 5c3484
Packit 5c3484
      mpz_set_str (res->operands.val, sc, 0);
Packit 5c3484
      *e = res;
Packit 5c3484
      free (sc);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      error = "operand expected";
Packit 5c3484
      longjmp (errjmpbuf, (int) (long) str);
Packit 5c3484
    }
Packit 5c3484
  return str;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
char *
Packit 5c3484
skipspace (char *str)
Packit 5c3484
{
Packit 5c3484
  while (str[0] == ' ')
Packit 5c3484
    str++;
Packit 5c3484
  return str;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Make a new expression with operation OP and right hand side
Packit 5c3484
   RHS and left hand side lhs.  Put the result in R.  */
Packit 5c3484
void
Packit 5c3484
makeexp (expr_t *r, enum op_t op, expr_t lhs, expr_t rhs)
Packit 5c3484
{
Packit 5c3484
  expr_t res;
Packit 5c3484
  res = malloc (sizeof (struct expr));
Packit 5c3484
  res -> op = op;
Packit 5c3484
  res -> operands.ops.lhs = lhs;
Packit 5c3484
  res -> operands.ops.rhs = rhs;
Packit 5c3484
  *r = res;
Packit 5c3484
  return;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Free the memory used by expression E.  */
Packit 5c3484
void
Packit 5c3484
free_expr (expr_t e)
Packit 5c3484
{
Packit 5c3484
  if (e->op != LIT)
Packit 5c3484
    {
Packit 5c3484
      free_expr (e->operands.ops.lhs);
Packit 5c3484
      if (e->operands.ops.rhs != NULL)
Packit 5c3484
	free_expr (e->operands.ops.rhs);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      mpz_clear (e->operands.val);
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Evaluate the expression E and put the result in R.  */
Packit 5c3484
void
Packit 5c3484
mpz_eval_expr (mpz_ptr r, expr_t e)
Packit 5c3484
{
Packit 5c3484
  mpz_t lhs, rhs;
Packit 5c3484
Packit 5c3484
  switch (e->op)
Packit 5c3484
    {
Packit 5c3484
    case LIT:
Packit 5c3484
      mpz_set (r, e->operands.val);
Packit 5c3484
      return;
Packit 5c3484
    case PLUS:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_add (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case MINUS:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_sub (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case MULT:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_mul (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case DIV:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_fdiv_q (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case MOD:
Packit 5c3484
      mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_abs (rhs, rhs);
Packit 5c3484
      mpz_eval_mod_expr (r, e->operands.ops.lhs, rhs);
Packit 5c3484
      mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case REM:
Packit 5c3484
      /* Check if lhs operand is POW expression and optimize for that case.  */
Packit 5c3484
      if (e->operands.ops.lhs->op == POW)
Packit 5c3484
	{
Packit 5c3484
	  mpz_t powlhs, powrhs;
Packit 5c3484
	  mpz_init (powlhs);
Packit 5c3484
	  mpz_init (powrhs);
Packit 5c3484
	  mpz_init (rhs);
Packit 5c3484
	  mpz_eval_expr (powlhs, e->operands.ops.lhs->operands.ops.lhs);
Packit 5c3484
	  mpz_eval_expr (powrhs, e->operands.ops.lhs->operands.ops.rhs);
Packit 5c3484
	  mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
	  mpz_powm (r, powlhs, powrhs, rhs);
Packit 5c3484
	  if (mpz_cmp_si (rhs, 0L) < 0)
Packit 5c3484
	    mpz_neg (r, r);
Packit 5c3484
	  mpz_clear (powlhs);
Packit 5c3484
	  mpz_clear (powrhs);
Packit 5c3484
	  mpz_clear (rhs);
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_fdiv_r (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#if __GNU_MP_VERSION >= 2
Packit 5c3484
    case INVMOD:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_invert (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#endif
Packit 5c3484
    case POW:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      if (mpz_cmpabs_ui (lhs, 1) <= 0)
Packit 5c3484
	{
Packit 5c3484
	  /* For 0^rhs and 1^rhs, we just need to verify that
Packit 5c3484
	     rhs is well-defined.  For (-1)^rhs we need to
Packit 5c3484
	     determine (rhs mod 2).  For simplicity, compute
Packit 5c3484
	     (rhs mod 2) for all three cases.  */
Packit 5c3484
	  expr_t two, et;
Packit 5c3484
	  two = malloc (sizeof (struct expr));
Packit 5c3484
	  two -> op = LIT;
Packit 5c3484
	  mpz_init_set_ui (two->operands.val, 2L);
Packit 5c3484
	  makeexp (&et, MOD, e->operands.ops.rhs, two);
Packit 5c3484
	  e->operands.ops.rhs = et;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      if (mpz_cmp_si (rhs, 0L) == 0)
Packit 5c3484
	/* x^0 is 1 */
Packit 5c3484
	mpz_set_ui (r, 1L);
Packit 5c3484
      else if (mpz_cmp_si (lhs, 0L) == 0)
Packit 5c3484
	/* 0^y (where y != 0) is 0 */
Packit 5c3484
	mpz_set_ui (r, 0L);
Packit 5c3484
      else if (mpz_cmp_ui (lhs, 1L) == 0)
Packit 5c3484
	/* 1^y is 1 */
Packit 5c3484
	mpz_set_ui (r, 1L);
Packit 5c3484
      else if (mpz_cmp_si (lhs, -1L) == 0)
Packit 5c3484
	/* (-1)^y just depends on whether y is even or odd */
Packit 5c3484
	mpz_set_si (r, (mpz_get_ui (rhs) & 1) ? -1L : 1L);
Packit 5c3484
      else if (mpz_cmp_si (rhs, 0L) < 0)
Packit 5c3484
	/* x^(-n) is 0 */
Packit 5c3484
	mpz_set_ui (r, 0L);
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  unsigned long int cnt;
Packit 5c3484
	  unsigned long int y;
Packit 5c3484
	  /* error if exponent does not fit into an unsigned long int.  */
Packit 5c3484
	  if (mpz_cmp_ui (rhs, ~(unsigned long int) 0) > 0)
Packit 5c3484
	    goto pow_err;
Packit 5c3484
Packit 5c3484
	  y = mpz_get_ui (rhs);
Packit 5c3484
	  /* x^y == (x/(2^c))^y * 2^(c*y) */
Packit 5c3484
#if __GNU_MP_VERSION >= 2
Packit 5c3484
	  cnt = mpz_scan1 (lhs, 0);
Packit 5c3484
#else
Packit 5c3484
	  cnt = 0;
Packit 5c3484
#endif
Packit 5c3484
	  if (cnt != 0)
Packit 5c3484
	    {
Packit 5c3484
	      if (y * cnt / cnt != y)
Packit 5c3484
		goto pow_err;
Packit 5c3484
	      mpz_tdiv_q_2exp (lhs, lhs, cnt);
Packit 5c3484
	      mpz_pow_ui (r, lhs, y);
Packit 5c3484
	      mpz_mul_2exp (r, r, y * cnt);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    mpz_pow_ui (r, lhs, y);
Packit 5c3484
	}
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    pow_err:
Packit 5c3484
      error = "result of `pow' operator too large";
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      longjmp (errjmpbuf, 1);
Packit 5c3484
    case GCD:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_gcd (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
    case LCM:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_lcm (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#endif
Packit 5c3484
    case AND:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_and (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case IOR:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_ior (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
    case XOR:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      mpz_xor (r, lhs, rhs);
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#endif
Packit 5c3484
    case NEG:
Packit 5c3484
      mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
      mpz_neg (r, r);
Packit 5c3484
      return;
Packit 5c3484
    case NOT:
Packit 5c3484
      mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
      mpz_com (r, r);
Packit 5c3484
      return;
Packit 5c3484
    case SQRT:
Packit 5c3484
      mpz_init (lhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      if (mpz_sgn (lhs) < 0)
Packit 5c3484
	{
Packit 5c3484
	  error = "cannot take square root of negative numbers";
Packit 5c3484
	  mpz_clear (lhs);
Packit 5c3484
	  longjmp (errjmpbuf, 1);
Packit 5c3484
	}
Packit 5c3484
      mpz_sqrt (r, lhs);
Packit 5c3484
      return;
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
    case ROOT:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      if (mpz_sgn (rhs) <= 0)
Packit 5c3484
	{
Packit 5c3484
	  error = "cannot take non-positive root orders";
Packit 5c3484
	  mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	  longjmp (errjmpbuf, 1);
Packit 5c3484
	}
Packit 5c3484
      if (mpz_sgn (lhs) < 0 && (mpz_get_ui (rhs) & 1) == 0)
Packit 5c3484
	{
Packit 5c3484
	  error = "cannot take even root orders of negative numbers";
Packit 5c3484
	  mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	  longjmp (errjmpbuf, 1);
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      {
Packit 5c3484
	unsigned long int nth = mpz_get_ui (rhs);
Packit 5c3484
	if (mpz_cmp_ui (rhs, ~(unsigned long int) 0) > 0)
Packit 5c3484
	  {
Packit 5c3484
	    /* If we are asked to take an awfully large root order, cheat and
Packit 5c3484
	       ask for the largest order we can pass to mpz_root.  This saves
Packit 5c3484
	       some error prone special cases.  */
Packit 5c3484
	    nth = ~(unsigned long int) 0;
Packit 5c3484
	  }
Packit 5c3484
	mpz_root (r, lhs, nth);
Packit 5c3484
      }
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
#endif
Packit 5c3484
    case FAC:
Packit 5c3484
      mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
      if (mpz_size (r) > 1)
Packit 5c3484
	{
Packit 5c3484
	  error = "result of `!' operator too large";
Packit 5c3484
	  longjmp (errjmpbuf, 1);
Packit 5c3484
	}
Packit 5c3484
      mpz_fac_ui (r, mpz_get_ui (r));
Packit 5c3484
      return;
Packit 5c3484
#if __GNU_MP_VERSION >= 2
Packit 5c3484
    case POPCNT:
Packit 5c3484
      mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
      { long int cnt;
Packit 5c3484
	cnt = mpz_popcount (r);
Packit 5c3484
	mpz_set_si (r, cnt);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case HAMDIST:
Packit 5c3484
      { long int cnt;
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
	mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
	cnt = mpz_hamdist (lhs, rhs);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	mpz_set_si (r, cnt);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
#endif
Packit 5c3484
    case LOG2:
Packit 5c3484
      mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
      { unsigned long int cnt;
Packit 5c3484
	if (mpz_sgn (r) <= 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "logarithm of non-positive number";
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	cnt = mpz_sizeinbase (r, 2);
Packit 5c3484
	mpz_set_ui (r, cnt - 1);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case LOG:
Packit 5c3484
      { unsigned long int cnt;
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
	mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
	if (mpz_sgn (lhs) <= 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "logarithm of non-positive number";
Packit 5c3484
	    mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	if (mpz_cmp_ui (rhs, 256) >= 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "logarithm base too large";
Packit 5c3484
	    mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	cnt = mpz_sizeinbase (lhs, mpz_get_ui (rhs));
Packit 5c3484
	mpz_set_ui (r, cnt - 1);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case FERMAT:
Packit 5c3484
      {
Packit 5c3484
	unsigned long int t;
Packit 5c3484
	mpz_init (lhs);
Packit 5c3484
	mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
	t = (unsigned long int) 1 << mpz_get_ui (lhs);
Packit 5c3484
	if (mpz_cmp_ui (lhs, ~(unsigned long int) 0) > 0 || t == 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "too large Mersenne number index";
Packit 5c3484
	    mpz_clear (lhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	mpz_set_ui (r, 1);
Packit 5c3484
	mpz_mul_2exp (r, r, t);
Packit 5c3484
	mpz_add_ui (r, r, 1);
Packit 5c3484
	mpz_clear (lhs);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case MERSENNE:
Packit 5c3484
      mpz_init (lhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      if (mpz_cmp_ui (lhs, ~(unsigned long int) 0) > 0)
Packit 5c3484
	{
Packit 5c3484
	  error = "too large Mersenne number index";
Packit 5c3484
	  mpz_clear (lhs);
Packit 5c3484
	  longjmp (errjmpbuf, 1);
Packit 5c3484
	}
Packit 5c3484
      mpz_set_ui (r, 1);
Packit 5c3484
      mpz_mul_2exp (r, r, mpz_get_ui (lhs));
Packit 5c3484
      mpz_sub_ui (r, r, 1);
Packit 5c3484
      mpz_clear (lhs);
Packit 5c3484
      return;
Packit 5c3484
    case FIBONACCI:
Packit 5c3484
      { mpz_t t;
Packit 5c3484
	unsigned long int n, i;
Packit 5c3484
	mpz_init (lhs);
Packit 5c3484
	mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
	if (mpz_sgn (lhs) <= 0 || mpz_cmp_si (lhs, 1000000000) > 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "Fibonacci index out of range";
Packit 5c3484
	    mpz_clear (lhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	n = mpz_get_ui (lhs);
Packit 5c3484
	mpz_clear (lhs);
Packit 5c3484
Packit 5c3484
#if __GNU_MP_VERSION > 2 || __GNU_MP_VERSION_MINOR >= 1
Packit 5c3484
	mpz_fib_ui (r, n);
Packit 5c3484
#else
Packit 5c3484
	mpz_init_set_ui (t, 1);
Packit 5c3484
	mpz_set_ui (r, 1);
Packit 5c3484
Packit 5c3484
	if (n <= 2)
Packit 5c3484
	  mpz_set_ui (r, 1);
Packit 5c3484
	else
Packit 5c3484
	  {
Packit 5c3484
	    for (i = 3; i <= n; i++)
Packit 5c3484
	      {
Packit 5c3484
		mpz_add (t, t, r);
Packit 5c3484
		mpz_swap (t, r);
Packit 5c3484
	      }
Packit 5c3484
	  }
Packit 5c3484
	mpz_clear (t);
Packit 5c3484
#endif
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case RANDOM:
Packit 5c3484
      {
Packit 5c3484
	unsigned long int n;
Packit 5c3484
	mpz_init (lhs);
Packit 5c3484
	mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
	if (mpz_sgn (lhs) <= 0 || mpz_cmp_si (lhs, 1000000000) > 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "random number size out of range";
Packit 5c3484
	    mpz_clear (lhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	n = mpz_get_ui (lhs);
Packit 5c3484
	mpz_clear (lhs);
Packit 5c3484
	mpz_urandomb (r, rstate, n);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case NEXTPRIME:
Packit 5c3484
      {
Packit 5c3484
	mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
	mpz_nextprime (r, r);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    case BINOM:
Packit 5c3484
      mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
      mpz_eval_expr (lhs, e->operands.ops.lhs);
Packit 5c3484
      mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
      {
Packit 5c3484
	unsigned long int k;
Packit 5c3484
	if (mpz_cmp_ui (rhs, ~(unsigned long int) 0) > 0)
Packit 5c3484
	  {
Packit 5c3484
	    error = "k too large in (n over k) expression";
Packit 5c3484
	    mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	    longjmp (errjmpbuf, 1);
Packit 5c3484
	  }
Packit 5c3484
	k = mpz_get_ui (rhs);
Packit 5c3484
	mpz_bin_ui (r, lhs, k);
Packit 5c3484
      }
Packit 5c3484
      mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
      return;
Packit 5c3484
    case TIMING:
Packit 5c3484
      {
Packit 5c3484
	int t0;
Packit 5c3484
	t0 = cputime ();
Packit 5c3484
	mpz_eval_expr (r, e->operands.ops.lhs);
Packit 5c3484
	printf ("time: %d\n", cputime () - t0);
Packit 5c3484
      }
Packit 5c3484
      return;
Packit 5c3484
    default:
Packit 5c3484
      abort ();
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Evaluate the expression E modulo MOD and put the result in R.  */
Packit 5c3484
void
Packit 5c3484
mpz_eval_mod_expr (mpz_ptr r, expr_t e, mpz_ptr mod)
Packit 5c3484
{
Packit 5c3484
  mpz_t lhs, rhs;
Packit 5c3484
Packit 5c3484
  switch (e->op)
Packit 5c3484
    {
Packit 5c3484
      case POW:
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_mod_expr (lhs, e->operands.ops.lhs, mod);
Packit 5c3484
	mpz_eval_expr (rhs, e->operands.ops.rhs);
Packit 5c3484
	mpz_powm (r, lhs, rhs, mod);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	return;
Packit 5c3484
      case PLUS:
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_mod_expr (lhs, e->operands.ops.lhs, mod);
Packit 5c3484
	mpz_eval_mod_expr (rhs, e->operands.ops.rhs, mod);
Packit 5c3484
	mpz_add (r, lhs, rhs);
Packit 5c3484
	if (mpz_cmp_si (r, 0L) < 0)
Packit 5c3484
	  mpz_add (r, r, mod);
Packit 5c3484
	else if (mpz_cmp (r, mod) >= 0)
Packit 5c3484
	  mpz_sub (r, r, mod);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	return;
Packit 5c3484
      case MINUS:
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_mod_expr (lhs, e->operands.ops.lhs, mod);
Packit 5c3484
	mpz_eval_mod_expr (rhs, e->operands.ops.rhs, mod);
Packit 5c3484
	mpz_sub (r, lhs, rhs);
Packit 5c3484
	if (mpz_cmp_si (r, 0L) < 0)
Packit 5c3484
	  mpz_add (r, r, mod);
Packit 5c3484
	else if (mpz_cmp (r, mod) >= 0)
Packit 5c3484
	  mpz_sub (r, r, mod);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	return;
Packit 5c3484
      case MULT:
Packit 5c3484
	mpz_init (lhs); mpz_init (rhs);
Packit 5c3484
	mpz_eval_mod_expr (lhs, e->operands.ops.lhs, mod);
Packit 5c3484
	mpz_eval_mod_expr (rhs, e->operands.ops.rhs, mod);
Packit 5c3484
	mpz_mul (r, lhs, rhs);
Packit 5c3484
	mpz_mod (r, r, mod);
Packit 5c3484
	mpz_clear (lhs); mpz_clear (rhs);
Packit 5c3484
	return;
Packit 5c3484
      default:
Packit 5c3484
	mpz_init (lhs);
Packit 5c3484
	mpz_eval_expr (lhs, e);
Packit 5c3484
	mpz_mod (r, lhs, mod);
Packit 5c3484
	mpz_clear (lhs);
Packit 5c3484
	return;
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
cleanup_and_exit (int sig)
Packit 5c3484
{
Packit 5c3484
  switch (sig) {
Packit 5c3484
#ifdef LIMIT_RESOURCE_USAGE
Packit 5c3484
  case SIGXCPU:
Packit 5c3484
    printf ("expression took too long to evaluate%s\n", newline);
Packit 5c3484
    break;
Packit 5c3484
#endif
Packit 5c3484
  case SIGFPE:
Packit 5c3484
    printf ("divide by zero%s\n", newline);
Packit 5c3484
    break;
Packit 5c3484
  default:
Packit 5c3484
    printf ("expression required too much memory to evaluate%s\n", newline);
Packit 5c3484
    break;
Packit 5c3484
  }
Packit 5c3484
  exit (-2);
Packit 5c3484
}