/*

  Copyright 2014-2021 by

  Laboratoire d'Informatique de Paris 6, equipe PEQUAN,
  UPMC Universite Paris 06 - CNRS - UMR 7606 - LIP6, Paris, France,

  Laboratoire d'Informatique de Paris 6 - Équipe PEQUAN
  Sorbonne Universités
  UPMC Univ Paris 06
  UMR 7606, LIP6
  Boîte Courrier 169
  4, place Jussieu
  F-75252 Paris Cedex 05
  France,

  Sorbonne Université
  CNRS, Laboratoire d'Informatique de Paris 6, LIP6
  F - 75005 Paris
  France

  and by

  Department of Computer Science & Engineering
  UAA College of Engineering
  University of Alaska Anchorage.

  Contributor Ch. Lauter

  christoph.lauter@christoph-lauter.org

  This software is a computer program whose purpose is to provide an
  environment for safe floating-point code development. It is
  particularly targeted to the automated implementation of
  mathematical floating-point libraries (libm). Amongst other features,
  it offers a certified infinity norm, an automatic polynomial
  implementer and a fast Remez algorithm.

  This software is governed by the CeCILL-C license under French law and
  abiding by the rules of distribution of free software.  You can  use,
  modify and/ or redistribute the software under the terms of the CeCILL-C
  license as circulated by CEA, CNRS and INRIA at the following URL
  "http://www.cecill.info".

  As a counterpart to the access to the source code and  rights to copy,
  modify and redistribute granted by the license, users are provided only
  with a limited warranty  and the software's author,  the holder of the
  economic rights,  and the successive licensors  have only  limited
  liability.

  In this respect, the user's attention is drawn to the risks associated
  with loading,  using,  modifying and/or developing or reproducing the
  software by the user in light of its specific status of free software,
  that may mean  that it is complicated to manipulate,  and  that  also
  therefore means  that it is reserved for developers  and  experienced
  professionals having in-depth computer knowledge. Users are therefore
  encouraged to load and test the software's suitability as regards their
  requirements in conditions enabling the security of their systems and/or
  data to be ensured and,  more generally, to use and operate it in the
  same conditions as regards security.

  The fact that you are presently reading this means that you have had
  knowledge of the CeCILL-C license and that you accept its terms.

  This program is distributed WITHOUT ANY WARRANTY; without even the
  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

*/

#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "general.h"
#include "execute.h"
#include "infnorm.h"
#include "expression.h"
#include "polynomials.h"
#include "printf.h"
#include "hash.h"

#if (!(defined(HAVE_MP_BITCNT_T) && (HAVE_MP_BITCNT_T)))
typedef unsigned long int mp_bitcnt_t;
#endif


/* Helper types */


struct __hash_result_struct_t {
  uint64_t hash;
  int hasHash;
};
typedef struct __hash_result_struct_t hash_result_t;

struct __boolean_result_cache_struct_t {
  int res;
  int cached;
};
typedef struct __boolean_result_cache_struct_t boolean_result_cache_t;

struct __node_result_cache_struct_t {
  node *res;
  int cached;
};
typedef struct __node_result_cache_struct_t node_result_cache_t;

/* Types for constants, sparse polynomials and polynomials */

typedef enum __constant_type_enum_t constant_type_t;
enum __constant_type_enum_t {
  INTEGER = 0,
  EXPRESSION,
  MPFR,
  SCALEDMPQ
};

typedef struct __scaled_mpq_struct_t scaled_mpq_t;
struct __scaled_mpq_struct_t {
  mp_exp_t expo;
  mpq_t significand;
};

typedef struct __constant_struct_t * constant_t;
struct __constant_struct_t {
  unsigned int           refCount;
  constant_type_t        type;
  boolean_result_cache_t isZero;
  boolean_result_cache_t isOne;
  boolean_result_cache_t isMinusOne;
  boolean_result_cache_t isNonNegativeInteger;
  boolean_result_cache_t isInteger;
  boolean_result_cache_t isPositive;
  boolean_result_cache_t isDyadic;
  boolean_result_cache_t isRational;
  node_result_cache_t    expressionCache;
  hash_result_t          hash;
  constant_t             prev;
  constant_t             next;
  union {
    int integer;
    node *expr;
    mpfr_t mpfr;
    scaled_mpq_t scaledMpq;
  } value;
};

typedef struct __sparse_polynomial_struct_t * sparse_polynomial_t;
struct __sparse_polynomial_struct_t {
  unsigned int refCount;
  constant_t deg;
  unsigned int monomialCount;
  hash_result_t hash;
  constant_t *coeffs;
  constant_t *monomialDegrees;
};

#define SPARSE_POLYNOMIAL_GCD_MIN(a, b)                                 (((a) < (b)) ? ((a)) : ((b)))

#define SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_MONOMIAL_COUNT              ((unsigned int) ((((uint64_t) 1u) << SPARSE_POLYNOMIAL_GCD_MIN(20,8*sizeof(unsigned int))) - ((uint64_t) 1u)))
#define SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_DEGREE                      ((int) ((((int64_t) 1) << SPARSE_POLYNOMIAL_GCD_MIN(22,8*sizeof(int)-1)) - ((int64_t) 1)))
#define SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_INTEGER_BOUND               ((mp_bitcnt_t) ((((uint64_t) 1u) << SPARSE_POLYNOMIAL_GCD_MIN(19,8*sizeof(mp_bitcnt_t)-1)) - ((uint64_t) 1u)))
#define SPARSE_POLYNOMIAL_GCD_HEURISTIC_TRIALS                          (4)

#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_MONOMIAL_COUNT              ((unsigned int) 64)
#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_DEGREE                      ((unsigned int) 64)
#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_SPARSITY                    (0.25)
#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_KARATSUBA_MIN_DEGREE            ((unsigned int) 64)
#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_RATIONAL_MIN_DEGREE             ((unsigned int) 64)
#define SPARSE_POLYNOMIAL_MUL_OPTIMIZED_INTEGER_MIN_DEGREE              ((unsigned int) 1024)

#define SPARSE_POLYNOMIAL_DIV_INTEGER_MAX_BITS_ALPHA                    (((uint64_t) (1073741824)) * ((uint64_t) (8)))

#define SPARSE_POLYNOMIAL_INTERPOLATION_DOUBLE_PRECISION_ITERATIONS     (17)

typedef enum __polynomial_type_enum_t polynomial_type_t;
enum __polynomial_type_enum_t {
  SPARSE = 0,
  ADDITION,
  SUBTRACTION,
  MULTIPLICATION,
  COMPOSITION,
  NEGATE,
  POWER
};

typedef enum __polynomial_output_type_enum_t polynomial_output_type_t;
enum __polynomial_output_type_enum_t {
  ANY_FORM = 0,
  HORNER_FORM,
  CANONICAL_FORM
};

struct __polynomial_struct_t {
  unsigned int refCount;
  polynomial_type_t type;
  polynomial_output_type_t outputType;
  hash_result_t hash;
  boolean_result_cache_t usesExpressionConstant;
  union {
    sparse_polynomial_t sparse;
    polynomial_t g;
    struct {
      polynomial_t g;
      polynomial_t h;
    } pair;
    struct {
      polynomial_t g;
      constant_t c;
    } powering;
  } value;
};

/* Globally defined constants and caches */

#define CONSTANT_INTEGER_CACHE_MIN (-16384)
#define CONSTANT_INTEGER_CACHE_MAX (16383)
#define CONSTANT_INTEGER_CACHE_SIZE ((CONSTANT_INTEGER_CACHE_MAX) - (CONSTANT_INTEGER_CACHE_MIN) + 1)

#define CONSTANT_MALLOC_CACHE_SIZE (65536)

typedef struct __cached_constant_struct_t __cached_constant_t;
struct __cached_constant_struct_t {
  constant_t constant;
  int initialized;
};

static int __constant_cache_initialized = 0;
static __cached_constant_t __constant_integer_cache[CONSTANT_INTEGER_CACHE_SIZE];

static constant_t __constant_malloc_cache[CONSTANT_MALLOC_CACHE_SIZE];
static int __constant_malloc_cache_index = 0;

static constant_t __constant_linked_list = NULL;

/* MPZ, MPQ, MPFR and MPFI recycling functions */

#define SOLLYA_RECYCLE_CACHE_MIN_SIZE  ((size_t) (((uint64_t) 1) << 16))
#define SOLLYA_RECYCLE_CACHE_MAX_SIZE  ((size_t) (((uint64_t) 1) << 27))

static int __sollya_recycle_caches_initialized = 0;
static uint64_t __sollya_recycle_mpz_used = ((uint64_t) 0);
static uint64_t __sollya_recycle_mpq_used = ((uint64_t) 0);
static uint64_t __sollya_recycle_mpfr_used = ((uint64_t) 0);
static uint64_t __sollya_recycle_mpfi_used = ((uint64_t) 0);
static mpz_t *__sollya_recycle_mpz_cache = NULL;
static mpq_t *__sollya_recycle_mpq_cache = NULL;
static mpfr_t *__sollya_recycle_mpfr_cache = NULL;
static sollya_mpfi_t *__sollya_recycle_mpfi_cache = NULL;
static size_t __sollya_recycle_mpz_allocated = ((size_t) 0);
static size_t __sollya_recycle_mpq_allocated = ((size_t) 0);
static size_t __sollya_recycle_mpfr_allocated = ((size_t) 0);
static size_t __sollya_recycle_mpfi_allocated = ((size_t) 0);
static size_t __sollya_recycle_mpz_cached = ((size_t) 0);
static size_t __sollya_recycle_mpq_cached = ((size_t) 0);
static size_t __sollya_recycle_mpfr_cached = ((size_t) 0);
static size_t __sollya_recycle_mpfi_cached = ((size_t) 0);

static inline void __sollyaRecycleInitializeCaches() {
  if (__sollya_recycle_caches_initialized) return;
  __sollya_recycle_mpz_cache = (mpz_t *) safeCalloc(SOLLYA_RECYCLE_CACHE_MIN_SIZE, sizeof(mpz_t));
  __sollya_recycle_mpq_cache = (mpq_t *) safeCalloc(SOLLYA_RECYCLE_CACHE_MIN_SIZE, sizeof(mpq_t));
  __sollya_recycle_mpfr_cache = (mpfr_t *) safeCalloc(SOLLYA_RECYCLE_CACHE_MIN_SIZE, sizeof(mpfr_t));
  __sollya_recycle_mpfi_cache = (sollya_mpfi_t *) safeCalloc(SOLLYA_RECYCLE_CACHE_MIN_SIZE, sizeof(sollya_mpfi_t));
  __sollya_recycle_mpz_allocated = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  __sollya_recycle_mpq_allocated = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  __sollya_recycle_mpfr_allocated = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  __sollya_recycle_mpfi_allocated = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  __sollya_recycle_mpz_cached = ((size_t) 0);
  __sollya_recycle_mpq_cached = ((size_t) 0);
  __sollya_recycle_mpfr_cached = ((size_t) 0);
  __sollya_recycle_mpfi_cached = ((size_t) 0);
  __sollya_recycle_mpz_used = ((uint64_t) 0);
  __sollya_recycle_mpq_used = ((uint64_t) 0);
  __sollya_recycle_mpfr_used = ((uint64_t) 0);
  __sollya_recycle_mpfi_used = ((uint64_t) 0);
  __sollya_recycle_caches_initialized = 1;
}

static inline void __sollyaRecycleFreeCaches() {
  size_t i;
  
  if (!__sollya_recycle_caches_initialized) return;
  for (i=((size_t) 0);i<__sollya_recycle_mpz_cached;i++) {
    mpz_clear(__sollya_recycle_mpz_cache[i]);
  }
  for (i=((size_t) 0);i<__sollya_recycle_mpq_cached;i++) {
    mpq_clear(__sollya_recycle_mpq_cache[i]);
  }
  for (i=((size_t) 0);i<__sollya_recycle_mpfr_cached;i++) {
    mpfr_clear(__sollya_recycle_mpfr_cache[i]);
  }
  for (i=((size_t) 0);i<__sollya_recycle_mpfi_cached;i++) {
    sollya_mpfi_clear(__sollya_recycle_mpfi_cache[i]);
  }
  __sollya_recycle_mpz_cached = ((size_t) 0);
  __sollya_recycle_mpq_cached = ((size_t) 0);
  __sollya_recycle_mpfr_cached = ((size_t) 0);
  __sollya_recycle_mpfi_cached = ((size_t) 0);
  safeFree(__sollya_recycle_mpz_cache);
  safeFree(__sollya_recycle_mpq_cache);
  safeFree(__sollya_recycle_mpfr_cache);
  safeFree(__sollya_recycle_mpfi_cache);
  __sollya_recycle_mpz_cache = NULL;
  __sollya_recycle_mpq_cache = NULL;
  __sollya_recycle_mpfr_cache = NULL;
  __sollya_recycle_mpfi_cache = NULL;
  __sollya_recycle_mpz_allocated = ((size_t) 0);
  __sollya_recycle_mpq_allocated = ((size_t) 0);
  __sollya_recycle_mpfr_allocated = ((size_t) 0);
  __sollya_recycle_mpfi_allocated = ((size_t) 0);
  __sollya_recycle_mpz_used = ((uint64_t) 0);
  __sollya_recycle_mpq_used = ((uint64_t) 0);
  __sollya_recycle_mpfr_used = ((uint64_t) 0);
  __sollya_recycle_mpfi_used = ((uint64_t) 0);
  __sollya_recycle_caches_initialized = 0;  
}

static inline void __sollyaRecycleAdaptMpzCacheSize() {
  size_t predictedSize, newSize;
  uint64_t t1, t2;

  __sollyaRecycleInitializeCaches();
  t1 = __sollya_recycle_mpz_used;
  t1 >>= 1;
  predictedSize = (size_t) t1;
  t2 = (uint64_t) predictedSize;
  if (t1 != t2) {
    predictedSize = __sollya_recycle_mpz_allocated;
  }
  if (predictedSize < SOLLYA_RECYCLE_CACHE_MIN_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  }
  if (predictedSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (predictedSize < __sollya_recycle_mpz_allocated) return;
  newSize = __sollya_recycle_mpz_allocated;
  newSize <<= 1;
  if (newSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    newSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (newSize <= __sollya_recycle_mpz_allocated) return;
  __sollya_recycle_mpz_cache = (mpz_t *) safeRealloc(((void *) __sollya_recycle_mpz_cache),
						     (newSize * sizeof(mpz_t)));
  __sollya_recycle_mpz_allocated = newSize;
}

static inline void __sollyaRecycleAdaptMpqCacheSize() {
  size_t predictedSize, newSize;
  uint64_t t1, t2;

  __sollyaRecycleInitializeCaches();
  t1 = __sollya_recycle_mpq_used;
  t1 >>= 1;
  predictedSize = (size_t) t1;
  t2 = (uint64_t) predictedSize;
  if (t1 != t2) {
    predictedSize = __sollya_recycle_mpq_allocated;
  }
  if (predictedSize < SOLLYA_RECYCLE_CACHE_MIN_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  }
  if (predictedSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (predictedSize < __sollya_recycle_mpq_allocated) return;
  newSize = __sollya_recycle_mpq_allocated;
  newSize <<= 1;
  if (newSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    newSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (newSize <= __sollya_recycle_mpq_allocated) return;
  __sollya_recycle_mpq_cache = (mpq_t *) safeRealloc(((void *) __sollya_recycle_mpq_cache),
						     (newSize * sizeof(mpq_t)));
  __sollya_recycle_mpq_allocated = newSize;
}

static inline void __sollyaRecycleAdaptMpfrCacheSize() {
  size_t predictedSize, newSize;
  uint64_t t1, t2;

  __sollyaRecycleInitializeCaches();
  t1 = __sollya_recycle_mpfr_used;
  t1 >>= 1;
  predictedSize = (size_t) t1;
  t2 = (uint64_t) predictedSize;
  if (t1 != t2) {
    predictedSize = __sollya_recycle_mpfr_allocated;
  }
  if (predictedSize < SOLLYA_RECYCLE_CACHE_MIN_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  }
  if (predictedSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (predictedSize < __sollya_recycle_mpfr_allocated) return;
  newSize = __sollya_recycle_mpfr_allocated;
  newSize <<= 1;
  if (newSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    newSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (newSize <= __sollya_recycle_mpfr_allocated) return;
  __sollya_recycle_mpfr_cache = (mpfr_t *) safeRealloc(((void *) __sollya_recycle_mpfr_cache),
						       (newSize * sizeof(mpfr_t)));
  __sollya_recycle_mpfr_allocated = newSize;
}

static inline void __sollyaRecycleAdaptMpfiCacheSize() {
  size_t predictedSize, newSize;
  uint64_t t1, t2;

  __sollyaRecycleInitializeCaches();
  t1 = __sollya_recycle_mpfi_used;
  t1 >>= 1;
  predictedSize = (size_t) t1;
  t2 = (uint64_t) predictedSize;
  if (t1 != t2) {
    predictedSize = __sollya_recycle_mpfi_allocated;
  }
  if (predictedSize < SOLLYA_RECYCLE_CACHE_MIN_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MIN_SIZE;
  }
  if (predictedSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    predictedSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (predictedSize < __sollya_recycle_mpfi_allocated) return;
  newSize = __sollya_recycle_mpfi_allocated;
  newSize <<= 1;
  if (newSize > SOLLYA_RECYCLE_CACHE_MAX_SIZE) {
    newSize = SOLLYA_RECYCLE_CACHE_MAX_SIZE;
  }
  if (newSize <= __sollya_recycle_mpfi_allocated) return;
  __sollya_recycle_mpfi_cache = (sollya_mpfi_t *) safeRealloc(((void *) __sollya_recycle_mpfi_cache),
							      (newSize * sizeof(sollya_mpfi_t)));
  __sollya_recycle_mpfi_allocated = newSize;
}

static inline void __sollyaRecycleMpzInit(mpz_t op) {
  __sollyaRecycleInitializeCaches();
  if (__sollya_recycle_mpz_cached > ((size_t) 0)) {
    __sollya_recycle_mpz_cached--;
    mpz_swap(op, __sollya_recycle_mpz_cache[__sollya_recycle_mpz_cached]);
    mpz_set_ui(op, 0u);
  } else {
    mpz_init(op);
  }
  __sollya_recycle_mpz_used++;
}

static inline void __sollyaRecycleMpzInitSetSi(mpz_t op, signed long int a) {
  __sollyaRecycleMpzInit(op);
  mpz_set_si(op, a);
}

static inline void __sollyaRecycleMpzInitSetUi(mpz_t op, unsigned long int a) {
  __sollyaRecycleMpzInit(op);
  mpz_set_ui(op, a);
}

static inline void __sollyaRecycleMpqInit(mpq_t op) {
  __sollyaRecycleInitializeCaches();
  if (__sollya_recycle_mpq_cached > ((size_t) 0)) {
    __sollya_recycle_mpq_cached--;
    mpq_swap(op, __sollya_recycle_mpq_cache[__sollya_recycle_mpq_cached]);
    mpq_set_ui(op, 0u, 1u);
  } else {
    mpq_init(op);
  }
  __sollya_recycle_mpq_used++;
}

static inline void __sollyaRecycleMpfrInit2(mpfr_t op, mp_prec_t prec) {
  __sollyaRecycleInitializeCaches();
  if (__sollya_recycle_mpfr_cached > ((size_t) 0)) {
    __sollya_recycle_mpfr_cached--;
    mpfr_swap(op, __sollya_recycle_mpfr_cache[__sollya_recycle_mpfr_cached]);
    mpfr_set_prec(op, prec);
  } else {
    mpfr_init2(op, prec);
  }
  __sollya_recycle_mpfr_used++;
}

static inline void __sollyaRecycleMpfiInit2(sollya_mpfi_t op, mp_prec_t prec) {
  __sollyaRecycleInitializeCaches();
  if (__sollya_recycle_mpfi_cached > ((size_t) 0)) {
    __sollya_recycle_mpfi_cached--;
    sollya_mpfi_swap(op, __sollya_recycle_mpfi_cache[__sollya_recycle_mpfi_cached]);
    sollya_mpfi_set_prec(op, prec);
  } else {
    sollya_mpfi_init2(op, prec);
  }
  __sollya_recycle_mpfi_used++;
}

static inline void __sollyaRecycleMpzClear(mpz_t op) {
  __sollyaRecycleInitializeCaches();
  __sollyaRecycleAdaptMpzCacheSize();
  if (__sollya_recycle_mpz_cached < __sollya_recycle_mpz_allocated) {
    mpz_swap(__sollya_recycle_mpz_cache[__sollya_recycle_mpz_cached], op);
    __sollya_recycle_mpz_cached++;
  } else {
    mpz_clear(op);
  }
  __sollya_recycle_mpz_used--;
}

static inline void __sollyaRecycleMpqClear(mpq_t op) {
  __sollyaRecycleInitializeCaches();
  __sollyaRecycleAdaptMpqCacheSize();
  if (__sollya_recycle_mpq_cached < __sollya_recycle_mpq_allocated) {
    mpq_swap(__sollya_recycle_mpq_cache[__sollya_recycle_mpq_cached], op);
    __sollya_recycle_mpq_cached++;
  } else {
    mpq_clear(op);
  }
  __sollya_recycle_mpq_used--;
}

static inline void __sollyaRecycleMpfrClear(mpfr_t op) {
  __sollyaRecycleInitializeCaches();
  __sollyaRecycleAdaptMpfrCacheSize();
  if (__sollya_recycle_mpfr_cached < __sollya_recycle_mpfr_allocated) {
    mpfr_swap(__sollya_recycle_mpfr_cache[__sollya_recycle_mpfr_cached], op);
    __sollya_recycle_mpfr_cached++;
  } else {
    mpfr_clear(op);
  }
  __sollya_recycle_mpfr_used--;
}

static inline void __sollyaRecycleMpfiClear(sollya_mpfi_t op) {
  __sollyaRecycleInitializeCaches();
  __sollyaRecycleAdaptMpfiCacheSize();
  if (__sollya_recycle_mpfi_cached < __sollya_recycle_mpfi_allocated) {
    sollya_mpfi_swap(__sollya_recycle_mpfi_cache[__sollya_recycle_mpfi_cached], op);
    __sollya_recycle_mpfi_cached++;
  } else {
    sollya_mpfi_clear(op);
  }
  __sollya_recycle_mpfi_used--;
}

/* End of recycling functions */

/* Helper functions */

static inline int mpfr_is_machine_integer(int *intval, mpfr_t op) {
  long int t, ttt;
  int tt;

  if (!mpfr_number_p(op)) return 0;
  if (!mpfr_integer_p(op)) return 0;
  if (!mpfr_fits_slong_p(op, GMP_RNDN)) return 0;
  t = mpfr_get_si(op, GMP_RNDN); /* exact */
  tt = t;
  ttt = tt;
  if (t != ttt) return 0;
  *intval = tt;
  return 1;
}

static inline int mpfr_is_machine_unsigned_integer(unsigned int *intval, mpfr_t op) {
  unsigned long int t, ttt;
  unsigned int tt;

  if (!mpfr_number_p(op)) return 0;
  if (mpfr_sgn(op) < 0) return 0;
  if (!mpfr_integer_p(op)) return 0;
  if (!mpfr_fits_ulong_p(op, GMP_RNDN)) return 0;
  t = mpfr_get_ui(op, GMP_RNDN); /* exact */
  tt = t;
  ttt = tt;
  if (t != ttt) return 0;
  *intval = tt;
  return 1;
}

/* This function supposes that op is canonicalized, i.e. zero is
   represented as 0/1, that the numerator and the denominator have no
   common factors and that the denominator is positive. The function
   guarantees that op is canonicalized in output as well.
*/
static inline mp_exp_t mpq_remove_powers_of_two(mpq_t op) {
  mp_bitcnt_t dyadNum, dyadDen;
  mp_exp_t expo;

  if (mpq_sgn(op) == 0) return ((mp_exp_t) 0);

  dyadNum = mpz_scan1(mpq_numref(op), 0);
  dyadDen = mpz_scan1(mpq_denref(op), 0);
  mpz_tdiv_q_2exp(mpq_numref(op), mpq_numref(op), dyadNum);
  mpz_tdiv_q_2exp(mpq_denref(op), mpq_denref(op), dyadDen);
  expo = dyadNum - dyadDen;

  return expo;
}

static inline void scaledMpqAdd(mp_exp_t *EC, mpq_t c,
				mp_exp_t EA, mpq_t a,
				mp_exp_t EB, mpq_t b) {
  if (EB <= EA) {
    *EC = EB;
    mpq_mul_2exp(c, a, EA - EB); /* Exponent overflow possible */
    mpq_add(c, c, b);
  } else {
    *EC = EA;
    mpq_mul_2exp(c, b, EB - EA); /* Exponent overflow possible */
    mpq_add(c, c, a);
  }
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
}

static inline void scaledMpqAddInt(mp_exp_t *EC, mpq_t c,
				   mp_exp_t EA, mpq_t a,
				   int b) {
  mpq_t t;

  /* Can be optimized */
  __sollyaRecycleMpqInit(t);
  mpq_set_si(t, b, 1u);
  mpq_canonicalize(t);
  scaledMpqAdd(EC, c, EA, a, 0, t);
  __sollyaRecycleMpqClear(t);
}

static inline void scaledMpqSub(mp_exp_t *EC, mpq_t c,
				mp_exp_t EA, mpq_t a,
				mp_exp_t EB, mpq_t b) {
  if (EB <= EA) {
    *EC = EB;
    mpq_mul_2exp(c, a, EA - EB); /* Exponent overflow possible */
    mpq_sub(c, c, b);
  } else {
    *EC = EA;
    mpq_mul_2exp(c, b, EB - EA); /* Exponent overflow possible */
    mpq_sub(c, a, c);
  }
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
}

static inline void scaledMpqSubInt(mp_exp_t *EC, mpq_t c,
				   mp_exp_t EA, mpq_t a,
				   int b) {
  mpq_t t;

  /* Can be optimized */
  __sollyaRecycleMpqInit(t);
  mpq_set_si(t, b, 1u);
  mpq_canonicalize(t);
  scaledMpqSub(EC, c, EA, a, 0, t);
  __sollyaRecycleMpqClear(t);
}

static inline void scaledMpqIntSub(mp_exp_t *EC, mpq_t c,
				   int a,
				   mp_exp_t EB, mpq_t b) {
  mpq_t t;

  /* Can be optimized */
  __sollyaRecycleMpqInit(t);
  mpq_set_si(t, a, 1u);
  mpq_canonicalize(t);
  scaledMpqSub(EC, c, 0, t, EB, b);
  __sollyaRecycleMpqClear(t);
}

static inline void scaledMpqMul(mp_exp_t *EC, mpq_t c,
				mp_exp_t EA, mpq_t a,
				mp_exp_t EB, mpq_t b) {
  *EC = EA + EB; /* Exponent overflow possible */
  mpq_mul(c, a, b);
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
}

static inline void scaledMpqMulInt(mp_exp_t *EC, mpq_t c,
				   mp_exp_t EA, mpq_t a,
				   int b) {
  mpq_t t;

  /* Can be optimized */
  __sollyaRecycleMpqInit(t);
  mpq_set_si(t, b, 1u);
  mpq_canonicalize(t);
  scaledMpqMul(EC, c, EA, a, 0, t);
  __sollyaRecycleMpqClear(t);
}

static inline int sollya_mpz_pow(mpz_t z, mpz_t x, mpz_t y) {
  unsigned long int k;
  int sgn, res;
  mpz_t yhi, ylo, xyhi, xylo, xt14;

  /* Get sign of y */
  sgn = mpz_sgn(y);

  /* Refuse work for negative y */
  if (sgn < 0) return 0;

  /* If y is zero, set z to 1 */
  if (sgn == 0) {
    mpz_set_ui(z, 1u);
    return 1;
  }

  /* Try to see if y fits into a ulong */
  if (mpz_fits_ulong_p(y)) {
    k = mpz_get_ui(y);
    mpz_pow_ui(z, x, k);
    return 1;
  }

  /* Decompose y into y = yhi * 2^14 + ylo */
  __sollyaRecycleMpzInit(yhi);
  __sollyaRecycleMpzInit(ylo);
  mpz_fdiv_qr_ui(yhi, ylo, y, (1u << 14));

  /* Compute x^y as

     x^y = x^(2^14 * yhi + ylo) = (x^(2^14))^yhi * x^ylo.

  */
  __sollyaRecycleMpzInit(xylo);
  __sollyaRecycleMpzInit(xyhi);
  __sollyaRecycleMpzInit(xt14);
  if (sollya_mpz_pow(xylo, x, ylo)) {
    mpz_pow_ui(xt14, x, (1u << 14));
    res = sollya_mpz_pow(xyhi, xt14, yhi);
    mpz_mul(z, xyhi, xylo);
  } else {
    res = 0;
  }

  /* Clear temporaries */
  __sollyaRecycleMpzClear(xt14);
  __sollyaRecycleMpzClear(xyhi);
  __sollyaRecycleMpzClear(xylo);
  __sollyaRecycleMpzClear(ylo);
  __sollyaRecycleMpzClear(yhi);

  /* Return success indicator */
  return res;
}

static inline int tryScaledMpqDiv(mp_exp_t *EC, mpq_t c,
				  mp_exp_t EA, mpq_t a,
				  mp_exp_t EB, mpq_t b) {
  if (mpq_sgn(b) == 0) return 0;
  *EC = EA - EB; /* Exponent overflow possible */
  mpq_div(c, a, b);
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
  return 1;
}

static inline void sollya_mpq_gcd(mpq_t c, mpq_t a, mpq_t b) {
  mpz_t u, v;

  __sollyaRecycleMpzInit(u);
  __sollyaRecycleMpzInit(v);
  mpz_mul(mpq_denref(c), mpq_denref(a), mpq_denref(b));
  mpz_mul(u, mpq_numref(a), mpq_denref(b));
  mpz_mul(v, mpq_denref(a), mpq_numref(b));
  mpz_gcd(mpq_numref(c), u, v);
  __sollyaRecycleMpzClear(v);
  __sollyaRecycleMpzClear(u);
  mpq_canonicalize(c);
}

static inline void sollya_mpq_extended_euclid(mpq_t g, mpq_t c, mpq_t d,
					      mpq_t a, mpq_t b) {
  mpz_t u, v;

  __sollyaRecycleMpzInit(u);
  __sollyaRecycleMpzInit(v);
  mpz_mul(mpq_denref(g), mpq_denref(a), mpq_denref(b));
  mpz_set_ui(mpq_denref(c), 1u);
  mpz_set_ui(mpq_denref(d), 1u);
  mpz_mul(u, mpq_numref(a), mpq_denref(b));
  mpz_mul(v, mpq_denref(a), mpq_numref(b));
  mpz_gcdext(mpq_numref(g), mpq_numref(c), mpq_numref(d), u, v);
  __sollyaRecycleMpzClear(v);
  __sollyaRecycleMpzClear(u);
  mpq_canonicalize(g);
  mpq_canonicalize(c);
  mpq_canonicalize(d);
}

static inline int scaledMpqIsInteger(mp_exp_t E, mpq_t a) {
  mpq_t aa;
  mp_exp_t EE;
  int res;

  /* 0 is an integer */
  if (mpq_sgn(a) == 0) return 1;

  /* If the denominator q is one in magnitude and the exponent E
     non-negative, 2^E * p/q is integer
  */
  if ((E >= 0) &&
      (mpz_cmpabs_ui(mpq_denref(a),1u) == 0)) return 1;

  /* If the denominator q is one in magnitude and the exponent E
     non-negative, 2^E * p/q is integer
  */
  if ((E >= 0) &&
      (mpz_cmpabs_ui(mpq_denref(a),1u) == 0)) return 1;

  /* We can take short-cuts if the numerator p and the denominator q
     are both odd. Check that condition.
  */
  if (mpz_odd_p(mpq_numref(a)) &&
      mpz_odd_p(mpq_denref(a))) {
    /* Here p and q are co-prime and odd. If E is negative, 2^E * p/q
       is no integer. Otherwise 2^E * p/q is an integer iff abs(p) ==
       abs(q).
    */
    if (E < 0) return 0;
    return (mpz_cmpabs(mpq_numref(a),mpq_denref(a)) == 0);
  }

  /* Here we know that at least one of the numerator and denominator
     has 2 as a prime factor.
  */
  __sollyaRecycleMpqInit(aa);
  mpq_set(aa, a);
  EE = E;

  mpq_canonicalize(aa);
  EE += mpq_remove_powers_of_two(aa); /* Exponent overflow possible */

  /* Now we have 2^EE * aa = 2^E * a, aa in least factors and all
     prime factors 2 taken out.
  */
  if (EE < 0) {
    __sollyaRecycleMpqClear(aa);
    return 0;
  }

  res = (mpz_cmpabs(mpq_numref(aa),mpq_denref(aa)) == 0);
  __sollyaRecycleMpqClear(aa);

  return res;
}

static inline int scaledMpqIsDyadic(mp_exp_t E, mpq_t a) {
  mpq_t aa;
  int res;

  UNUSED_PARAM(E);

  /* 0 is a dyadic */
  if (mpq_sgn(a) == 0) return 1;

  /* If the denominator q is one in magnitude, 2^E * p/q is dyadic. */
  if (mpz_cmpabs_ui(mpq_denref(a),1u) == 0) return 1;

  /* We can take short-cuts if the numerator p and the denominator q
     are both odd. Check that condition.
  */
  if (mpz_odd_p(mpq_numref(a)) &&
      mpz_odd_p(mpq_denref(a))) {
    /* Here p and q are co-prime and odd and q is different from 1.
       The number is not dyadic.
    */
    return 0;
  }

  /* Here we know that at least one of the numerator and denominator
     has 2 as a prime factor.
  */
  __sollyaRecycleMpqInit(aa);
  mpq_set(aa, a);
  mpq_canonicalize(aa);
  mpq_remove_powers_of_two(aa);

  /* Now we have 2^EE * aa = 2^E * a, aa in least factors and all
     prime factors 2 taken out.

     2^EE * pp/qq with pp and qq odd, co-prime. The number is
     dyadic iff qq is one in magnitude.
  */
  res = (mpz_cmpabs_ui(mpq_denref(a),1u) == 0);
  __sollyaRecycleMpqClear(aa);
  return res;
}

/* Determine if 2^EA * a > 2^EB * b */
static inline int scaledMpqIsGreaterThan(mp_exp_t EA, mpq_t a,
					 mp_exp_t EB, mpq_t b) {
  mp_exp_t E, Emin, Emax, F, D;
  mpz_t p, q;
  mpq_t r;
  int res;

  /* Handle zero in input */
  if (mpq_sgn(a) == 0) {
    return (mpq_sgn(b) < 0);
  }
  if (mpq_sgn(b) == 0) {
    return (mpq_sgn(a) > 0);
  }

  /* Handle inputs of different signs */
  if (mpq_sgn(a) * mpq_sgn(b) < 0) {
    return (mpq_sgn(a) > 0);
  }

  /* Here 2^EA * a and 2^EB * b have the same sign

     The comparison 2^EA * a > 2^EB * b is equivalent to

     *    2^(EA - EB) > b/a      if a is positive
     *    2^(EA - EB) < b/a      otherwise

     We start by computing Emin and Emax such that

     2^Emin < b/a < 2^Emax

     This way we can then perform an easy, first check
     based on the order of magnitudes.

  */
  E = (mp_exp_t) ((mpz_sizeinbase(mpq_numref(b),2) +
		   mpz_sizeinbase(mpq_denref(a),2)) -
		  (mpz_sizeinbase(mpq_denref(b),2) +
		   mpz_sizeinbase(mpq_numref(a),2))); /* Exponent overflow possible */
  Emin = E - 2; /* Exponent overflow possible */
  Emax = E + 2; /* Exponent overflow possible */

  /* Perform an easy check based on the magnitudes, taking the sign of
     a into account.
  */
  if (mpq_sgn(a) > 0) {
    /* a is positive, so we have to perform the comparison

       2^(EA - EB) > b/a

       knowing that 2^Emin < b/a < 2^Emax.

       If EA - EB >= Emax, we have

       2^(EA - EB) >= 2^Emax > b/a

       hence the answer surely is "yes, 2^(EA - EB) > b/a is
       satisfied"

       If EA - EB <= Emin, we have

       2^(EA - EB) <= 2^Emin < b/a

       hence the answer surely is "no, 2^(EA - EB) > b/a is not
       satisfied"

    */
    if (EA - EB >= Emax) return 1; /* Exponent overflow possible */
    if (EA - EB <= Emin) return 0; /* Exponent overflow possible */
  } else {
    /* a is positive, so we have to perform the comparison

       2^(EA - EB) < b/a

       knowing that 2^Emin < b/a < 2^Emax.

       If EA - EB >= Emax, we have

       2^(EA - EB) >= 2^Emax > b/a

       hence the answer surely is "no, 2^(EA - EB) < b/a is not
       satisfied"

       If EA - EB <= Emin, we have

       2^(EA - EB) <= 2^Emin < b/a

       hence the answer surely is "yes, 2^(EA - EB) < b/a is
       satisfied"

    */
    if (EA - EB >= Emax) return 0; /* Exponent overflow possible */
    if (EA - EB <= Emin) return 1; /* Exponent overflow possible */
  }

  /* Here, b/a is too close to 2^E to allow the comparison to be
     decided merely on the base of the order of magnitude.

     However, now it is reasonable to compute the ratio

     b/a = 2^F * p/q

     where p and q are co-prime odd integers and q is positive.

     Then the comparison between 2^(EA - EB) and b/a
     becomes a comparison between

     2^(EA - EB - F) * q and p.

     Let D = EA - EB - F

  */
  __sollyaRecycleMpqInit(r);
  mpq_div(r, b, a);
  F = mpq_remove_powers_of_two(r);
  __sollyaRecycleMpzInit(p);
  __sollyaRecycleMpzInit(q);
  mpq_get_num(p, r);
  mpq_get_den(q, r);
  __sollyaRecycleMpqClear(r);

  /* Compute D = EA - EB - F */
  D = EA - EB - F; /* Exponent overflow possible */

  /* Now integrate 2^D into q or p, such that the comparison boils down
     to comparing p and q.
  */
  if (D >= 0) {
    mpz_mul_2exp(q, q, (mp_bitcnt_t) D);
  } else {
    mpz_mul_2exp(p, p, (mp_bitcnt_t) (-D));
  }

  if (mpq_sgn(a) > 0) {
    /* a is positive. We have to perform the comparison

       2^(EA - EB) > b/a

       which is equivalent to

       q > p.

    */
    res = (mpz_cmp(q, p) > 0);
    __sollyaRecycleMpzClear(p);
    __sollyaRecycleMpzClear(q);
    return res;
  }

  /* a is negative. We have to perform the comparison

     2^(EA - EB) < b/a

     which is equivalent to

     q < p.

  */
  res = (mpz_cmp(q, p) < 0);
  __sollyaRecycleMpzClear(p);
  __sollyaRecycleMpzClear(q);
  return res;
}

static inline void scaledMpqFloor(mp_exp_t *EB, mpq_t b,
				  mp_exp_t EA, mpq_t a) {
  mp_exp_t sizeNum, sizeDen, sizeOut, ER, ERP, Erest, Edelta;
  mp_prec_t precOut;
  mpfr_t t;
  mpq_t r, rPrime, one, rest, delta;
  mpz_t z, num, den;

  /* If 2^EA * a is an integer, we have nothing to do */
  if (scaledMpqIsInteger(EA, a)) {
    *EB = EA;
    mpq_set(b, a);
    return;
  }

  /* Here 2^EA * a is not an integer and hence non-zero

     Get approximate sizes of the numerator and the denominator in
     bits.

  */
  sizeNum = (mp_exp_t) (mpz_sizeinbase(mpq_numref(a),2) + 1);
  sizeDen = (mp_exp_t) (mpz_sizeinbase(mpq_denref(a),2));

  /* Compute an upper bound on the size of the output */
  sizeOut = EA + sizeNum + 5 - sizeDen; /* Integer overflow possible */
  if (sizeOut < 12) sizeOut = 12;

  /* This upper bound on the size of the output gives us
     an idea of the precision we need to compute a first guess
     on the output.
  */
  precOut = sizeOut;
  if (precOut < 12) precOut = 12;

  /* Compute a first guess on the output */
  __sollyaRecycleMpfrInit2(t, precOut);
  mpfr_set_z_2exp(t, mpq_numref(a), EA, GMP_RNDD);
  mpfr_div_z(t, t, mpq_denref(a), GMP_RNDD);
  mpfr_floor(t, t);

  /* Represent the first guess as a scaled MPQ */
  __sollyaRecycleMpqInit(r);
  __sollyaRecycleMpzInit(z);
  if (mpfr_zero_p(t)) {
    ER = EA;
    mpq_set_si(r, 0, 1u);
  } else {
    ER = mpfr_get_z_2exp(z, t);
    mpq_set_z(r,z);
  }
  __sollyaRecycleMpfrClear(t);
  __sollyaRecycleMpzClear(z);
  mpq_canonicalize(r);
  ER += mpq_remove_powers_of_two(r); /* Exponent overflow possible */

  /* As we computed the first guess in round-down mode, we are sure
     that 2^ER * r < 2^EA * a.

     We now check if

     2^ER * r > 2^EA * a - 1,

     i.e. if

     2^ER * r + 1 > 2^EA * a

     This condition should be satisfied in almost all cases. If it is
     not, the first guess was not accurate enough.

     Representing 2^ER * r + 1 is in general as memory intensive as
     representing 2^ER * r.

  */
  __sollyaRecycleMpqInit(rPrime);
  __sollyaRecycleMpqInit(one);
  mpq_set_si(one, 1, 1u);
  scaledMpqAdd(&ERP, rPrime, ER, r, 0, one);
  __sollyaRecycleMpqClear(one);

  if (scaledMpqIsGreaterThan(ERP, rPrime, EA, a)) {
    __sollyaRecycleMpqClear(rPrime);
    *EB = ER;
    mpq_set(b,r);
    __sollyaRecycleMpqClear(r);
    return;
  }
  __sollyaRecycleMpqClear(rPrime);

  /* The accuracy of the first guess was not enough.
     We compute the rest

     2^Erest * rest = 2^EA * a - 2^ER * r

     then we compute floor(2^Erest * rest) in MPQ/MPZ arithmetic.

  */
  __sollyaRecycleMpqInit(rest);
  scaledMpqSub(&Erest, rest, EA, a, ER, r);
  __sollyaRecycleMpzInit(num);
  __sollyaRecycleMpzInit(den);
  mpq_get_num(num, rest);
  mpq_get_den(den, rest);
  __sollyaRecycleMpqClear(rest);
  if (Erest > 0) {
    mpz_mul_2exp(num, num, (mp_bitcnt_t) Erest);
  } else {
    mpz_mul_2exp(den, den, (mp_bitcnt_t) (-Erest)); /* Exponent overflow possible */
  }

  /* Now compute floor(num/den). That's a Euclidean division. */
  mpz_fdiv_q(num,num,den);
  __sollyaRecycleMpzClear(den);

  /* Represent the correction as a scaled MPQ */
  __sollyaRecycleMpqInit(delta);
  mpq_set_z(delta, num);
  mpq_canonicalize(delta);
  Edelta = mpq_remove_powers_of_two(delta);
  __sollyaRecycleMpzClear(num);

  /* The result is 2^ER * r + 2^Edelta * delta */
  scaledMpqAdd(EB, b, ER, r, Edelta, delta);
  __sollyaRecycleMpqClear(r);
  __sollyaRecycleMpqClear(delta);
}

/* Checks if 2^E * a is an integer that can be represented as an unsigned int and
   computes that integer.
*/
static inline int scaledMpqIsUnsignedInt(unsigned int *n, mp_exp_t E, mpq_t a) {
  mpfr_t t;
  unsigned long tI, ttI, uIAbs;
  unsigned int tt;
  signed long uI;
  mpz_t r, tmp, u, v;

  /* First check for zero */
  if (mpq_sgn(a) == 0) {
    *n = 0u;
    return 1;
  }

  /* Then check if 2^E * a is a non-negative integer */
  if (!scaledMpqIsInteger(E,a)) return 0;
  if (mpq_sgn(a) < 0) return 0;

  /* Here, 2^E * a is a positive integer

     Compute a first guess on its integer value.

     We compute t = floor(2^E * p/q * (1 + eps))

     with abs(eps) <= 2 * e + e^2

     where abs(e) <= 2^(2 * -l - 7 + 1)

     where l is the number of bits in an unsigned int.

  */
  __sollyaRecycleMpfrInit2(t, 2 * 8 * sizeof(unsigned int) + 7);
  mpfr_set_z_2exp(t, mpq_numref(a), E, GMP_RNDD);
  mpfr_div_z(t, t, mpq_denref(a), GMP_RNDD);
  mpfr_floor(t, t);
  if (!mpfr_fits_ulong_p(t, GMP_RNDN)) {
    __sollyaRecycleMpfrClear(t);
    return 0;
  }
  tI = mpfr_get_ui(t, GMP_RNDN); /* exact */
  __sollyaRecycleMpfrClear(t);

  /* Now compute the "rest"


     / (2^E * p - q * t) / q  if E >= 0
     r/q = 2^E * p/q - t = |
     \ (p - 2^-E * q * t) / q if E < 0

     We can be sure that this computation is not too
     memory intensive because we know that t is bounded.

  */
  __sollyaRecycleMpzInit(r);
  if (E >= 0) {
    mpz_set(r, mpq_numref(a));
    mpz_mul_2exp(r, r, (mp_bitcnt_t) E);
    __sollyaRecycleMpzInit(tmp);
    mpz_mul_ui(tmp, mpq_denref(a), tI);
    mpz_sub(r, r, tmp);
    __sollyaRecycleMpzClear(tmp);
  } else {
    mpz_set(r, mpq_denref(a));
    mpz_mul_2exp(r, r, (mp_bitcnt_t) (-E)); /* Exponent overflow possible */
    mpz_mul_ui(r, r, tI);
    mpz_sub(r, mpq_numref(a), r);
  }

  /* If the rest is zero, we know the integer value

     tI = 2^E * a

  */
  if (mpz_sgn(r) == 0) {
    __sollyaRecycleMpzClear(r);
    tt = tI;
    ttI = tt;
    if (ttI != tI) return 0;
    *n = tt;
    return 1;
  }

  /* Here, the rest r/q is not zero. We perform
     an Euclidean division.

     u = floor(r/q), q * u + v = r

  */
  __sollyaRecycleMpzInit(u);
  __sollyaRecycleMpzInit(v);
  mpz_tdiv_qr(u, v, r, mpq_denref(a));

  /* If v is non-zero, 2^E * a is no integer */
  if (mpz_sgn(v) != 0) {
    __sollyaRecycleMpzClear(v);
    __sollyaRecycleMpzClear(u);
    __sollyaRecycleMpzClear(r);
    return 0;
  }
  __sollyaRecycleMpzClear(v);
  __sollyaRecycleMpzClear(r);

  /* Here we know that u + tI = 2^E * a

     We start by checking if u fits a signed long.

  */
  if (!mpz_fits_slong_p(u)) {
    __sollyaRecycleMpzClear(u);
    return 0;
  }
  uI = mpz_get_si(u);
  __sollyaRecycleMpzClear(u);

  /* Here we know that uI + tI = 2^E * a

     where uI is a signed long and tI is an unsigned long.

  */
  if (uI < 0l) {
    uIAbs = -uI;
    if (uIAbs < tI) return 0;
    tI -= uIAbs;
    tt = tI;
    ttI = tt;
    if (ttI != tI) return 0;
    *n = tt;
    return 1;
  }

  /* Here we know that uI + tI = 2^E * a and uI >= 0 */
  uIAbs = uI;
  tI += uIAbs;
  if (tI < uIAbs) return 0;
  tt = tI;
  ttI = tt;
  if (ttI != tI) return 0;
  *n = tt;
  return 1;
}

/* Checks if 2^E * a is an integer that can be represented as an int and
   computes that integer.
*/
static inline int scaledMpqIsInt(int *n, mp_exp_t E, mpq_t a) {
  unsigned int nU, nUU, rMA;
  int r, rM;
  mpq_t aAbs;

  if (mpq_sgn(a) >= 0) {
    if (!scaledMpqIsUnsignedInt(&nU, E, a)) return 0;
    r = nU;
    if (r < 0) return 0;
    nUU = r;
    if (nUU != nU) return 0;
    *n = r;
    return 1;
  }

  __sollyaRecycleMpqInit(aAbs);
  mpq_neg(aAbs, a);
  if (!scaledMpqIsUnsignedInt(&nU, E, aAbs)) {
    __sollyaRecycleMpqClear(aAbs);
    return 0;
  }
  __sollyaRecycleMpqClear(aAbs);
  r = nU;
  if (r < 0) return 0;
  nUU = r;
  if (nUU != nU) return 0;
  rM = -r;
  if (rM > 0) return 0;
  rMA = -rM;
  if (rMA != nU) return 0;
  *n = rM;
  return 1;
}

static inline int tryScaledMpqPow(mp_exp_t *EC, mpq_t c,
				  mp_exp_t EA, mpq_t a,
				  mp_exp_t EB, mpq_t b) {
  unsigned int n;
  mpz_t eaZ, nZ, num, den;
  signed long int eaI, ecI, ecII;
  mp_exp_t eaE, ecR;

  if (!scaledMpqIsUnsignedInt(&n, EB, b)) return 0;
  if (n == 0u) {
    *EC = 0;
    mpq_set_ui(c,1,1u);
    mpq_canonicalize(c);
    *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
    return 1;
  }
  if (n == 1u) {
    *EC = EA;
    mpq_set(c, a);
    mpq_canonicalize(c);
    *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
    return 1;
  }

  eaI = EA;
  eaE = eaI;
  if (eaE != EA) return 0;
  __sollyaRecycleMpzInitSetSi(eaZ, eaI);
  __sollyaRecycleMpzInitSetUi(nZ, n);
  mpz_mul(eaZ, eaZ, nZ);
  if (!mpz_fits_slong_p(eaZ)) {
    __sollyaRecycleMpzClear(nZ);
    __sollyaRecycleMpzClear(eaZ);
    return 0;
  }
  ecI = mpz_get_si(eaZ);
  __sollyaRecycleMpzClear(nZ);
  __sollyaRecycleMpzClear(eaZ);
  ecR = ecI;
  ecII = ecR;
  if (ecII != ecI) return 0;

  __sollyaRecycleMpzInit(num);
  __sollyaRecycleMpzInit(den);

  mpq_get_num(num, a);
  mpq_get_den(den, a);

  mpz_pow_ui(num, num, n);
  mpz_pow_ui(den, den, n);

  *EC = ecR;
  mpq_set_num(c, num);
  mpq_set_den(c, den);
  mpq_canonicalize(c);
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */

  __sollyaRecycleMpzClear(den);
  __sollyaRecycleMpzClear(num);

  return 1;
}

static inline int tryScaledMpqPowInt(mp_exp_t *EC, mpq_t c,
				     mp_exp_t EA, mpq_t a,
				     int b) {
  unsigned int n;
  mpz_t eaZ, nZ, num, den;
  signed long int eaI, ecI, ecII;
  mp_exp_t eaE, ecR;
  int bb;

  if (b < 0) return 0;
  n = b;
  bb = n;
  if (bb < 0) return 0;
  if (bb != b) return 0;

  if (n == 0u) {
    *EC = 0;
    mpq_set_ui(c,1,1u);
    mpq_canonicalize(c);
    *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
    return 1;
  }
  if (n == 1u) {
    *EC = EA;
    mpq_set(c, a);
    mpq_canonicalize(c);
    *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */
    return 1;
  }

  eaI = EA;
  eaE = eaI;
  if (eaE != EA) return 0;
  __sollyaRecycleMpzInitSetSi(eaZ, eaI);
  __sollyaRecycleMpzInitSetUi(nZ, n);
  mpz_mul(eaZ, eaZ, nZ);
  if (!mpz_fits_slong_p(eaZ)) {
    __sollyaRecycleMpzClear(nZ);
    __sollyaRecycleMpzClear(eaZ);
    return 0;
  }
  ecI = mpz_get_si(eaZ);
  __sollyaRecycleMpzClear(nZ);
  __sollyaRecycleMpzClear(eaZ);
  ecR = ecI;
  ecII = ecR;
  if (ecII != ecI) return 0;

  __sollyaRecycleMpzInit(num);
  __sollyaRecycleMpzInit(den);

  mpq_get_num(num, a);
  mpq_get_den(den, a);

  mpz_pow_ui(num, num, n);
  mpz_pow_ui(den, den, n);

  *EC = ecR;
  mpq_set_num(c, num);
  mpq_set_den(c, den);
  mpq_canonicalize(c);
  *EC += mpq_remove_powers_of_two(c); /* Exponent overflow possible */

  __sollyaRecycleMpzClear(den);
  __sollyaRecycleMpzClear(num);

  return 1;
}

static inline int tryExactIntAddition(int *c, int a, int b) {
  int aNeg, bNeg, cNeg, cT;
  unsigned int aAbs, bAbs, cAbs, cTAbs;

  if (a >= 0) {
    aAbs = a;
    aNeg = 0;
  } else {
    aAbs = -a;
    aNeg = 1;
  }
  if (b >= 0) {
    bAbs = b;
    bNeg = 0;
  } else {
    bAbs = -b;
    bNeg = 1;
  }
  if (aNeg == bNeg) {
    cNeg = aNeg;
    cAbs = aAbs + bAbs;
    if (cAbs < aAbs) return 0; /* Should never happen */
  } else {
    if (aAbs >= bAbs) {
      cNeg = aNeg;
      cAbs = aAbs - bAbs;
    } else {
      cNeg = bNeg;
      cAbs = bAbs - aAbs;
    }
  }
  if (cNeg) {
    cT = -cAbs;
    if (cT > 0) return 0;
    cTAbs = -cT;
    if (cTAbs != cAbs) return 0;
  } else {
    cT = cAbs;
    if (cT < 0) return 0;
    cTAbs = cT;
    if (cTAbs != cAbs) return 0;
  }
  *c = cT;
  return 1;
}

static inline int tryExactIntSubtraction(int *c, int a, int b) {
  int aNeg, bNeg, cNeg, cT;
  unsigned int aAbs, bAbs, cAbs, cTAbs;

  if (a >= 0) {
    aAbs = a;
    aNeg = 0;
  } else {
    aAbs = -a;
    aNeg = 1;
  }
  if (b >= 0) {
    bAbs = b;
    bNeg = 1;
  } else {
    bAbs = -b;
    bNeg = 0;
  }
  if (aNeg == bNeg) {
    cNeg = aNeg;
    cAbs = aAbs + bAbs;
    if (cAbs < aAbs) return 0; /* Should never happen */
  } else {
    if (aAbs >= bAbs) {
      cNeg = aNeg;
      cAbs = aAbs - bAbs;
    } else {
      cNeg = bNeg;
      cAbs = bAbs - aAbs;
    }
  }
  if (cNeg) {
    cT = -cAbs;
    if (cT > 0) return 0;
    cTAbs = -cT;
    if (cTAbs != cAbs) return 0;
  } else {
    cT = cAbs;
    if (cT < 0) return 0;
    cTAbs = cT;
    if (cTAbs != cAbs) return 0;
  }
  *c = cT;
  return 1;
}

static inline void exactUint64Mul(uint64_t *ch,
				  uint64_t *cl,
				  uint64_t a,
				  uint64_t b) {
  uint64_t ah, al, bh, bl;
  uint64_t ahbh, ahbl, albh, albl;
  uint64_t ahblh, ahbll, albhh, albhl;
  uint64_t rh, rl;

  ah = a >> 32;        bh = a >> 32;
  al = a - (ah << 32); bl = b - (bh << 32);

  ahbh = ah * bh; ahbl = ah * bl;
  albh = al * bh; albl = al * bl;

  ahblh = ahbl >> 32; ahbll = ahbl - (ahblh << 32);
  albhh = albh >> 32; albhl = albh - (albhh << 32);

  ahbll <<= 32;
  albhl <<= 32;

  rl = albl + ahbll;
  if (rl < ahbll) ahblh++;

  rl += albhl;
  if (rl < albhl) ahblh++;

  rh = ahbh + ahblh + albhh;

  *ch = rh;
  *cl = rl;
}

static inline int tryExactUint64Multiplication(uint64_t *c,
					       uint64_t a,
					       uint64_t b) {
  uint64_t cth, ctl;

  exactUint64Mul(&cth, &ctl, a, b);

  if (cth == UINT64_C(0)) {
    *c = ctl;
    return 1;
  }
  return 0;
}

static inline int tryExactUnsignedIntMultiplication(unsigned int *c,
						    unsigned int a,
						    unsigned int b) {
  uint64_t a64, b64, c64, cT64;
  unsigned int aa, bb, cT;

  a64 = a;
  aa = a64;
  if (aa != a) return 0; /* should never happen */

  b64 = b;
  bb = b64;
  if (bb != b) return 0; /* should never happen */

  if (!tryExactUint64Multiplication(&c64, a64, b64)) return 0;

  cT = c64;
  cT64 = cT;
  if (c64 != cT64) return 0;

  *c = cT;
  return 1;
}

static inline int tryExactIntMultiplication(int *c, int a, int b) {
  int aNeg, bNeg, cNeg;
  unsigned int aAbs, bAbs, cAbs, cTAbs;
  int cT;

  if (a >= 0) {
    aAbs = a;
    aNeg = -1;
  } else {
    aAbs = -a;
    aNeg = 1;
  }
  if (b >= 0) {
    bAbs = b;
    bNeg = -1;
  } else {
    bAbs = -b;
    bNeg = 1;
  }
  cNeg = aNeg * bNeg;
  if (!tryExactUnsignedIntMultiplication(&cAbs, aAbs, bAbs)) return 0;
  if (cNeg < 0) {
    cT = -cAbs;
    if (cT > 0) return 0;
    cTAbs = -cT;
    if (cTAbs != cAbs) return 0;
  } else {
    cT = cAbs;
    if (cT < 0) return 0;
    cTAbs = cT;
    if (cTAbs != cAbs) return 0;
  }
  *c = cT;
  return 1;
}

static inline int tryExactUnsignedIntDivision(unsigned int *c,
					      unsigned int a,
					      unsigned int b) {
  if (b == 0u) return 0;
  if (a % b != 0u) return 0;
  *c = a / b;
  return 1;
}

static inline int tryExactIntDivision(int *c, int a, int b) {
  int aNeg, bNeg, cNeg;
  unsigned int aAbs, bAbs, cAbs, cTAbs;
  int cT;

  if (a >= 0) {
    aAbs = a;
    aNeg = -1;
  } else {
    aAbs = -a;
    aNeg = 1;
  }
  if (b >= 0) {
    bAbs = b;
    bNeg = -1;
  } else {
    bAbs = -b;
    bNeg = 1;
  }
  cNeg = aNeg * bNeg;
  if (!tryExactUnsignedIntDivision(&cAbs, aAbs, bAbs)) return 0;
  if (cNeg < 0) {
    cT = -cAbs;
    if (cT > 0) return 0;
    cTAbs = -cT;
    if (cTAbs != cAbs) return 0;
  } else {
    cT = cAbs;
    if (cT < 0) return 0;
    cTAbs = cT;
    if (cTAbs != cAbs) return 0;
  }
  *c = cT;
  return 1;
}

static inline int tryExactIntNegation(int *c, int a) {
  int r, aa;

  /* Handle zero */
  if (a == 0) {
    *c = a;
    return 1;
  }

  /* Try negation */
  r = -a;

  /* If r is zero or r and a have the same sign, something went
     wrong
  */
  if (r == 0) return 0;
  if (!((r > 0) ^ (a > 0))) return 0;

  /* Now we know that r and a have opposite signs. Handle now clamping. */
  aa = -r;

  /* If aa is zero or aa and r have the same sign, something went
     wrong
  */
  if (aa == 0) return 0;
  if (!((aa > 0) ^ (r > 0))) return 0;

  /* Now we know that aa and a have the same sign.

     If aa is not equal to a, there has been some clamping.
     Otherwise, r is the opposite of a.
  */
  if (aa != a) return 0;

  *c = r;
  return 1;
}

static inline int tryExactIntPower(int *c, int a, int b) {
  unsigned int n;
  int r, t;

  if (b < 0) return 0;
  if (b == 0) {
    *c = 1;
    return 1;
  }
  n = b;

  t = a;
  r = 1;
  while (n > 0u) {
    if (n & 1u) {
      if (!tryExactIntMultiplication(&r,r,t)) return 0;
    }
    n >>= 1;
    if (n > 0u) {
      if (!tryExactIntMultiplication(&t,t,t)) return 0;
    }
  }

  *c = r;
  return 1;
}

/* Computes c = a + b exactly, adapting the precision of c */
static inline void mpfr_add_exact(mpfr_t c, mpfr_t a, mpfr_t b) {
  mp_exp_t Ea, Eb;
  mp_prec_t pa, pb, pc;


  if ((!mpfr_number_p(a)) ||
      (!mpfr_number_p(b))) {
    mpfr_add(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, mpfr_get_prec(b));
    mpfr_set(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_zero_p(b)) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */

  Ea = mpfr_get_exp(a);
  Eb = mpfr_get_exp(b);
  pa = mpfr_get_prec(a);
  pb = mpfr_get_prec(b);

  /* The ulps are the following

     ulp_pa(a) = 2^(Ea - pa)
     ulp_pb(b) = 2^(Eb - pb)

     So the ulp of the sum must be

     ulp_pc(c) = 2^min(Ea - pa, Eb - pb)

     The exponent of the result c is bounded
     by

     Ec <= max(Ea, Eb) + 1

     The + 1 comes from the possible outgoing carry.

     So we need

     pc = max(Ea, Eb) - min(Ea - pa, Eb - pb) + 1 bits

     for c.

  */
  pc = (Ea > Eb? Ea : Eb) - (Ea - pa < Eb - pb ? Ea - pa : Eb - pb) + 1; /* Exponent overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the addition */
  mpfr_add(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}

/* Computes c = a + b exactly, adapting the precision of c */
static inline void mpfr_add_exact_int(mpfr_t c, mpfr_t a, int b) {
  mp_exp_t Ea, Eb;
  mp_prec_t pa, pb, pc;


  if (!mpfr_number_p(a)) {
    mpfr_add_si(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, 8 * sizeof(int) + 5);
    mpfr_set_si(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (b == 0) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */

  Ea = mpfr_get_exp(a);
  Eb = 8 * sizeof(int);
  pa = mpfr_get_prec(a);
  pb = 8 * sizeof(int);

  /* The ulps are the following

     ulp_pa(a) = 2^(Ea - pa)
     ulp_pb(b) = 2^(Eb - pb)

     So the ulp of the sum must be

     ulp_pc(c) = 2^min(Ea - pa, Eb - pb)

     The exponent of the result c is bounded
     by

     Ec <= max(Ea, Eb) + 1

     The + 1 comes from the possible outgoing carry.

     So we need

     pc = max(Ea, Eb) - min(Ea - pa, Eb - pb) + 1 bits

     for c.

  */
  pc = (Ea > Eb? Ea : Eb) - (Ea - pa < Eb - pb ? Ea - pa : Eb - pb) + 1; /* Exponent overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the addition */
  mpfr_add_si(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}


/* Computes c = a - b exactly, adapting the precision of c */
static inline void mpfr_sub_exact(mpfr_t c, mpfr_t a, mpfr_t b) {
  mp_exp_t Ea, Eb;
  mp_prec_t pa, pb, pc;


  if ((!mpfr_number_p(a)) ||
      (!mpfr_number_p(b))) {
    mpfr_sub(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, mpfr_get_prec(b));
    mpfr_neg(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_zero_p(b)) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */

  Ea = mpfr_get_exp(a);
  Eb = mpfr_get_exp(b);
  pa = mpfr_get_prec(a);
  pb = mpfr_get_prec(b);

  /* The ulps are the following

     ulp_pa(a) = 2^(Ea - pa)
     ulp_pb(b) = 2^(Eb - pb)

     So the ulp of the sum must be

     ulp_pc(c) = 2^min(Ea - pa, Eb - pb)

     The exponent of the result c is bounded
     by

     Ec <= max(Ea, Eb) + 1

     The + 1 comes from the possible outgoing carry.

     So we need

     pc = max(Ea, Eb) - min(Ea - pa, Eb - pb) + 1 bits

     for c.

  */
  pc = (Ea > Eb? Ea : Eb) - (Ea - pa < Eb - pb ? Ea - pa : Eb - pb) + 1; /* Exponent overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the subtraction */
  mpfr_sub(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}

/* Computes c = a - b exactly, adapting the precision of c */
static inline void mpfr_sub_exact_int(mpfr_t c, mpfr_t a, int b) {
  mp_exp_t Ea, Eb;
  mp_prec_t pa, pb, pc;


  if (!mpfr_number_p(a)) {
    mpfr_sub_si(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, 8 * sizeof(b) + 5);
    mpfr_set_si(c, b, GMP_RNDN); /* exact */
    mpfr_neg(c, c, GMP_RNDN); /* exact */
    return;
  }

  if (b == 0) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */

  Ea = mpfr_get_exp(a);
  Eb = 8 * sizeof(int);
  pa = mpfr_get_prec(a);
  pb = 8 * sizeof(int);

  /* The ulps are the following

     ulp_pa(a) = 2^(Ea - pa)
     ulp_pb(b) = 2^(Eb - pb)

     So the ulp of the sum must be

     ulp_pc(c) = 2^min(Ea - pa, Eb - pb)

     The exponent of the result c is bounded
     by

     Ec <= max(Ea, Eb) + 1

     The + 1 comes from the possible outgoing carry.

     So we need

     pc = max(Ea, Eb) - min(Ea - pa, Eb - pb) + 1 bits

     for c.

  */
  pc = (Ea > Eb? Ea : Eb) - (Ea - pa < Eb - pb ? Ea - pa : Eb - pb) + 1; /* Exponent overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the subtraction */
  mpfr_sub_si(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}

static inline void mpfr_int_sub_exact(mpfr_t c, int a, mpfr_t b) {
  mpfr_sub_exact_int(c, b, a);
  mpfr_neg(c, c, GMP_RNDN); /* exact */
}

/* Computes c = a * b exactly, adapting the precision of c */
static inline void mpfr_mul_exact(mpfr_t c, mpfr_t a, mpfr_t b) {
  mp_prec_t pa, pb, pc;

  if ((!mpfr_number_p(a)) ||
      (!mpfr_number_p(b))) {
    mpfr_mul(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_zero_p(b)) {
    mpfr_set_prec(c, mpfr_get_prec(b));
    mpfr_set(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_cmp_si(a,1) == 0) {
    mpfr_set_prec(c, mpfr_get_prec(b));
    mpfr_set(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_cmp_si(b,1) == 0) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */
  pa = mpfr_get_prec(a);
  pb = mpfr_get_prec(b);

  /* Need pc = pa + pb bits of precision for an exact product. */
  pc = pa + pb; /* Precision overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the multiplication */
  mpfr_mul(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}

/* Computes c = a * b exactly, adapting the precision of c */
static inline void mpfr_mul_exact_int(mpfr_t c, mpfr_t a, int b) {
  mp_prec_t pa, pb, pc;

  if (!mpfr_number_p(a)) {
    mpfr_mul_si(c, a, b, GMP_RNDN); /* Producing NaN or Inf */
    return;
  }

  if (mpfr_zero_p(a)) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  if (b == 0) {
    mpfr_set_prec(c, 8 * sizeof(int) + 5);
    mpfr_set_si(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (mpfr_cmp_si(a,1) == 0) {
    mpfr_set_prec(c, 8 * sizeof(int) + 5);
    mpfr_set_si(c, b, GMP_RNDN); /* exact */
    return;
  }

  if (b == 1) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return;
  }

  /* Here, both inputs are non-zero numbers */
  pa = mpfr_get_prec(a);
  pb = 8 * sizeof(int) + 5;

  /* Need pc = pa + pb bits of precision for an exact product. */
  pc = pa + pb; /* Precision overflow possible */
  mpfr_set_prec(c, pc);

  /* Perform the multiplication */
  mpfr_mul_si(c, a, b, GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
}

static inline int try_mpfr_pow_exact(mpfr_t c, mpfr_t a, unsigned long int b) {
  mp_prec_t pa, pc, paR;
  unsigned int paI, pcI, pcII;
  unsigned int bI;
  unsigned long int bb;

  /* Special input NaN or Inf */
  if (!mpfr_number_p(a)) {
    /* Producing NaN, Inf or 1 */
    mpfr_set_prec(c,12);
    mpfr_pow_ui(c,a,b,GMP_RNDN);
    return 1;
  }

  /* Here, a is a real number.

     Handle the easy cases b = 0 and b = 1.

  */
  if (b == 0) {
    mpfr_set_prec(c, 12);
    mpfr_set_si(c, 1, GMP_RNDN); /* exact */
    return 1;
  }

  if (b == 1) {
    mpfr_set_prec(c, mpfr_get_prec(a));
    mpfr_set(c, a, GMP_RNDN); /* exact */
    return 1;
  }

  /* It's so easy to handle b = 2, so let's do that */
  if (b == 2) {
    mpfr_set_prec(c, 2 * mpfr_get_prec(a));
    mpfr_mul(c, a, a, GMP_RNDN); /* exact */
    pc = mpfr_min_prec(c);
    if (pc < 12) pc = 12;
    mpfr_prec_round(c, pc, GMP_RNDN); /* exact */
    return 1;
  }

  /* Let's see if we can represent b on an unsigned int bI.
     If we can't, we refuse to do the job.
  */
  bI = b;
  bb = bI;
  if (bb != b) return 0;

  /* Handle the general case */
  pa = mpfr_get_prec(a);
  if (pa <= 1) return 0;

  /* pa >= 2 > 0 */
  paI = pa;
  paR = paI;
  if (paR != pa) return 0;

  /* Now we know that a is on paI bits, where paI is an unsigned int

     At most, we need paI * bI bits of precision to represent a^bI.

     We try if we can represent paI * b on an unsigned int. If we
     can't, we refuse to do the job.

  */
  if (!tryExactUnsignedIntMultiplication(&pcI, paI, bI)) return 0;

  /* Now we know that we need pcI bits. We check if we can write pcI
     on an mp_prec_t. We first make sure that pcI is at least 12.
  */
  if (pcI < 12u) pcI = 12u;
  pc = pcI;
  if (pc < 12) pc = 12;
  pcII = pc;
  if (pcII != pcI) return 0;

  /* Now we know that we need pc bits for a^b */
  mpfr_set_prec(c, pc);
  mpfr_pow_ui(c,a,b,GMP_RNDN); /* exact */

  /* It could be that we actually don't need all the precision of c to
     keep the result. So we reduce that precision to the minimum.
  */
  pc = mpfr_min_prec(c);
  if (pc < 12) pc = 12;

  /* Adjust the precision of c again. */
  mpfr_prec_round(c, pc, GMP_RNDN); /* exact */

  return 1;
}

/* Part for constants */

static inline constant_t __polynomialGetIthCoefficientAsConstantIntIndex(polynomial_t, int);
static inline constant_t __polynomialGetIthCoefficientAsConstant(polynomial_t, mpz_t);

static inline void constantFree(constant_t);
static inline constant_t constantFromCopy(constant_t);
static inline void constantEvalMpfi(sollya_mpfi_t, constant_t);
static inline constant_t constantAdd(constant_t, constant_t);
static inline constant_t constantSub(constant_t, constant_t);
static inline constant_t constantMul(constant_t, constant_t);
static inline constant_t constantDiv(constant_t, constant_t);
static inline constant_t constantPow(constant_t, constant_t);
static inline constant_t constantNeg(constant_t);

static inline int constantIsNonNegativeInteger(constant_t, int);
static inline int tryConstantToUnsignedInt(unsigned int *, constant_t);
static inline int tryConstantToMpz(mpz_t, constant_t);
static inline int constantIsGreater(constant_t, constant_t, int);

static inline void __constantLink(constant_t c) {
  if (c == NULL) return;
  c->next = __constant_linked_list;
  __constant_linked_list = c;
  c->prev = NULL;
  if (c->next != NULL) {
    c->next->prev = c;
  }
}

static inline void __constantUnlink(constant_t c) {
  if (c == NULL) return;
  if (c == __constant_linked_list) {
    __constant_linked_list = c->next;
  }
  if (c->prev != NULL) {
    c->prev->next = c->next;
  }
  if (c->next != NULL) {
    c->next->prev = c->prev;
  }
}

static inline void __constantCheckEmptyLinkedList() {
  constant_t curr;
  node *tmp;
  
  if (__constant_linked_list == NULL) return;
  while (__constant_linked_list != NULL) {
    for (curr=__constant_linked_list;curr!=NULL;curr=curr->next) {
      if (curr->expressionCache.cached && (curr->expressionCache.res != NULL)) 
	break;
    }
    if (curr == NULL)
      break;
    if (!(curr->expressionCache.cached))
      break;
    if (curr->expressionCache.res == NULL)
      break;
    tmp = curr->expressionCache.res;
    curr->expressionCache.res = NULL;
    curr->expressionCache.cached = 0;
    freeThing(tmp);
  }
}

static inline void constantInitializeCaches() {
  int i;

  __sollyaRecycleInitializeCaches();
  
  if (__constant_cache_initialized) return;
  for (i=0;i<CONSTANT_INTEGER_CACHE_SIZE;i++) {
    __constant_integer_cache[i].constant = NULL;
    __constant_integer_cache[i].initialized = 0;
  }
  __constant_malloc_cache_index = 0;
  __constant_cache_initialized = 1;
}

static inline void constantFreeCaches() {
  int i;
  
  if (!__constant_cache_initialized) {
    __sollyaRecycleFreeCaches();
    return;
  }
  __constantCheckEmptyLinkedList();
  for (i=0;i<CONSTANT_INTEGER_CACHE_SIZE;i++) {
    if (__constant_integer_cache[i].initialized) {
      constantFree(__constant_integer_cache[i].constant);
      __constant_integer_cache[i].initialized = 0;
    }
  }
  __constantCheckEmptyLinkedList();
  for (i=0;i<__constant_malloc_cache_index;i++) {
    safeFree(__constant_malloc_cache[i]);
  }
  __constant_malloc_cache_index = 0;
  __constant_cache_initialized = 0;
  __sollyaRecycleFreeCaches();
  __constant_linked_list = NULL;
}

static inline constant_t __constantAllocate() {
  constant_t res;
  
  constantInitializeCaches();
  if (__constant_cache_initialized) {
    if ((0 < __constant_malloc_cache_index) && (__constant_malloc_cache_index <= CONSTANT_MALLOC_CACHE_SIZE)) {
      __constant_malloc_cache_index--;
      res = __constant_malloc_cache[__constant_malloc_cache_index];
      __constantLink(res);
      return res;
    }
  }
  res = (constant_t) safeMalloc(sizeof(struct __constant_struct_t));
  __constantLink(res);
  return res;
}

static inline void __constantFreeMem(constant_t c) {
  constantInitializeCaches();
  __constantUnlink(c);
  if (__constant_cache_initialized) {
    if ((0 <= __constant_malloc_cache_index) && (__constant_malloc_cache_index < CONSTANT_MALLOC_CACHE_SIZE)) {
      __constant_malloc_cache[__constant_malloc_cache_index] = c;
      __constant_malloc_cache_index++;
      return;
    }
  }
  safeFree(c);
}

static inline constant_t __constantAllocatePostTreatment(constant_t c) {
  return c;
}

static inline constant_t constantFromInt(int c) {
  constant_t res;

  if ((CONSTANT_INTEGER_CACHE_MIN <= c) && (c <= CONSTANT_INTEGER_CACHE_MAX)) {
    constantInitializeCaches();
    if (__constant_integer_cache[c - (CONSTANT_INTEGER_CACHE_MIN)].initialized) {
      return constantFromCopy(__constant_integer_cache[c - (CONSTANT_INTEGER_CACHE_MIN)].constant);
    }
  }

  res = __constantAllocate();
  res->refCount = 1;
  res->isZero.cached = 0;
  res->isOne.cached = 0;
  res->isMinusOne.cached = 0;
  res->isNonNegativeInteger.cached = 0;
  res->isPositive.cached = 0;
  res->isDyadic.cached = 0;
  res->isRational.cached = 0;
  res->isInteger.cached = 0;
  res->type = INTEGER;
  res->value.integer = c;
  res->hash.hasHash = 0;
  res->expressionCache.cached = 0;
  res->expressionCache.res = NULL;
  res = __constantAllocatePostTreatment(res);

  if ((CONSTANT_INTEGER_CACHE_MIN <= c) && (c <= CONSTANT_INTEGER_CACHE_MAX)) {
    constantInitializeCaches();
    if (!(__constant_integer_cache[c - (CONSTANT_INTEGER_CACHE_MIN)].initialized)) {
      __constant_integer_cache[c - (CONSTANT_INTEGER_CACHE_MIN)].constant = constantFromCopy(res);
      __constant_integer_cache[c - (CONSTANT_INTEGER_CACHE_MIN)].initialized = 1;
    }
  }

  return res;
}

static inline mp_prec_t mpfr_get_needed_prec(mpfr_t c) {
  mp_prec_t p;

  p = mpfr_min_prec(c);
  if (p < ((mp_prec_t) 12)) p = ((mp_prec_t) 12);

  return p;
}

static inline constant_t constantFromMpfr(mpfr_t c) {
  constant_t res;
  int intval;

  if (mpfr_is_machine_integer(&intval, c)) {
    return constantFromInt(intval);
  }

  res = __constantAllocate();
  res->refCount = 1;
  res->isZero.cached = 0;
  res->isOne.cached = 0;
  res->isMinusOne.cached = 0;
  res->isNonNegativeInteger.cached = 0;
  res->isPositive.cached = 0;
  res->isDyadic.cached = 0;
  res->isRational.cached = 0;
  res->isInteger.cached = 0;
  res->hash.hasHash = 0;
  res->expressionCache.cached = 0;
  res->expressionCache.res = NULL;
  res->type = MPFR;
  __sollyaRecycleMpfrInit2(res->value.mpfr, mpfr_get_needed_prec(c));
  mpfr_set(res->value.mpfr, c, GMP_RNDN); /* exact, enough precision */

  return __constantAllocatePostTreatment(res);
}

static inline constant_t constantFromUnsignedInt(unsigned int c) {
  constant_t res;
  int cI;
  unsigned int cc;
  mpfr_t t;

  cI = c;
  cc = cI;
  if ((cI < 0) || (cc != c)) {
    __sollyaRecycleMpfrInit2(t, 8 * sizeof(unsigned int) + 5);
    mpfr_set_ui(t, c, GMP_RNDN); /* exact */
    res = constantFromMpfr(t);
    __sollyaRecycleMpfrClear(t);
    return res;
  }

  return constantFromInt(cI);
}

/* The argument c must be a canonicalized mpq_t number, i.e. zero
   needs to be represented as 0/1, the numerator and the denominator
   of c must not have any common factor and the denominator must be
   positive.
*/
static inline constant_t constantFromScaledMpq(mp_exp_t e, mpq_t c) {
  mpz_t num, den;
  constant_t res;
  mp_bitcnt_t dyadNum, dyadDen;
  mp_exp_t expo;
  mp_prec_t p;
  mpfr_t mpfrval;

  if (mpq_sgn(c) == 0) {
    return constantFromInt(0);
  }

  __sollyaRecycleMpzInit(num);
  __sollyaRecycleMpzInit(den);
  mpq_get_num(num, c);
  mpq_get_den(den, c);
  dyadNum = mpz_scan1(num, 0);
  dyadDen = mpz_scan1(den, 0);
  mpz_tdiv_q_2exp(num, num, dyadNum);
  mpz_tdiv_q_2exp(den, den, dyadDen);
  expo = e + dyadNum - dyadDen; /* Exponent overflow possible */

  if (mpz_cmp_si(den, 1) == 0) {
    /* The denominator is one, so we can actually
       use a MPFR (or integer) representation.
    */
    p = mpz_sizeinbase(num, 2);
    if (p < 12) p = 12;
    __sollyaRecycleMpfrInit2(mpfrval, p);
    mpfr_set_z_2exp(mpfrval, num, expo, GMP_RNDN); /* exact as enough precision */
    if (mpfr_number_p(mpfrval)) {
      res = constantFromMpfr(mpfrval);
      __sollyaRecycleMpfrClear(mpfrval);
      __sollyaRecycleMpzClear(den);
      __sollyaRecycleMpzClear(num);
      return res;
    } else {
      __sollyaRecycleMpfrClear(mpfrval);
    }
  }

  /* Here, we are sure we must use a scaled MPQ representation */
  res = __constantAllocate();
  res->refCount = 1;
  res->isZero.cached = 0;
  res->isOne.cached = 0;
  res->isMinusOne.cached = 0;
  res->isNonNegativeInteger.cached = 0;
  res->isPositive.cached = 0;
  res->isDyadic.cached = 0;
  res->isRational.cached = 0;
  res->isInteger.cached = 0;
  res->type = SCALEDMPQ;
  res->value.scaledMpq.expo = expo;
  res->hash.hasHash = 0;
  res->expressionCache.cached = 0;
  res->expressionCache.res = NULL;
  __sollyaRecycleMpqInit(res->value.scaledMpq.significand);
  mpq_set_num(res->value.scaledMpq.significand, num);
  mpq_set_den(res->value.scaledMpq.significand, den);
  __sollyaRecycleMpzClear(den);
  __sollyaRecycleMpzClear(num);

  return __constantAllocatePostTreatment(res);
}

static inline constant_t constantFromMpq(mpq_t c) {
  return constantFromScaledMpq((mp_exp_t) 0, c);
}

static inline constant_t constantFromMpz(mpz_t c) {
  mpq_t q;
  constant_t res;

  __sollyaRecycleMpqInit(q);
  mpq_set_z(q, c);
  mpq_canonicalize(q);
  res = constantFromMpq(q);
  __sollyaRecycleMpqClear(q);

  return res;
}

static inline constant_t constantFromBinomialUnsignedInt(unsigned int n, unsigned int i) {
  mpz_t bin;
  constant_t res;

  __sollyaRecycleMpzInit(bin);
  mpz_bin_uiui(bin, n, i);
  res = constantFromMpz(bin);
  __sollyaRecycleMpzClear(bin);

  return res;
}

static inline int __constantFromBinomialConstantAndUnsignedIntUnsafe(constant_t *res, constant_t n, unsigned int k) {
  mpz_t nz, bin;

  __sollyaRecycleMpzInit(nz);
  if (!tryConstantToMpz(nz, n)) {
    __sollyaRecycleMpzClear(nz);
    return 0;
  }

  __sollyaRecycleMpzInit(bin);
  mpz_bin_ui(bin, nz, k);
  *res = constantFromMpz(bin);
  __sollyaRecycleMpzClear(bin);
  __sollyaRecycleMpzClear(nz);
  return 1;
}

static inline int __constantFromBinomialUnsafe(constant_t *bin, constant_t n, constant_t k) {
  unsigned int nu, ku, ju;
  constant_t j;

  /* Check whether both n and k are representable as unsigned machine
     integers.
  */

  if (tryConstantToUnsignedInt(&nu, n) &&
      tryConstantToUnsignedInt(&ku, k)) {
    /* Here we can use the special function on machine integers. */
    *bin = constantFromBinomialUnsignedInt(nu, ku);
    return 1;
  }

  /* Here, at least one of n or k is not representable as an unsigned
     machine integer.

     Continue by checking whether at least k is representable as an
     unsigned machine integer.

  */
  if (tryConstantToUnsignedInt(&ku, k)) {
    /* Use the special function for bin(n, k) with k unsigned int */
    return __constantFromBinomialConstantAndUnsignedIntUnsafe(bin, n, ku);
  }

  /* Compute j = n - k and check if j (and n) are representable as
     unsigned machine integers.
  */
  j = constantSub(n, k);
  if (!constantIsNonNegativeInteger(j, 0)) {
    constantFree(j);
    return 0;
  }

  if (!tryConstantToUnsignedInt(&ju, j)) {
    constantFree(j);
    return 0;
  }
  constantFree(j);

  if (tryConstantToUnsignedInt(&nu, n)) {
    *bin = constantFromBinomialUnsignedInt(nu, ju);
    return 1;
  }

  return __constantFromBinomialConstantAndUnsignedIntUnsafe(bin, n, ju);
}

static inline int constantFromBinomial(constant_t *bin, constant_t n, constant_t k) {

  /* Handle stupid input */
  if (n == NULL) return 0;
  if (k == NULL) return 0;

  /* Check if n and k are both non-negative integers and if k is no
     greater than n.
  */
  if (constantIsNonNegativeInteger(n, 0) &&
      constantIsNonNegativeInteger(k, 0) &&
      (!constantIsGreater(k, n, 1))) {
    /* Both n and k are non-negative integers.

       Use an auxilliary function and return its return value.
    */
    return __constantFromBinomialUnsafe(bin, n, k);
  }

  /* Here one of n or k is negative or no integer. Signal failure. */
  return 0;
}

static inline constant_t constantFromCopy(constant_t c) {
  if (c == NULL) return NULL;
  c->refCount++;
  return c;
}

static inline constant_t constantFromExpression(node *c) {
  node *simplified, *copy;
  mpq_t rational;
  constant_t res;

  if (c == NULL) return NULL;
  if (!isConstant(c)) return NULL;
  if ((c->nodeType == MEMREF) &&
      (c->cache->polynomialRepresentation != NULL)) {
    /* The expression is a polynomial but is constant. The constant of
       degree 0 does the job
    */
    return __polynomialGetIthCoefficientAsConstantIntIndex(c->cache->polynomialRepresentation, 0);
  }
  if (accessThruMemRef(c)->nodeType == CONSTANT) {
    return constantFromMpfr(*(accessThruMemRef(c)->value));
  }
  __sollyaRecycleMpqInit(rational);
  if (tryEvaluateConstantTermToMpq(rational, c)) {
    mpq_canonicalize(rational);
    res = constantFromMpq(rational);
    __sollyaRecycleMpqClear(rational);
    return res;
  }
  __sollyaRecycleMpqClear(rational);
  simplified = simplifyRationalErrorfree(c);
  if (accessThruMemRef(simplified)->nodeType == CONSTANT) {
    res = constantFromMpfr(*(accessThruMemRef(simplified)->value));
    freeThing(simplified);
    return res;
  }
  if (simplified == c) {
    copy = addMemRef(copyThing(accessThruMemRef(simplified)));
    tryCopyTreeAnnotations(copy, simplified);
    freeThing(simplified);
    simplified = copy;
  }

  res = __constantAllocate();
  res->refCount = 1;
  res->isZero.cached = 0;
  res->isOne.cached = 0;
  res->isMinusOne.cached = 0;
  res->isNonNegativeInteger.cached = 0;
  res->isPositive.cached = 0;
  res->isDyadic.cached = 0;
  res->isRational.cached = 0;
  res->isInteger.cached = 0;
  res->type = EXPRESSION;
  res->value.expr = simplified;
  res->hash.hasHash = 0;
  res->expressionCache.cached = 0;
  res->expressionCache.res = NULL;

  return __constantAllocatePostTreatment(res);
}


static inline void constantFree(constant_t c) {
  if (c == NULL) return;
  c->refCount--;
  if (c->refCount > 0u) return;
  if (c->expressionCache.cached) {
    freeThing(c->expressionCache.res);
    c->expressionCache.cached = 0;
    c->expressionCache.res = NULL;
  }
  switch (c->type) {
  case INTEGER:
    break;
  case EXPRESSION:
    freeThing(c->value.expr);
    break;
  case MPFR:
    __sollyaRecycleMpfrClear(c->value.mpfr);
    break;
  case SCALEDMPQ:
    __sollyaRecycleMpqClear(c->value.scaledMpq.significand);
    break;
  }
  __constantFreeMem(c);
}

static inline int constantIsZero(constant_t a, int defVal) {
  int s;

  if (a == NULL) return defVal;
  if (a->isZero.cached) return a->isZero.res;
  if (a->isOne.cached && a->isOne.res) return 0;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 0;
  if (a->isPositive.cached && a->isPositive.res) return 0;
  if (a->isInteger.cached && (!a->isInteger.res)) return 0;
  if (a->isNonNegativeInteger.cached && (!a->isNonNegativeInteger.res)) return 0;
  switch (a->type) {
  case INTEGER:
    a->isZero.cached = 1;
    a->isZero.res = (a->value.integer == 0);
    return a->isZero.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isZero.cached = 1;
      a->isZero.res = mpfr_zero_p(*(accessThruMemRef(a->value.expr)->value));
      return a->isZero.res;
    }
    if (evaluateSignFast(&s, a->value.expr)) {
      if (s == 0) {
	a->isZero.cached = 1;
	a->isZero.res = 1;
	return a->isZero.res;
      } else {
	a->isZero.cached = 1;
	a->isZero.res = 0;
	return a->isZero.res;
      }
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isZero.cached = 1;
    a->isZero.res = mpfr_zero_p(a->value.mpfr);
    return a->isZero.res;
    break;
  case SCALEDMPQ:
    a->isZero.cached = 1;
    a->isZero.res = (mpq_sgn(a->value.scaledMpq.significand) == 0);
    return a->isZero.res;
    break;
  }
  return defVal;
}

static inline int constantIsOne(constant_t a, int defVal) {
  int s;

  if (a == NULL) return defVal;
  if (a->isOne.cached) return a->isOne.res;
  if (a->isZero.cached && a->isZero.res) return 0;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 0;
  if (a->isInteger.cached && (!a->isInteger.res)) return 0;
  if (a->isNonNegativeInteger.cached && (!a->isNonNegativeInteger.res)) return 0;
  if (a->isPositive.cached && (!a->isPositive.res)) return 0;
  switch (a->type) {
  case INTEGER:
    a->isOne.cached = 1;
    a->isOne.res = (a->value.integer == 1);
    return a->isOne.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isOne.cached = 1;
      a->isOne.res = (mpfr_cmp_si(*(accessThruMemRef(a->value.expr)->value),1) == 0);
      return a->isOne.res;
    }
    if (evaluateSignFast(&s, a->value.expr)) {
      if (s <= 0) {
	a->isOne.cached = 1;
	a->isOne.res = 0;
	return a->isOne.res;
      }
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isOne.cached = 1;
    a->isOne.res = (mpfr_cmp_si(a->value.mpfr,1) == 0);
    return a->isOne.res;
    break;
  case SCALEDMPQ:
    /* We have to compare 2^E * n/d == 1. This means checking 2^E * n == d */
    if (mpq_sgn(a->value.scaledMpq.significand) <= 0) {
      a->isOne.cached = 1;
      a->isOne.res = 0;
      return a->isOne.res;
    }
    if (a->value.scaledMpq.expo == ((mp_exp_t) 0)) {
      /* Here, E = 0, so we have to check whether n == d */
      a->isOne.cached = 1;
      if (mpz_cmp(mpq_numref(a->value.scaledMpq.significand),
		  mpq_denref(a->value.scaledMpq.significand)) == 0) {
	a->isOne.res = 1;
      } else {
	a->isOne.res = 0;
      }
      return a->isOne.res;
    }
    /* Here E != 0 */
    a->value.scaledMpq.expo += mpq_remove_powers_of_two(a->value.scaledMpq.significand); /* Exponent overflow possible */
    if (a->value.scaledMpq.expo != ((mp_exp_t) 0)) {
      a->isOne.cached = 1;
      a->isOne.res = 0;
      return a->isOne.res;
    }
    if (mpz_cmp(mpq_numref(a->value.scaledMpq.significand),
		mpq_denref(a->value.scaledMpq.significand)) == 0) {
      a->isOne.cached = 1;
    } else {
      a->isOne.cached = 0;
    }
    return a->isOne.res;
    break;
  }
  return defVal;
}

static inline int constantIsMinusOne(constant_t a, int defVal) {
  int s;
  mpz_t t;

  if (a == NULL) return defVal;
  if (a->isMinusOne.cached) return a->isMinusOne.res;
  if (a->isZero.cached && a->isZero.res) return 0;
  if (a->isOne.cached && a->isOne.res) return 0;
  if (a->isInteger.cached && (!a->isInteger.res)) return 0;
  if (a->isNonNegativeInteger.cached && a->isNonNegativeInteger.res) return 0;
  if (a->isPositive.cached && a->isPositive.res) return 0;
  switch (a->type) {
  case INTEGER:
    a->isMinusOne.cached = 1;
    a->isMinusOne.res = (a->value.integer == -1);
    return a->isMinusOne.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isMinusOne.cached = 1;
      a->isMinusOne.res = (mpfr_cmp_si(*(accessThruMemRef(a->value.expr)->value),-1) == 0);
      return a->isMinusOne.res;
    }
    if (evaluateSignFast(&s, a->value.expr)) {
      if (s >= 0) {
	a->isMinusOne.cached = 1;
	a->isMinusOne.res = 0;
	return a->isMinusOne.res;
      }
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isMinusOne.cached = 1;
    a->isMinusOne.res = (mpfr_cmp_si(a->value.mpfr,-1) == 0);
    return a->isMinusOne.res;
    break;
  case SCALEDMPQ:
    /* We have to compare 2^E * n/d == -1. This means checking 2^E * n == -d */
    if (mpq_sgn(a->value.scaledMpq.significand) >= 0) {
      a->isMinusOne.cached = 1;
      a->isMinusOne.res = 0;
      return a->isMinusOne.res;
    }
    if (a->value.scaledMpq.expo == ((mp_exp_t) 0)) {
      /* Here, E = 0, so we have to check whether n == -d */
      a->isMinusOne.cached = 1;
      __sollyaRecycleMpzInit(t);
      mpz_neg(t, mpq_denref(a->value.scaledMpq.significand));
      if (mpz_cmp(mpq_numref(a->value.scaledMpq.significand), t) == 0) {
	a->isMinusOne.res = 1;
      } else {
	a->isMinusOne.res = 0;
      }
      __sollyaRecycleMpzClear(t);
      return a->isMinusOne.res;
    }
    /* Here E != 0 */
    a->value.scaledMpq.expo += mpq_remove_powers_of_two(a->value.scaledMpq.significand); /* Exponent overflow possible */
    if (a->value.scaledMpq.expo != ((mp_exp_t) 0)) {
      a->isMinusOne.cached = 1;
      a->isMinusOne.res = 0;
      return a->isMinusOne.res;
    }
    __sollyaRecycleMpzInit(t);
    mpz_neg(t, mpq_denref(a->value.scaledMpq.significand));
    if (mpz_cmp(mpq_numref(a->value.scaledMpq.significand), t) == 0) {
      a->isMinusOne.cached = 1;
    } else {
      a->isMinusOne.cached = 0;
    }
    __sollyaRecycleMpzClear(t);
    return a->isMinusOne.res;
    break;
  }
  return defVal;
}

static inline int constantIsNonNegativeInteger(constant_t a, int defVal) {
  int s;
  node *t, *d;

  if (a == NULL) return defVal;
  if (a->isNonNegativeInteger.cached) return a->isNonNegativeInteger.res;
  if (a->isZero.cached && a->isZero.res) return 1;
  if (a->isOne.cached && a->isOne.res) return 1;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 0;
  if (a->isInteger.cached && (!a->isInteger.res)) return 0;
  if (a->isPositive.cached && a->isZero.cached &&
      (!(a->isPositive.res || a->isZero.res))) return 0;
  switch (a->type) {
  case INTEGER:
    a->isNonNegativeInteger.cached = 1;
    a->isNonNegativeInteger.res = (a->value.integer >= 0);
    return a->isNonNegativeInteger.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isNonNegativeInteger.cached = 1;
      a->isNonNegativeInteger.res = ((mpfr_sgn(*(accessThruMemRef(a->value.expr)->value)) >= 0) &&
				     mpfr_integer_p(*(accessThruMemRef(a->value.expr)->value)));
      return a->isNonNegativeInteger.res;
    }
    if (!evaluateSignFast(&s, a->value.expr)) return defVal;
    if (s < 0) {
      a->isNonNegativeInteger.cached = 1;
      a->isNonNegativeInteger.res = 0;
      return a->isNonNegativeInteger.res;
    }
    t = addMemRef(makeSub(copyThing(a->value.expr),
			  addMemRef(makeNearestInt(copyThing(a->value.expr)))));
    d = simplifyRationalErrorfree(t);
    freeThing(t);
    if (!evaluateSignFast(&s, d)) {
      freeThing(d);
      return defVal;
    }
    freeThing(d);
    a->isNonNegativeInteger.cached = 1;
    a->isNonNegativeInteger.res = (s == 0);
    return a->isNonNegativeInteger.res;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isNonNegativeInteger.cached = 1;
    a->isNonNegativeInteger.res = (mpfr_integer_p(a->value.mpfr) &&
				   (mpfr_sgn(a->value.mpfr) >= 0));
    return a->isNonNegativeInteger.res;
    break;
  case SCALEDMPQ:
    if (mpq_sgn(a->value.scaledMpq.significand) < 0) return 0;
    a->isNonNegativeInteger.cached = 1;
    a->isNonNegativeInteger.res = scaledMpqIsInteger(a->value.scaledMpq.expo,
						     a->value.scaledMpq.significand);
    return a->isNonNegativeInteger.res;
    break;
  }

  return defVal;
}

static inline int constantIsInteger(constant_t a, int defVal) {
  int s;
  node *t, *d;

  if (a == NULL) return defVal;
  if (a->isInteger.cached) return a->isInteger.res;
  if (a->isZero.cached && a->isZero.res) return 1;
  if (a->isOne.cached && a->isOne.res) return 1;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 1;
  if (a->isNonNegativeInteger.cached && a->isNonNegativeInteger.res) return 1;
  switch (a->type) {
  case INTEGER:
    a->isInteger.cached = 1;
    a->isInteger.res = 1;
    return a->isInteger.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isInteger.cached = 1;
      a->isInteger.res = (mpfr_integer_p(*(accessThruMemRef(a->value.expr)->value)));
      return a->isInteger.res;
    }
    t = addMemRef(makeSub(copyThing(a->value.expr),
			  addMemRef(makeNearestInt(copyThing(a->value.expr)))));
    d = simplifyRationalErrorfree(t);
    freeThing(t);
    if (!evaluateSignFast(&s, d)) {
      freeThing(d);
      return defVal;
    }
    freeThing(d);
    a->isInteger.cached = 1;
    a->isInteger.res = (s == 0);
    return a->isInteger.res;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isInteger.cached = 1;
    a->isInteger.res = mpfr_integer_p(a->value.mpfr);
    return a->isInteger.res;
    break;
  case SCALEDMPQ:
    a->isInteger.cached = 1;
    a->isInteger.res = scaledMpqIsInteger(a->value.scaledMpq.expo,
					  a->value.scaledMpq.significand);
    return a->isInteger.res;
    break;
  }

  return defVal;
}

static inline int constantIsDyadic(constant_t a, int defVal) {
  if (a == NULL) return defVal;
  if (a->isDyadic.cached) return a->isDyadic.res;
  if (a->isRational.cached && (!a->isRational.res)) return 0;
  if (a->isZero.cached && a->isZero.res) return 1;
  if (a->isOne.cached && a->isOne.res) return 1;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 1;
  if (a->isInteger.cached && a->isInteger.res) return 1;
  if (a->isNonNegativeInteger.cached && a->isNonNegativeInteger.res) return 1;
  switch (a->type) {
  case INTEGER:
    a->isDyadic.cached = 1;
    a->isDyadic.res = 1;
    return a->isDyadic.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isDyadic.cached = 1;
      a->isDyadic.res = 1;
      return a->isDyadic.res;
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isDyadic.cached = 1;
    a->isDyadic.res = 1;
    return a->isDyadic.res;
    break;
  case SCALEDMPQ:
    a->isDyadic.cached = 1;
    a->isDyadic.res = scaledMpqIsDyadic(a->value.scaledMpq.expo,
					a->value.scaledMpq.significand);
    return a->isDyadic.res;
    break;
  }
  return defVal;
}

static inline int constantIsRational(constant_t a, int defVal) {
  if (a == NULL) return defVal;
  if (a->isRational.cached) return a->isRational.res;
  if (a->isDyadic.cached && a->isDyadic.res) return 1;
  if (a->isZero.cached && a->isZero.res) return 1;
  if (a->isOne.cached && a->isOne.res) return 1;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 1;
  if (a->isInteger.cached && a->isInteger.res) return 1;
  if (a->isNonNegativeInteger.cached && a->isNonNegativeInteger.res) return 1;
  switch (a->type) {
  case INTEGER:
    a->isRational.cached = 1;
    a->isRational.res = 1;
    return a->isRational.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isRational.cached = 1;
      a->isRational.res = 1;
      return a->isRational.res;
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isRational.cached = 1;
    a->isRational.res = 1;
    return a->isRational.res;
    break;
  case SCALEDMPQ:
    a->isRational.cached = 1;
    a->isRational.res = 1;
    return a->isRational.res;
    break;
  }
  return defVal;
}

static inline int constantHoldsOnPrecBits(constant_t a, mp_prec_t prec, int defVal) {
  sollya_mpfi_t t;
  int res;

  if (a == NULL) return defVal;
  if (prec < 2) return defVal;

  __sollyaRecycleMpfiInit2(t, prec + 15);
  constantEvalMpfi(t, a);
  if (sollya_mpfi_has_nan(t) ||
      sollya_mpfi_has_infinity(t)) {
    res = defVal;
  } else {
    res = sollya_mpfi_is_point_and_real(t);
  }
  __sollyaRecycleMpfiClear(t);

  return res;
}

static inline int constantIsEqual(constant_t a, constant_t b, int defVal) {
  constant_t d;
  int res, sa, sb;
  mpq_t t;
  mp_exp_t G;

  /* Trivial answers */
  if (a == NULL) return defVal;
  if (b == NULL) return defVal;
  if (a == b) return 1;

  /* Using a hash here is actually a bad idea as we are talking about
     mathematical identity not the structural one.
  */

  if (a->type != b->type) {
    /* If the constants are not of the same type, compute their
       difference and compare to zero.
    */
    d = constantSub(a, b);
    res = constantIsZero(d, defVal);
    constantFree(d);
    return res;
  }

  /* Here a and b are of the same type */
  switch (a->type) {
  case INTEGER:
    return (a->value.integer == b->value.integer);
    break;
  case EXPRESSION:
    if ((accessThruMemRef(a->value.expr)->nodeType == CONSTANT) &&
	(accessThruMemRef(b->value.expr)->nodeType == CONSTANT)) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      if (!mpfr_number_p(*(accessThruMemRef(b->value.expr)->value))) return defVal;
      return mpfr_equal_p(*(accessThruMemRef(a->value.expr)->value),
			  *(accessThruMemRef(b->value.expr)->value));
    }
    if (evaluateSignFast(&sa, a->value.expr) &&
	evaluateSignFast(&sb, b->value.expr)) {
      if ((sa == 0) && (sb == 0)) {
	/* If both expressions are zero, they are equal */
	return 1;
      }
      if (sa * sb <= 0) {
	/* If the expressions are of different sign, they are not equal */
	return 0;
      }
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    if (!mpfr_number_p(b->value.mpfr)) return defVal;
    return mpfr_equal_p(a->value.mpfr,b->value.mpfr);
    break;
  case SCALEDMPQ:
    sa = mpq_sgn(a->value.scaledMpq.significand);
    sb = mpq_sgn(b->value.scaledMpq.significand);
    /* Reason upon the sign first */
    if ((sa == 0) && (sb == 0)) return 1;
    if (sa * sb <= 0) return 0;
    /* Here a and b are of the same sign and none is zero

       If their exponents are equal and their significands are equal,
       they are equal.

    */
    if ((a->value.scaledMpq.expo == b->value.scaledMpq.expo) &&
	(mpq_cmp(a->value.scaledMpq.significand,
		 b->value.scaledMpq.significand) == 0))
      return 1;
    /* Here the exponents are unequal or the significands are unequal

       In order to compare 2^E * n/d to 2^F * p/q, compare

       2^(E - F) to (p/q) / (n/d).

    */
    __sollyaRecycleMpqInit(t);
    mpq_div(t, b->value.scaledMpq.significand, a->value.scaledMpq.significand);
    G = mpq_remove_powers_of_two(t);

    /* Here, we have to compare 2^(E - F) to 2^G * t,

       where t = r/s = 2^-G * (p/q) / (n/d)

       is a fraction such that both r and s are odd.

       This means if

       * E - F != G, a and b are unequal
       * E - F == G:
       - if t is equal to one, a and b are equal
       - otherwise, a and b are unequal.
    */
    if (a->value.scaledMpq.expo - b->value.scaledMpq.expo != G) { /* Exponent overflow possible */
      res = 0;
    } else {
      res = (mpq_cmp_si(t, 1, 1u) == 0);
    }
    __sollyaRecycleMpqClear(t);
    return res;
    break;
  }

  return defVal;
}

static inline int constantIsPositive(constant_t a, int defVal) {
  int s;

  if (a == NULL) return defVal;
  if (a->isPositive.cached) return a->isPositive.res;
  if (a->isOne.cached && a->isOne.res) return 1;
  if (a->isMinusOne.cached && a->isMinusOne.res) return 0;
  if (a->isZero.cached && a->isZero.res) return 0;
  if (a->isNonNegativeInteger.cached && a->isZero.cached &&
      a->isNonNegativeInteger.res && (!a->isZero.res)) return 1;
  switch (a->type) {
  case INTEGER:
    a->isPositive.cached = 1;
    a->isPositive.res = (a->value.integer > 0);
    return a->isPositive.res;
    break;
  case EXPRESSION:
    if (accessThruMemRef(a->value.expr)->nodeType == CONSTANT) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      a->isPositive.cached = 1;
      a->isPositive.res = (mpfr_sgn(*(accessThruMemRef(a->value.expr)->value)) > 0);
      return a->isPositive.res;
    }
    if (evaluateSignFast(&s, a->value.expr)) {
      if (s > 0) {
	a->isPositive.cached = 1;
	a->isPositive.res = 1;
	return a->isPositive.res;
      }
    }
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    a->isPositive.cached = 1;
    a->isPositive.res = (mpfr_sgn(a->value.mpfr) > 0);
    return a->isPositive.res;
    break;
  case SCALEDMPQ:
    a->isPositive.cached = 1;
    a->isPositive.res = (mpq_sgn(a->value.scaledMpq.significand) > 0);
    return a->isPositive.res;
    break;
  }
  return defVal;
}

static inline int constantIsGreater(constant_t a, constant_t b, int defVal) {
  constant_t d;
  int res;
  node *dn;
  int sa, sb;

  /* Handle stupid inputs */
  if (a == NULL) return defVal;
  if (b == NULL) return defVal;
  if (a == b) return 0;

  /* If the inputs don't have the same type, compute the difference
     and compare with 0.
  */
  if (a->type != b->type) {
    d = constantSub(a, b);
    res = constantIsPositive(d, defVal);
    constantFree(d);
    return res;
  }

  /* Here, the inputs have the same representation type */
  switch (a->type) {
  case INTEGER:
    return (a->value.integer > b->value.integer);
    break;
  case EXPRESSION:
    if ((accessThruMemRef(a->value.expr)->nodeType == CONSTANT) &&
	(accessThruMemRef(b->value.expr)->nodeType == CONSTANT)) {
      if (!mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) return defVal;
      if (!mpfr_number_p(*(accessThruMemRef(b->value.expr)->value))) return defVal;
      return (mpfr_cmp(*(accessThruMemRef(a->value.expr)->value),
		       *(accessThruMemRef(b->value.expr)->value)) > 0);
    }
    if (evaluateSignFast(&sa, a->value.expr) &&
	evaluateSignFast(&sb, b->value.expr)) {
      if (sa * sb <= 0) {
	/* The inputs have different sign or are both zero

	   If they are both zero, a is not greater than b.
	   If a is positive, b is negative and a is greater than b.
	   If a is negative, b is positive and a is not greater than b.

	*/
	if ((sa == 0) && (sb == 0)) return 0;
	if (sa > 0) return 1;
	return 0;
      }
    }
    /* Construct the difference and try to evaluate its sign */
    dn = addMemRef(makeSub(copyThing(a->value.expr),
			   copyThing(b->value.expr)));
    if (evaluateSignFast(&sa, dn)) {
      freeThing(dn);
      return (sa > 0);
    }
    freeThing(dn);
    return defVal;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return defVal;
    if (!mpfr_number_p(b->value.mpfr)) return defVal;
    return (mpfr_cmp(a->value.mpfr,b->value.mpfr) > 0);
    break;
  case SCALEDMPQ:
    return scaledMpqIsGreaterThan(a->value.scaledMpq.expo,
				  a->value.scaledMpq.significand,
				  b->value.scaledMpq.expo,
				  b->value.scaledMpq.significand);
    break;
  }

  return defVal;
}

static inline int constantIsGreaterOrEqual(constant_t a, constant_t b, int defVal) {
  int resGreater, resEqual;

  resGreater = constantIsGreater(a, b, 42);
  if (resGreater == 42) {
    resEqual = constantIsEqual(a, b, 42);
    if (resEqual == 42) return defVal;
    if (resEqual) return 1;
    return defVal;
  }
  if (resGreater) return 1;
  return constantIsEqual(a, b, defVal);
}

static inline node *__constantToExpressionImpl(constant_t a) {
  mpfr_t num, den;
  mp_prec_t p;
  node *res;
  mp_exp_t ED, EN, EE;

  if (a == NULL) return NULL;
  switch (a->type) {
  case INTEGER:
    return addMemRef(makeConstantInt(a->value.integer));
    break;
  case EXPRESSION:
    return addMemRef(copyThing(a->value.expr));
    break;
  case MPFR:
    return addMemRef(makeConstant(a->value.mpfr));
    break;
  case SCALEDMPQ:
    if (mpq_sgn(a->value.scaledMpq.significand) == 0)
      return addMemRef(makeConstantInt(0));
    if (mpz_cmp_si(mpq_denref(a->value.scaledMpq.significand),1) == 0) {
      /* The denominator is 1, so we do not have to take it into account */
      p = mpz_sizeinbase(mpq_numref(a->value.scaledMpq.significand), 2);
      if (p < 12) p = 12;
      __sollyaRecycleMpfrInit2(num, p);
      mpfr_set_z_2exp(num,
		      mpq_numref(a->value.scaledMpq.significand),
		      a->value.scaledMpq.expo,
		      GMP_RNDN); /* exact as enough precision */
      if (mpfr_number_p(num)) {
	res = addMemRef(makeConstant(num));
      } else {
	mpfr_set_z(num, mpq_numref(a->value.scaledMpq.significand), GMP_RNDN); /* exact as enough precision */
	res = addMemRef(makeMul(addMemRef(makePow(makeConstantInt(2),
						  addMemRef(makeConstantInt(a->value.scaledMpq.expo)))),
				addMemRef(makeConstant(num))));
      }
      __sollyaRecycleMpfrClear(num);
      return addMemRef(res);
    }
    /* Here, we must take both the numerator and the denominator into
       account.
    */
    p = mpz_sizeinbase(mpq_numref(a->value.scaledMpq.significand), 2);
    if (p < 12) p = 12;
    if (a->value.scaledMpq.expo >= 0) {
      EN = a->value.scaledMpq.expo;
      ED = 0;
    } else {
      ED = -a->value.scaledMpq.expo; /* Exponent overflow possible */
      EN = 0;
    }
    __sollyaRecycleMpfrInit2(num, p);
    mpfr_set_z_2exp(num,
		    mpq_numref(a->value.scaledMpq.significand),
		    EN,
		    GMP_RNDN); /* exact as enough precision */
    if (mpfr_number_p(num)) {
      EN = 0;
    } else {
      mpfr_set_z(num,
		 mpq_numref(a->value.scaledMpq.significand),
		 GMP_RNDN); /* exact as enough precision */
    }
    p = mpz_sizeinbase(mpq_denref(a->value.scaledMpq.significand), 2);
    if (p < 12) p = 12;
    __sollyaRecycleMpfrInit2(den, p);
    mpfr_set_z_2exp(den,
		    mpq_denref(a->value.scaledMpq.significand),
		    ED,
		    GMP_RNDN); /* exact as enough precision */
    if (mpfr_number_p(den)) {
      ED = 0;
    } else {
      mpfr_set_z(den,
		 mpq_denref(a->value.scaledMpq.significand),
		 GMP_RNDN); /* exact as enough precision */
    }
    EE = EN - ED; /* Exponent overflow possible */
    if (EE == 0) {
      res = addMemRef(makeDiv(addMemRef(makeConstant(num)),addMemRef(makeConstant(den))));
    } else {
      res = addMemRef(makeMul(addMemRef(makePow(addMemRef(makeConstantInt(2)),
						addMemRef(makeConstantInt(EE)))),
			      addMemRef(makeDiv(makeConstant(num),makeConstant(den)))));
    }
    __sollyaRecycleMpfrClear(num);
    __sollyaRecycleMpfrClear(den);
    return addMemRef(res);
    break;
  }
  return NULL;
}

static inline int __constantGetsUsedByExpression(constant_t c, node *expr) {
  if (c == NULL) return 0;
  if (expr == NULL) return 0;
  if (expr->nodeType != MEMREF) return 0;
  if (expr->cache->polynomialRepresentation == NULL) return 0;
  if (expr->cache->polynomialRepresentation->type != SPARSE) return 0;
  if (expr->cache->polynomialRepresentation->value.sparse->monomialCount != 1u) return 0;
  if (!constantIsZero(expr->cache->polynomialRepresentation->value.sparse->monomialDegrees[0u],1)) return 0;
  if (expr->cache->polynomialRepresentation->value.sparse->coeffs[0] != c) return 0;
  return 1;
}

static inline node *constantToExpression(constant_t a) {
  node *res;

  /* Handle stupid case */
  if (a == NULL)
    return NULL;

  /* Look up cache */
  if (a->expressionCache.cached)
    return copyThing(a->expressionCache.res);

  /* Cache miss, compute information */
  res = addMemRef(__constantToExpressionImpl(a));

  /* Try to write information back to cache */
  if ((!(a->expressionCache.cached)) &&
      (res->nodeType == MEMREF)) {
    /* Need to be careful not to create a reference loop */
    if (!__constantGetsUsedByExpression(a, res)) {
      a->expressionCache.res = copyThing(res);
      a->expressionCache.cached = 1;
    }
  }

  return res;
}

static inline int tryConstantToScaledMpq(mp_exp_t *E, mpq_t rop, constant_t a) {
  mpq_t t;
  mpz_t mant;
  mp_exp_t expo;

  if (a == NULL) return 0;
  switch (a->type) {
  case INTEGER:
    mpq_set_si(rop, a->value.integer, 1u);
    mpq_canonicalize(rop);
    *E = mpq_remove_powers_of_two(rop);
    return 1;
    break;
  case EXPRESSION:
    __sollyaRecycleMpqInit(t);
    if (tryEvaluateConstantTermToMpq(t, a->value.expr)) {
      mpq_set(rop, t);
      mpq_canonicalize(rop);
      *E = mpq_remove_powers_of_two(rop);
      __sollyaRecycleMpqClear(t);
      return 1;
    }
    __sollyaRecycleMpqClear(t);
    return 0;
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return 0;
    if (mpfr_zero_p(a->value.mpfr)) {
      *E = 0;
      mpq_set_si(rop, 0, 1u);
      mpq_canonicalize(rop);
      return 1;
    }
    __sollyaRecycleMpzInit(mant);
    expo = mpfr_get_z_2exp(mant, a->value.mpfr);
    mpq_set_z(rop, mant);
    mpq_canonicalize(rop);
    *E = expo + mpq_remove_powers_of_two(rop);  /* Exponent overflow possible */
    __sollyaRecycleMpzClear(mant);
    return 1;
    break;
  case SCALEDMPQ:
    *E = a->value.scaledMpq.expo;
    mpq_set(rop, a->value.scaledMpq.significand);
    mpq_canonicalize(rop);
    return 1;
    break;
  }
  return 0;
}

static inline int tryConstantToMpq(mpq_t rop, constant_t a) {
  mpq_t q;
  mp_exp_t E;

  __sollyaRecycleMpqInit(q);
  if (!tryConstantToScaledMpq(&E, q, a)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }

  if (E >= ((mp_exp_t) 0)) {
    mpq_mul_2exp(rop, q, ((mp_bitcnt_t) E));
  } else {
    mpq_div_2exp(rop, q, ((mp_bitcnt_t) (-E)));
  }
  __sollyaRecycleMpqClear(q);

  return 1;
}

static inline int tryConstantToMpz(mpz_t r, constant_t a) {
  mpz_t num, den;
  mpq_t q;
  mp_exp_t EQ;

  if (a == NULL) return 0;
  switch (a->type) {
  case INTEGER:
    mpz_set_si(r, a->value.integer);
    return 1;
    break;
  case EXPRESSION:
    if ((accessThruMemRef(a->value.expr)->nodeType == CONSTANT) &&
	mpfr_number_p(*(accessThruMemRef(a->value.expr)->value)) &&
	mpfr_integer_p(*(accessThruMemRef(a->value.expr)->value))) {
      mpfr_get_z(r, *(accessThruMemRef(a->value.expr)->value), GMP_RNDN); /* exact */
      return 1;
    }
    break;
  case MPFR:
    if (!mpfr_number_p(a->value.mpfr)) return 0;
    if (!mpfr_integer_p(a->value.mpfr)) return 0;
    mpfr_get_z(r, a->value.mpfr, GMP_RNDN); /* exact */
    return 1;
    break;
  case SCALEDMPQ:
    if (!scaledMpqIsInteger(a->value.scaledMpq.expo,
			    a->value.scaledMpq.significand)) return 0;
    __sollyaRecycleMpzInit(num);
    __sollyaRecycleMpzInit(den);
    mpq_get_num(num, a->value.scaledMpq.significand);
    mpq_get_den(den, a->value.scaledMpq.significand);
    if (a->value.scaledMpq.expo >= 0) {
      mpz_mul_2exp(num, num, (mp_bitcnt_t) a->value.scaledMpq.expo);
    } else {
      mpz_mul_2exp(den, den, (mp_bitcnt_t) (-a->value.scaledMpq.expo)); /* Exponent overflow possible */
    }
    mpz_fdiv_q(r, num, den);
    __sollyaRecycleMpzClear(den);
    __sollyaRecycleMpzClear(num);
    return 1;
    break;
  }

  __sollyaRecycleMpqInit(q);
  if (!tryConstantToScaledMpq(&EQ, q, a)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  if (!scaledMpqIsInteger(EQ, q)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  __sollyaRecycleMpzInit(num);
  __sollyaRecycleMpzInit(den);
  mpq_get_num(num, a->value.scaledMpq.significand);
  mpq_get_den(den, a->value.scaledMpq.significand);
  if (a->value.scaledMpq.expo >= 0) {
    mpz_mul_2exp(num, num, (mp_bitcnt_t) a->value.scaledMpq.expo);
  } else {
    mpz_mul_2exp(den, den, (mp_bitcnt_t) (-a->value.scaledMpq.expo)); /* Exponent overflow possible */
  }
  mpz_fdiv_q(r, num, den);
  __sollyaRecycleMpzClear(den);
  __sollyaRecycleMpzClear(num);
  return 1;
}

static inline int tryConstantIntegerBound(mp_bitcnt_t *b, constant_t a) {
  mpz_t z;

  __sollyaRecycleMpzInit(z);
  if (!tryConstantToMpz(z, a)) {
    __sollyaRecycleMpzClear(z);
    return 0;
  }

  *b = (mp_bitcnt_t) mpz_sizeinbase(z, 2) + 1;
  __sollyaRecycleMpzClear(z);
  return 1;
}

static inline int tryConstantDenominatorLcm(mpz_t lcm, constant_t a) {
  mpq_t q;
  mp_exp_t E;
  mpz_t n;

  __sollyaRecycleMpqInit(q);
  if (!tryConstantToScaledMpq(&E, q, a)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }

  __sollyaRecycleMpzInit(n);
  mpq_get_den(n, q);
  if (E < ((mp_exp_t) 0)) {
    mpz_mul_2exp(n, n, ((mp_bitcnt_t) (-E)));
  }
  mpz_lcm(lcm, lcm, n);
  __sollyaRecycleMpzClear(n);
  __sollyaRecycleMpqClear(q);

  return 1;
}


static inline int tryConstantToInt(int *r, constant_t a) {
  mpq_t q;
  mp_exp_t EQ;
  int res;

  if (a == NULL) return 0;
  switch (a->type) {
  case INTEGER:
    *r = a->value.integer;
    return 1;
    break;
  case EXPRESSION:
    if ((accessThruMemRef(a->value.expr)->nodeType == CONSTANT) &&
	mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) {
      return mpfr_is_machine_integer(r, *(accessThruMemRef(a->value.expr)->value));
    }
    break;
  case MPFR:
    return mpfr_is_machine_integer(r, a->value.mpfr);
    break;
  default:
    break;
  }

  __sollyaRecycleMpqInit(q);
  if (!tryConstantToScaledMpq(&EQ, q, a)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  if (!scaledMpqIsInt(&res, EQ, q)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  *r = res;
  return 1;
}

static inline int tryConstantToUnsignedInt(unsigned int *r, constant_t a) {
  mpq_t q;
  mp_exp_t EQ;
  unsigned int res;

  if (a == NULL) return 0;
  switch (a->type) {
  case INTEGER:
    if (a->value.integer < 0) return 0;
    *r = a->value.integer;
    return 1;
    break;
  case EXPRESSION:
    if ((accessThruMemRef(a->value.expr)->nodeType == CONSTANT) &&
	mpfr_number_p(*(accessThruMemRef(a->value.expr)->value))) {
      return mpfr_is_machine_unsigned_integer(r, *(accessThruMemRef(a->value.expr)->value));
    }
    break;
  case MPFR:
    return mpfr_is_machine_unsigned_integer(r, a->value.mpfr);
    break;
  default:
    break;
  }

  __sollyaRecycleMpqInit(q);
  if (!tryConstantToScaledMpq(&EQ, q, a)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  if (!scaledMpqIsUnsignedInt(&res, EQ, q)) {
    __sollyaRecycleMpqClear(q);
    return 0;
  }
  *r = res;
  return 1;
}

void constantFPrintf(FILE *fd, constant_t c) {
  if (c == NULL) {
    sollyaFprintf(fd, "(null)");
    return;
  }
  switch (c->type) {
  case INTEGER:
    sollyaFprintf(fd,"%d",c->value.integer);
    break;
  case EXPRESSION:
    sollyaFprintf(fd,"%b",c->value.expr);
    break;
  case MPFR:
    sollyaFprintf(fd,"%v",c->value.mpfr);
    break;
  case SCALEDMPQ:
    if (c->value.scaledMpq.expo == 0) {
      sollyaFprintf(fd,"%r",
		    c->value.scaledMpq.significand);
    } else {
      sollyaFprintf(fd,"2^(%lld) * %r",
		    (long long int) c->value.scaledMpq.expo,
		    c->value.scaledMpq.significand);
    }
    break;
  }
}

char *constantToString(constant_t c) {
  char staticStr[8];
  char *str;
  int size, r;

  if (c == NULL) return NULL;
  switch (c->type) {
  case INTEGER:
    size = sollya_snprintf(staticStr,8,"%d",c->value.integer);
    break;
  case EXPRESSION:
    size = sollya_snprintf(staticStr,8,"%b",c->value.expr);
    break;
  case MPFR:
    size = sollya_snprintf(staticStr,8,"%v",c->value.mpfr);
    break;
  case SCALEDMPQ:
    if (c->value.scaledMpq.expo == 0) {
      size = sollya_snprintf(staticStr,8,"%r",
			     c->value.scaledMpq.significand);
    } else {
      size = sollya_snprintf(staticStr,8,"2^(%lld) * %r",
			     (long long int) c->value.scaledMpq.expo,
			     c->value.scaledMpq.significand);
    }
    break;
  default:
    return NULL;
    break;
  }
  if (size < 0) return NULL;
  str = (char *) safeCalloc(size + 2, sizeof(char));
  switch (c->type) {
  case INTEGER:
    r = sollya_snprintf(str,size,"%d",c->value.integer);
    break;
  case EXPRESSION:
    r = sollya_snprintf(str,size,"%b",c->value.expr);
    break;
  case MPFR:
    r = sollya_snprintf(str,size,"%v",c->value.mpfr);
    break;
  case SCALEDMPQ:
    if (c->value.scaledMpq.expo == 0) {
      r = sollya_snprintf(str,size,"%r",
			  c->value.scaledMpq.significand);
    } else {
      r = sollya_snprintf(str,size,"2^(%lld) * %r",
			  (long long int) c->value.scaledMpq.expo,
			  c->value.scaledMpq.significand);
    }
    break;
  default:
    safeFree(str);
    return NULL;
    break;
  }
  if (r < 0) {
    safeFree(str);
    return NULL;
  }
  return str;
}

static inline constant_t constantAdd(constant_t a, constant_t b) {
  node *aExpr, *bExpr, *cExpr;
  constant_t res;
  mpq_t aS, bS, cS;
  mp_exp_t EA, EB, EC;
  int cI;
  mpz_t cZ;
  unsigned long int bAbs;
  mpfr_t cM;

  /* Stupid inputs */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* Operation on neutral element */
  if (constantIsZero(a, 0)) return constantFromCopy(b);
  if (constantIsZero(b, 0)) return constantFromCopy(a);

  /* Handling expression representations */
  if ((a->type == EXPRESSION) ||
      (b->type == EXPRESSION)) {
    /* One of the two constants is an expression.
       Get expression representations for both,
       construct the expression for the operation
       and build the result constant.
    */
    aExpr = addMemRef(constantToExpression(a));
    bExpr = addMemRef(constantToExpression(b));
    cExpr = addMemRef(makeAdd(aExpr, bExpr));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
  }

  /* Here, none of the constants a and b is represented by an
     expression

     Now handle the case when both constants are represented by the
     same type.

  */
  if (a->type == b->type) {
    switch (a->type) {
    case INTEGER:
      if (tryExactIntAddition(&cI, a->value.integer, b->value.integer)) {
	res = constantFromInt(cI);
	return res;
      }
      __sollyaRecycleMpzInitSetSi(cZ, a->value.integer);
      if (b->value.integer >= 0) {
	bAbs = b->value.integer;
	mpz_add_ui(cZ, cZ, bAbs);
      } else {
	bAbs = - b->value.integer;
	mpz_sub_ui(cZ, cZ, bAbs);
      }
      res = constantFromMpz(cZ);
      __sollyaRecycleMpzClear(cZ);
      return res;
      break;
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_add_exact(cM, a->value.mpfr, b->value.mpfr);
      if (mpfr_number_p(a->value.mpfr) &&
	  mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeAdd(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqAdd(&EC, cS,
		   a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		   b->value.scaledMpq.expo, b->value.scaledMpq.significand);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Handle the case when only one of the inputs is a machine integer */
  if (a->type == INTEGER) {
    switch (b->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_add_exact_int(cM, b->value.mpfr, a->value.integer);
      if (mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeAdd(addMemRef(makeConstantInt(a->value.integer)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqAddInt(&EC, cS,
		      b->value.scaledMpq.expo, b->value.scaledMpq.significand,
		      a->value.integer);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }
  if (b->type == INTEGER) {
    switch (a->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_add_exact_int(cM, a->value.mpfr, b->value.integer);
      if (mpfr_number_p(a->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeAdd(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstantInt(b->value.integer))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqAddInt(&EC, cS,
		      a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		      b->value.integer);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Here, none of the constants a and b is represented by an
     expression and they are of different types.

     Convert both constants to scaled rational form, perform the
     operation and convert back.

     If one of the conversions to scaled rational form fails, convert
     to expression form and work with expression form.
  */
  __sollyaRecycleMpqInit(aS);
  __sollyaRecycleMpqInit(bS);
  if (tryConstantToScaledMpq(&EA, aS, a) &&
      tryConstantToScaledMpq(&EB, bS, b)) {
    __sollyaRecycleMpqInit(cS);
    scaledMpqAdd(&EC, cS, EA, aS, EB, bS);
    res = constantFromScaledMpq(EC, cS);
    __sollyaRecycleMpqClear(aS);
    __sollyaRecycleMpqClear(bS);
    __sollyaRecycleMpqClear(cS);
    return res;
  }
  __sollyaRecycleMpqClear(aS);
  __sollyaRecycleMpqClear(bS);

  /* Could not convert to scaled rational form. Convert to
     expressions.
  */
  aExpr = addMemRef(constantToExpression(a));
  bExpr = addMemRef(constantToExpression(b));
  cExpr = addMemRef(makeAdd(aExpr, bExpr));
  res = constantFromExpression(cExpr);
  freeThing(cExpr);
  return res;
}

static inline constant_t constantSub(constant_t a, constant_t b) {
  node *aExpr, *bExpr, *cExpr;
  constant_t res;
  mpq_t aS, bS, cS;
  mp_exp_t EA, EB, EC;
  int cI;
  mpz_t cZ;
  unsigned long int bAbs;
  mpfr_t cM;

  /* Stupid inputs */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* Operation on neutral element */
  if (constantIsZero(a, 0)) return constantNeg(b);
  if (constantIsZero(b, 0)) return constantFromCopy(a);

  /* Handling expression representations */
  if ((a->type == EXPRESSION) ||
      (b->type == EXPRESSION)) {
    /* One of the two constants is an expression.
       Get expression representations for both,
       construct the expression for the operation
       and build the result constant.
    */
    aExpr = addMemRef(constantToExpression(a));
    bExpr = addMemRef(constantToExpression(b));
    cExpr = addMemRef(makeSub(aExpr, bExpr));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
  }

  /* Here, none of the constants a and b is represented by an
     expression

     Now handle the case when both constants are represented by the
     same type.

  */
  if (a->type == b->type) {
    switch (a->type) {
    case INTEGER:
      if (tryExactIntSubtraction(&cI, a->value.integer, b->value.integer)) {
	res = constantFromInt(cI);
	return res;
      }
      __sollyaRecycleMpzInitSetSi(cZ, a->value.integer);
      if (b->value.integer >= 0) {
	bAbs = b->value.integer;
	mpz_sub_ui(cZ, cZ, bAbs);
      } else {
	bAbs = - b->value.integer;
	mpz_add_ui(cZ, cZ, bAbs);
      }
      res = constantFromMpz(cZ);
      __sollyaRecycleMpzClear(cZ);
      return res;
      break;
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_sub_exact(cM, a->value.mpfr, b->value.mpfr);
      if (mpfr_number_p(a->value.mpfr) &&
	  mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeSub(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqSub(&EC, cS,
		   a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		   b->value.scaledMpq.expo, b->value.scaledMpq.significand);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Handle the case when only one of the inputs is a machine integer */
  if (a->type == INTEGER) {
    switch (b->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_int_sub_exact(cM, a->value.integer, b->value.mpfr);
      if (mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeSub(addMemRef(makeConstantInt(a->value.integer)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqIntSub(&EC, cS,
		      a->value.integer,
		      b->value.scaledMpq.expo, b->value.scaledMpq.significand);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }
  if (b->type == INTEGER) {
    switch (a->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_sub_exact_int(cM, a->value.mpfr, b->value.integer);
      if (mpfr_number_p(a->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeSub(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstantInt(b->value.integer))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqSubInt(&EC, cS,
		      a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		      b->value.integer);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Here, none of the constants a and b is represented by an
     expression and they are of different types.

     Convert both constants to scaled rational form, perform the
     operation and convert back.

     If one of the conversions to scaled rational form fails, convert
     to expression form and work with expression form.
  */
  __sollyaRecycleMpqInit(aS);
  __sollyaRecycleMpqInit(bS);
  if (tryConstantToScaledMpq(&EA, aS, a) &&
      tryConstantToScaledMpq(&EB, bS, b)) {
    __sollyaRecycleMpqInit(cS);
    scaledMpqSub(&EC, cS, EA, aS, EB, bS);
    res = constantFromScaledMpq(EC, cS);
    __sollyaRecycleMpqClear(aS);
    __sollyaRecycleMpqClear(bS);
    __sollyaRecycleMpqClear(cS);
    return res;
  }
  __sollyaRecycleMpqClear(aS);
  __sollyaRecycleMpqClear(bS);

  /* Could not convert to scaled rational form. Convert to
     expressions.
  */
  aExpr = addMemRef(constantToExpression(a));
  bExpr = addMemRef(constantToExpression(b));
  cExpr = addMemRef(makeSub(aExpr, bExpr));
  res = constantFromExpression(cExpr);
  freeThing(cExpr);
  return res;
}

static inline constant_t constantMul(constant_t a, constant_t b) {
  node *aExpr, *bExpr, *cExpr;
  constant_t res;
  mpq_t aS, bS, cS;
  mp_exp_t EA, EB, EC;
  int cI;
  mpz_t cZ;
  mpfr_t cM;

  /* Stupid inputs */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* Operation on neutral element */
  if (constantIsOne(a, 0)) return constantFromCopy(b);
  if (constantIsOne(b, 0)) return constantFromCopy(a);

  /* Handling expression representations */
  if ((a->type == EXPRESSION) ||
      (b->type == EXPRESSION)) {
    /* One of the two constants is an expression.
       Get expression representations for both,
       construct the expression for the operation
       and build the result constant.
    */
    aExpr = addMemRef(constantToExpression(a));
    bExpr = addMemRef(constantToExpression(b));
    cExpr = addMemRef(makeMul(aExpr, bExpr));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
  }

  /* Here, none of the constants a and b is represented by an
     expression

     Now handle the case when both constants are represented by the
     same type.

  */
  if (a->type == b->type) {
    switch (a->type) {
    case INTEGER:
      if (tryExactIntMultiplication(&cI, a->value.integer, b->value.integer)) {
	res = constantFromInt(cI);
	return res;
      }
      __sollyaRecycleMpzInitSetSi(cZ, a->value.integer);
      mpz_mul_si(cZ, cZ, b->value.integer);
      res = constantFromMpz(cZ);
      __sollyaRecycleMpzClear(cZ);
      return res;
      break;
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_mul_exact(cM, a->value.mpfr, b->value.mpfr);
      if (mpfr_number_p(a->value.mpfr) &&
	  mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeMul(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqMul(&EC, cS,
		   a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		   b->value.scaledMpq.expo, b->value.scaledMpq.significand);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Handle the case when only one of the inputs is a machine integer */
  if (a->type == INTEGER) {
    switch (b->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_mul_exact_int(cM, b->value.mpfr, a->value.integer);
      if (mpfr_number_p(b->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeMul(addMemRef(makeConstantInt(a->value.integer)),
				  addMemRef(makeConstant(b->value.mpfr))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqMulInt(&EC, cS,
		      b->value.scaledMpq.expo, b->value.scaledMpq.significand,
		      a->value.integer);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }
  if (b->type == INTEGER) {
    switch (a->type) {
    case MPFR:
      __sollyaRecycleMpfrInit2(cM, 12);
      /* Next call may change the precision of cM */
      mpfr_mul_exact_int(cM, a->value.mpfr, b->value.integer);
      if (mpfr_number_p(a->value.mpfr) &&
	  (!mpfr_number_p(cM))) {
	cExpr = addMemRef(makeMul(addMemRef(makeConstant(a->value.mpfr)),
				  addMemRef(makeConstantInt(b->value.integer))));
	res = constantFromExpression(cExpr);
	freeThing(cExpr);
      } else {
	res = constantFromMpfr(cM);
      }
      __sollyaRecycleMpfrClear(cM);
      return res;
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      scaledMpqMulInt(&EC, cS,
		      a->value.scaledMpq.expo, a->value.scaledMpq.significand,
		      b->value.integer);
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(cS);
      return res;
      break;
    default:
      break;
    }
  }

  /* Here, none of the constants a and b is represented by an
     expression and they are of different types.

     Convert both constants to scaled rational form, perform the
     operation and convert back.

     If one of the conversions to scaled rational form fails, convert
     to expression form and work with expression form.
  */
  __sollyaRecycleMpqInit(aS);
  __sollyaRecycleMpqInit(bS);
  if (tryConstantToScaledMpq(&EA, aS, a) &&
      tryConstantToScaledMpq(&EB, bS, b)) {
    __sollyaRecycleMpqInit(cS);
    scaledMpqMul(&EC, cS, EA, aS, EB, bS);
    res = constantFromScaledMpq(EC, cS);
    __sollyaRecycleMpqClear(aS);
    __sollyaRecycleMpqClear(bS);
    __sollyaRecycleMpqClear(cS);
    return res;
  }
  __sollyaRecycleMpqClear(aS);
  __sollyaRecycleMpqClear(bS);

  /* Could not convert to scaled rational form. Convert to
     expressions.
  */
  aExpr = addMemRef(constantToExpression(a));
  bExpr = addMemRef(constantToExpression(b));
  cExpr = addMemRef(makeMul(aExpr, bExpr));
  res = constantFromExpression(cExpr);
  freeThing(cExpr);
  return res;
}

static inline constant_t constantDiv(constant_t a, constant_t b) {
  node *aExpr, *bExpr, *cExpr;
  constant_t res;
  mpq_t aS, bS, cS, cQ;
  mp_exp_t EA, EB, EC, E;
  int cI;
  unsigned long int bAbs;
  mpfr_t cR;

  /* Stupid inputs */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* Operation on neutral element */
  if (constantIsOne(b, 0)) return constantFromCopy(a);

  /* Handling expression representations */
  if ((a->type == EXPRESSION) ||
      (b->type == EXPRESSION)) {
    /* One of the two constants is an expression.
       Get expression representations for both,
       construct the expression for the operation
       and build the result constant.
    */
    aExpr = addMemRef(constantToExpression(a));
    bExpr = addMemRef(constantToExpression(b));
    cExpr = addMemRef(makeDiv(aExpr, bExpr));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
  }

  /* Here, none of the constants a and b is represented by an
     expression

     Now handle the case when both constants are represented by the
     same type.

  */
  if (a->type == b->type) {
    switch (a->type) {
    case INTEGER:
      if (b->value.integer != 0) {
	if (tryExactIntDivision(&cI, a->value.integer, b->value.integer)) {
	  res = constantFromInt(cI);
	  return res;
	}
	__sollyaRecycleMpqInit(cQ);
	if (b->value.integer >= 0) {
	  bAbs = b->value.integer;
	  mpq_set_si(cQ,a->value.integer,bAbs);
	  mpq_canonicalize(cQ);
	} else {
	  bAbs = (-(b->value.integer + 1)) + ((unsigned long int) 1u);
	  mpq_set_si(cQ,a->value.integer,bAbs);
	  mpq_canonicalize(cQ);
	  mpq_neg(cQ, cQ);
	}
	res = constantFromMpq(cQ);
	__sollyaRecycleMpqClear(cQ);
	return res;
      }
      break;
    case MPFR:
      if (mpfr_number_p(a->value.mpfr) &&
	  mpfr_number_p(b->value.mpfr)) {
	if (!mpfr_zero_p(b->value.mpfr)) {
	  if (mpfr_zero_p(a->value.mpfr)) {
	    return constantFromCopy(a);
	  } else {
	    __sollyaRecycleMpqInit(cQ);
	    E = mpfr_get_z_2exp(mpq_numref(cQ), a->value.mpfr) -
	      mpfr_get_z_2exp(mpq_denref(cQ), b->value.mpfr);  /* Integer overflow possible */
	    mpq_canonicalize(cQ);
	    res = constantFromScaledMpq(E, cQ);
	    __sollyaRecycleMpqClear(cQ);
	    return res;
	  }
	}
      }
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      if (tryScaledMpqDiv(&EC, cS,
			  a->value.scaledMpq.expo, a->value.scaledMpq.significand,
			  b->value.scaledMpq.expo, b->value.scaledMpq.significand)) {
	res = constantFromScaledMpq(EC, cS);
	__sollyaRecycleMpqClear(cS);
	return res;
      }
      __sollyaRecycleMpqClear(cS);
      break;
    default:
      break;
    }
  }

  /* Optimize for one special case: a is MPFR and b is integer and the
     division actually ends up being exact.
  */
  if ((a->type == MPFR) && (b->type == INTEGER)) {
    if (b->value.integer != 0) {
      __sollyaRecycleMpfrInit2(cR, mpfr_get_prec(a->value.mpfr));
      if (mpfr_div_si(cR, a->value.mpfr, (long int) b->value.integer, GMP_RNDN) == 0) {
	res = constantFromMpfr(cR);
	__sollyaRecycleMpfrClear(cR);
	return res;
      }
      __sollyaRecycleMpfrClear(cR);
    }
  }

  /* Here, none of the constants a and b is represented by an
     expression and they are of different types or the division
     on the same type did not work.

     Convert both constants to scaled rational form, perform the
     operation and convert back.

     If one of the conversions to scaled rational form fails, convert
     to expression form and work with expression form.
  */
  __sollyaRecycleMpqInit(aS);
  __sollyaRecycleMpqInit(bS);
  if (tryConstantToScaledMpq(&EA, aS, a) &&
      tryConstantToScaledMpq(&EB, bS, b)) {
    __sollyaRecycleMpqInit(cS);
    if (tryScaledMpqDiv(&EC, cS, EA, aS, EB, bS)) {
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(aS);
      __sollyaRecycleMpqClear(bS);
      __sollyaRecycleMpqClear(cS);
      return res;
    }
    __sollyaRecycleMpqClear(cS);
  }
  __sollyaRecycleMpqClear(aS);
  __sollyaRecycleMpqClear(bS);

  /* Could not convert to scaled rational form or the division did not work.
     Convert to expressions.
  */
  aExpr = addMemRef(constantToExpression(a));
  bExpr = addMemRef(constantToExpression(b));
  cExpr = addMemRef(makeDiv(aExpr, bExpr));
  res = constantFromExpression(cExpr);
  freeThing(cExpr);
  return res;
}

static inline constant_t constantPow(constant_t a, constant_t b) {
  node *aExpr, *bExpr, *cExpr;
  constant_t res;
  mpq_t aS, bS, cS;
  mp_exp_t EA, EB, EC;
  int cI;
  mpfr_t cM;
  unsigned long int bAbs;
  int bb;
  mpz_t cZ;

  /* Stupid inputs */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* Operation on neutral element */
  if (constantIsOne(a, 0)) return constantFromCopy(a);
  if (constantIsOne(b, 0)) return constantFromCopy(a);
  if (constantIsZero(b, 0)) return constantFromInt(1);

  /* Handling expression representations */
  if ((a->type == EXPRESSION) ||
      (b->type == EXPRESSION)) {
    /* One of the two constants is an expression.
       Get expression representations for both,
       construct the expression for the operation
       and build the result constant.
    */
    aExpr = addMemRef(constantToExpression(a));
    bExpr = addMemRef(constantToExpression(b));
    cExpr = addMemRef(makePow(aExpr, bExpr));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
  }

  /* Here, none of the constants a and b is represented by an
     expression

     Now handle the case when both constants are represented by the
     same type.

  */
  if (a->type == b->type) {
    switch (a->type) {
    case INTEGER:
      if (tryExactIntPower(&cI, a->value.integer, b->value.integer)) {
	res = constantFromInt(cI);
	return res;
      }
      if (b->value.integer > 0) {
	bAbs = b->value.integer;
	__sollyaRecycleMpzInitSetSi(cZ, a->value.integer);
	mpz_pow_ui(cZ, cZ, bAbs);
	res = constantFromMpz(cZ);
	__sollyaRecycleMpzClear(cZ);
	return res;
      }
      break;
    case MPFR:
      if (mpfr_number_p(a->value.mpfr) &&
	  mpfr_number_p(b->value.mpfr) &&
	  mpfr_integer_p(b->value.mpfr) &&
	  (mpfr_cmp_si(b->value.mpfr, 1) >= 0) &&
	  mpfr_fits_ulong_p(b->value.mpfr, GMP_RNDN)) {
	bAbs = mpfr_get_ui(b->value.mpfr, GMP_RNDN); /* exact */
	__sollyaRecycleMpfrInit2(cM, 12);
	if (try_mpfr_pow_exact(cM, a->value.mpfr, bAbs)) {
	  if (mpfr_number_p(a->value.mpfr) &&
	      (!mpfr_number_p(cM))) {
	    cExpr = addMemRef(makePow(addMemRef(makeConstant(a->value.mpfr)),
				      addMemRef(makeConstantUnsignedInt(bAbs))));
	    res = constantFromExpression(cExpr);
	    freeThing(cExpr);
	  } else {
	    res = constantFromMpfr(cM);
	  }
	  __sollyaRecycleMpfrClear(cM);
	  return res;
	}
	__sollyaRecycleMpfrClear(cM);
      }
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      if (tryScaledMpqPow(&EC, cS,
			  a->value.scaledMpq.expo, a->value.scaledMpq.significand,
			  b->value.scaledMpq.expo, b->value.scaledMpq.significand)) {
	res = constantFromScaledMpq(EC, cS);
	__sollyaRecycleMpqClear(cS);
	return res;
      }
      __sollyaRecycleMpqClear(cS);
      break;
    default:
      break;
    }
  }

  /* Handle the case when b is a machine integer and a is not */
  if (b->type == INTEGER) {
    switch (a->type) {
    case MPFR:
      if (mpfr_number_p(a->value.mpfr) && (b->value.integer >= 1)) {
	bAbs = b->value.integer;
	bb = bAbs;
	if ((bAbs >= 1) && (bb == b->value.integer)) {
	  __sollyaRecycleMpfrInit2(cM, 12);
	  if (try_mpfr_pow_exact(cM, a->value.mpfr, bAbs)) {
	    if (mpfr_number_p(a->value.mpfr) &&
		(!mpfr_number_p(cM))) {
	      cExpr = addMemRef(makePow(addMemRef(makeConstant(a->value.mpfr)),
					addMemRef(makeConstantInt(b->value.integer))));
	      res = constantFromExpression(cExpr);
	      freeThing(cExpr);
	    } else {
	      res = constantFromMpfr(cM);
	    }
	    __sollyaRecycleMpfrClear(cM);
	    return res;
	  }
	  __sollyaRecycleMpfrClear(cM);
	}
      }
      break;
    case SCALEDMPQ:
      __sollyaRecycleMpqInit(cS);
      if (tryScaledMpqPowInt(&EC, cS,
			     a->value.scaledMpq.expo, a->value.scaledMpq.significand,
			     b->value.integer)) {
	res = constantFromScaledMpq(EC, cS);
	__sollyaRecycleMpqClear(cS);
	return res;
      }
      __sollyaRecycleMpqClear(cS);
      break;
    default:
      break;
    }
  }

  /* Here, none of the constants a and b is represented by an
     expression and they are of different types or the powering
     on the same type did not work.

     Convert both constants to scaled rational form, perform the
     operation and convert back.

     If one of the conversions to scaled rational form fails, convert
     to expression form and work with expression form.
  */
  __sollyaRecycleMpqInit(aS);
  __sollyaRecycleMpqInit(bS);
  if (tryConstantToScaledMpq(&EA, aS, a) &&
      tryConstantToScaledMpq(&EB, bS, b)) {
    __sollyaRecycleMpqInit(cS);
    if (tryScaledMpqPow(&EC, cS, EA, aS, EB, bS)) {
      res = constantFromScaledMpq(EC, cS);
      __sollyaRecycleMpqClear(aS);
      __sollyaRecycleMpqClear(bS);
      __sollyaRecycleMpqClear(cS);
      return res;
    }
    __sollyaRecycleMpqClear(cS);
  }
  __sollyaRecycleMpqClear(aS);
  __sollyaRecycleMpqClear(bS);

  /* Could not convert to scaled rational form or the powering did not work.
     Convert to expressions.
  */
  aExpr = addMemRef(constantToExpression(a));
  bExpr = addMemRef(constantToExpression(b));
  cExpr = addMemRef(makePow(aExpr, bExpr));
  res = constantFromExpression(cExpr);
  freeThing(cExpr);
  return res;
}

static inline constant_t constantNeg(constant_t a) {
  constant_t res;
  int cI;
  mpz_t cZ;
  node *cExpr;
  mpfr_t cM;
  mpq_t cQ;

  if (a == NULL) return NULL;
  switch (a->type) {
  case INTEGER:
    if (tryExactIntNegation(&cI, a->value.integer)) {
      res = constantFromInt(cI);
      return res;
    }
    __sollyaRecycleMpzInitSetSi(cZ, a->value.integer);
    mpz_neg(cZ,cZ);
    res = constantFromMpz(cZ);
    __sollyaRecycleMpzClear(cZ);
    return res;
    break;
  case EXPRESSION:
    cExpr = addMemRef(makeNeg(copyThing(a->value.expr)));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
    break;
  case MPFR:
    __sollyaRecycleMpfrInit2(cM, mpfr_get_prec(a->value.mpfr));
    mpfr_neg(cM, a->value.mpfr, GMP_RNDN); /* exact */
    res = constantFromMpfr(cM);
    __sollyaRecycleMpfrClear(cM);
    return res;
    break;
  case SCALEDMPQ:
    __sollyaRecycleMpqInit(cQ);
    mpq_neg(cQ, a->value.scaledMpq.significand);
    res = constantFromScaledMpq(a->value.scaledMpq.expo, cQ);
    __sollyaRecycleMpqClear(cQ);
    return res;
    break;
  }

  return NULL;
}

/* Computes q and r such that 2^14 * q + r = a and q = floor(a *
   2^-14)
*/
static inline void constantCutTwo14(constant_t *q, constant_t *r, constant_t a) {
  int qI, rI;
  node *qN, *rN;
  mpfr_t qM, rM;
  mp_exp_t qE, rE;
  mpq_t qQ, rQ;

  /* Handle stupid input */
  if (a == NULL) {
    *q = NULL;
    *r = NULL;
    return;
  }

  switch (a->type) {
  case INTEGER:
    /* The int type is guaranteed to have at least 16 bits. So a shift
       by 14 bits is guaranteed to work.
    */
    qI = a->value.integer >> 14;
    rI = a->value.integer - (qI << 14);
    *q = constantFromInt(qI);
    *r = constantFromInt(rI);
    break;
  case EXPRESSION:
    qN = addMemRef(makeFloor(addMemRef(makeMul(copyThing(a->value.expr),
					       addMemRef(makePow(addMemRef(makeConstantInt(2)),
								 addMemRef(makeConstantInt(-14))))))));
    rN = addMemRef(makeSub(copyThing(a->value.expr),
			   addMemRef(makeMul(addMemRef(makePow(addMemRef(makeConstantInt(2)),
							       addMemRef(makeConstantInt(14)))),
					     copyThing(qN)))));
    *q = constantFromExpression(qN);
    *r = constantFromExpression(rN);
    freeThing(qN);
    freeThing(rN);
    break;
  case MPFR:
    __sollyaRecycleMpfrInit2(qM, mpfr_get_prec(a->value.mpfr));
    __sollyaRecycleMpfrInit2(rM, mpfr_get_prec(a->value.mpfr));
    mpfr_div_2si(qM, a->value.mpfr, 14, GMP_RNDN); /* exact */
    mpfr_floor(qM, qM); /* exact */
    mpfr_mul_2si(rM, qM, 14, GMP_RNDN); /* exact */
    mpfr_sub(rM, a->value.mpfr, rM, GMP_RNDN); /* exact, Sterbenz */
    *q = constantFromMpfr(qM);
    *r = constantFromMpfr(rM);
    __sollyaRecycleMpfrClear(rM);
    __sollyaRecycleMpfrClear(qM);
    break;
  case SCALEDMPQ:
    __sollyaRecycleMpqInit(qQ);
    __sollyaRecycleMpqInit(rQ);
    scaledMpqFloor(&qE, qQ, a->value.scaledMpq.expo - 14, a->value.scaledMpq.significand);
    scaledMpqSub(&rE, rQ, a->value.scaledMpq.expo, a->value.scaledMpq.significand, qE + 14, qQ);
    *q = constantFromScaledMpq(qE, qQ);
    *r = constantFromScaledMpq(rE, rQ);
    __sollyaRecycleMpqClear(rQ);
    __sollyaRecycleMpqClear(qQ);
    break;
  }
}

static inline constant_t constantGcd(constant_t a, constant_t b) {
  constant_t c;
  mpq_t aq, bq, cq;

  /* Handle stupid input */
  if (a == NULL) return NULL;
  if (b == NULL) return NULL;

  /* General case: we define the gcd of constants to be 1, unless
     both constants are rationals, in which case we define

     gcd(a/b, c/d) = gcd(a*d, c*b)/(b*d).

  */
  __sollyaRecycleMpqInit(aq);
  if (tryConstantToMpq(aq, a)) {
    __sollyaRecycleMpqInit(bq);
    if (tryConstantToMpq(bq, b)) {
      __sollyaRecycleMpqInit(cq);
      sollya_mpq_gcd(cq, aq, bq);
      c = constantFromMpq(cq);
      __sollyaRecycleMpqClear(cq);
    } else {
      c = constantFromInt(1);
    }
    __sollyaRecycleMpqClear(bq);
  } else {
    c = constantFromInt(1);
  }
  __sollyaRecycleMpqClear(aq);

  return c;
}

static inline void constantExtendedEuclid(constant_t *g, constant_t *r, constant_t *u, constant_t *v,
					  constant_t a, constant_t b) {
  mpq_t aq, bq, gq, uq, vq;
  
  /* Handle stupid input */
  if ((a == NULL) ||
      (b == NULL)) {
    *g = NULL;
    *r = NULL;
    *u = NULL;
    *v = NULL;
    return;
  }

  /* General case */
  __sollyaRecycleMpqInit(aq);
  if (tryConstantToMpq(aq, a)) {
    __sollyaRecycleMpqInit(bq);
    if (tryConstantToMpq(bq, b)) {
      __sollyaRecycleMpqInit(gq);
      __sollyaRecycleMpqInit(uq);
      __sollyaRecycleMpqInit(vq);
      sollya_mpq_extended_euclid(gq, uq, vq, aq, bq);
      *g = constantFromMpq(gq);
      *r = constantFromInt(0);
      *u = constantFromMpq(uq);
      *v = constantFromMpq(vq);
      __sollyaRecycleMpqClear(gq);
      __sollyaRecycleMpqClear(uq);
      __sollyaRecycleMpqClear(vq);
    } else {
      *g = constantFromCopy(a);
      *r = constantFromCopy(b);
      *u = constantFromInt(1);
      *v = constantFromInt(0);
    }
    __sollyaRecycleMpqClear(bq);
  } else {
    *g = constantFromCopy(a);
    *r = constantFromCopy(b);
    *u = constantFromInt(1);
    *v = constantFromInt(0);
  }
  __sollyaRecycleMpqClear(aq);
}

static inline constant_t constantFloor(constant_t a) {
  constant_t res;
  node *cExpr;
  mpfr_t cM;
  mpq_t cQ;
  mp_exp_t cE;

  if (a == NULL) return NULL;
  switch (a->type) {
  case INTEGER:
    res = constantFromCopy(a);
    return res;
    break;
  case EXPRESSION:
    cExpr = addMemRef(makeFloor(copyThing(a->value.expr)));
    res = constantFromExpression(cExpr);
    freeThing(cExpr);
    return res;
    break;
  case MPFR:
    __sollyaRecycleMpfrInit2(cM, mpfr_get_prec(a->value.mpfr));
    mpfr_rint_floor(cM, a->value.mpfr, GMP_RNDN); /* no double rounding as precision enough */
    res = constantFromMpfr(cM);
    __sollyaRecycleMpfrClear(cM);
    return res;
    break;
  case SCALEDMPQ:
    __sollyaRecycleMpqInit(cQ);
    scaledMpqFloor(&cE, cQ, a->value.scaledMpq.expo, a->value.scaledMpq.significand);
    res = constantFromScaledMpq(cE, cQ);
    __sollyaRecycleMpqClear(cQ);
    return res;
    break;
  }

  return NULL;
}

static inline void constantDivMod(constant_t *quot, constant_t *rest, constant_t a, constant_t b) {
  constant_t t;

  /* Handle stupid input */
  if ((a == NULL) ||
      (b == NULL)) {
    *quot = NULL;
    *rest = NULL;
    return;
  }

  /* Compute quot = floor(a/b) */
  t = constantDiv(a, b);
  *quot = constantFloor(t);
  constantFree(t);

  /* Compute rest = a - quot * b */
  t = constantMul(*quot, b);
  *rest = constantSub(a, t);
  constantFree(t);
}

static inline void constantEvalMpfr(mpfr_t rop, constant_t c) {
  mp_prec_t p;
  mpfr_t cutoff;
  int res;
  sollya_mpfi_t y;

  /* Handle stupid input */
  if (c == NULL) {
    mpfr_set_nan(rop);
    return;
  }

  /* Evaluation depending on the representation type */
  switch (c->type) {
  case INTEGER:
    mpfr_set_si(rop, c->value.integer, GMP_RNDN);
    break;
  case EXPRESSION:
    mpfr_set_nan(rop);
    __sollyaRecycleMpfrInit2(cutoff, 12);
    mpfr_set_si(cutoff, 0, GMP_RNDN);
    res = evaluateFaithfulWithCutOffFast(rop, c->value.expr, NULL, rop, cutoff, mpfr_get_prec(rop) + 10);
    if (res == 0) {
      __sollyaRecycleMpfiInit2(y, mpfr_get_prec(rop) + 10);
      evaluateConstantExpressionToInterval(y, c->value.expr);
      if (!(sollya_mpfi_has_infinity(y) ||
	    sollya_mpfi_has_nan(y))) {
	mpfr_set_si(rop, 0, GMP_RNDN);
      }
      __sollyaRecycleMpfiClear(y);
    }
    __sollyaRecycleMpfrClear(cutoff);
    break;
  case MPFR:
    mpfr_set(rop, c->value.mpfr, GMP_RNDN);
    break;
  case SCALEDMPQ:
    p = mpfr_get_prec(rop);
    mpfr_set_prec(rop, p + 10);
    mpfr_set_z_2exp(rop, mpq_numref(c->value.scaledMpq.significand), c->value.scaledMpq.expo, GMP_RNDN);
    mpfr_div_z(rop, rop, mpq_denref(c->value.scaledMpq.significand), GMP_RNDN);
    mpfr_prec_round(rop, p, GMP_RNDN);
    break;
  }
}

static inline void constantEvalMpfi(sollya_mpfi_t rop, constant_t c) {
  mp_prec_t p;

  /* Handle stupid input */
  if (c == NULL) {
    sollya_mpfi_set_nan(rop);
    return;
  }

  /* Evaluation depending on the representation type */
  switch (c->type) {
  case INTEGER:
    sollya_mpfi_set_si(rop, c->value.integer);
    break;
  case EXPRESSION:
    evaluateConstantExpressionToInterval(rop, c->value.expr);
    break;
  case MPFR:
    sollya_mpfi_set_fr(rop, c->value.mpfr);
    break;
  case SCALEDMPQ:
    p = sollya_mpfi_get_prec(rop);
    sollya_mpfi_set_prec(rop, p + 10);
    sollya_mpfi_set_z_2exp(rop, mpq_numref(c->value.scaledMpq.significand), c->value.scaledMpq.expo);
    sollya_mpfi_div_z(rop, rop, mpq_denref(c->value.scaledMpq.significand));
    sollya_mpfi_prec_round(rop, p);
    break;
  }
}

static inline void constantEvalMpfrUp(mpfr_t rop, constant_t c) {
  sollya_mpfi_t temp;

  /* Handle stupid input */
  if (c == NULL) {
    mpfr_set_nan(rop);
    return;
  }

  /* Handle general case thru interval arithmetic */
  __sollyaRecycleMpfiInit2(temp, (mpfr_get_prec(rop) + ((mp_prec_t) 10)));
  constantEvalMpfi(temp, c);
  sollya_mpfi_get_right(rop, temp);
  __sollyaRecycleMpfiClear(temp);
}

static inline constant_t constantRoundDyadic(constant_t c, mp_prec_t prec) {
  mpfr_t v;
  constant_t res;

  /* Handle stupid inputs */
  if (c == NULL) return NULL;

  /* If c is already dyadic, return a copy of c */
  if (constantIsDyadic(c, 0)) return constantFromCopy(c);

  /* Here, c is not dyadic. Allocate an MPFR variable with precision
     prec and evaluate c to that precision.
  */
  __sollyaRecycleMpfrInit2(v, prec);
  constantEvalMpfr(v, c);
  if (mpfr_number_p(v)) {
    res = constantFromMpfr(v);
  } else {
    res = constantFromCopy(c);
  }
  __sollyaRecycleMpfrClear(v);

  return res;
}

static inline constant_t constantRoundRational(constant_t c, mp_prec_t prec) {
  mpfr_t v;
  constant_t res;

  /* Handle stupid inputs */
  if (c == NULL) return NULL;

  /* If c is already rational, return a copy of c */
  if (constantIsRational(c, 0)) return constantFromCopy(c);

  /* Here, c is not rational. Allocate an MPFR variable with precision
     prec and evaluate c to that precision.
  */
  __sollyaRecycleMpfrInit2(v, prec);
  constantEvalMpfr(v, c);
  if (mpfr_number_p(v)) {
    res = constantFromMpfr(v);
  } else {
    res = constantFromCopy(c);
  }
  __sollyaRecycleMpfrClear(v);

  return res;
}

static inline constant_t constantRound(constant_t c, mp_prec_t prec) {
  mpfr_t v;
  constant_t res;

  /* Handle stupid inputs */
  if (c == NULL) return NULL;

  /* If c holds on prec bits, return a copy of c */
  if (constantHoldsOnPrecBits(c, prec, 0)) return constantFromCopy(c);

  /* Here, c does not hold on prec bits. Allocate an MPFR variable
     with precision prec and evaluate c to that precision.
  */
  __sollyaRecycleMpfrInit2(v, prec);
  constantEvalMpfr(v, c);
  if (mpfr_number_p(v)) {
    res = constantFromMpfr(v);
  } else {
    res = constantFromCopy(c);
  }
  __sollyaRecycleMpfrClear(v);

  return res;
}

static inline constant_t constantRoundWithErrorBound(constant_t c, sollya_mpfi_t I, mpfr_t delta) {
  mp_prec_t prec, p;
  long pl, pll;
  sollya_mpfi_t tI;
  mpfr_t t1, t2;

  /* Handle stupid input */
  if (c == NULL) return NULL;

  /* Handle the cases when delta is NaN or zero

     Just return an unrounded copy of the constant.

  */
  if (mpfr_nan_p(delta) || mpfr_zero_p(delta)) {
    return constantFromCopy(c);
  }

  /* Handle the cases when I contains Inf or NaN 

     Just return an unrounded copy of the constant.

  */
  if (sollya_mpfi_has_nan(I) || sollya_mpfi_has_infinity(I)) {
    return constantFromCopy(c);
  }

  /* Handle the case when delta is Inf 

     Just round the constant to 53 bits.

  */
  if (mpfr_inf_p(delta)) {
    return constantRound(c, ((mp_prec_t) 53));
  }
  
  /* Here delta is neither NaN, nor Inf nor zero and I is neither NaN
     nor Inf

     Perform the rounding with a precision p such that 

     cTilde = c + c * eps

     and 

     abs(c * eps * I) <= abs(delta). 

     As for a given precision p >= 2,

     abs(eps) <= 2^(-p) 

     we have to ensure that 

     2^(-p) <= abs(delta)/max(abs(c * I)).

     This requires us to have a first idea of c.
     
     We evaluate c using interval arithmetic with the precision
     similar to the one of I (and at least 70 bits) and deduce 
     an interval enclosure of abs(c * I).

     If the maximum of that interval enclosure of abs(c * I) is 
     zero, negative, Inf or NaN, we return the constant unrounded.

     Otherwise we compute 

     abs(delta) / max(abs(c * I)) 

     with floating-point arithmetic, rounding in such a way 
     that the value we obtain is certainly less or equal the 
     exact value. 

     If that value is negative, zero, Inf or NaN, we return the
     constant unrounded.

     Otherwise, we compute 

     p = min(12, ceil(-log2(abs(delta) / max(c * I))))

     rounded in such a way that the value we obtain is surely larger
     or equal to the exact value.

  */
  prec = sollya_mpfi_get_prec(I);
  if (prec < mpfr_get_prec(delta)) prec = mpfr_get_prec(delta);
  if (prec < ((mp_prec_t) 70)) prec = (mp_prec_t) 70;
  __sollyaRecycleMpfiInit2(tI, prec);
  constantEvalMpfi(tI, c);
  sollya_mpfi_mul(tI, tI, I);
  sollya_mpfi_abs(tI, tI);
  __sollyaRecycleMpfrInit2(t1, sollya_mpfi_get_prec(tI));
  __sollyaRecycleMpfrInit2(t2, sollya_mpfi_get_prec(tI));
  sollya_mpfi_get_right(t2, tI);
  __sollyaRecycleMpfiClear(tI);
  
  if (mpfr_inf_p(t2) || (mpfr_nan_p(t2) || (mpfr_sgn(t2) <= 0))) {
    __sollyaRecycleMpfrClear(t1);
    __sollyaRecycleMpfrClear(t2);
    return constantFromCopy(c);
  }

  mpfr_abs(t1, delta, GMP_RNDD);
  mpfr_div(t1, t1, t2, GMP_RNDD);

  if (mpfr_inf_p(t1) || (mpfr_nan_p(t1) || (mpfr_sgn(t1) <= 0))) {
    __sollyaRecycleMpfrClear(t1);
    __sollyaRecycleMpfrClear(t2);
    return constantFromCopy(c);
  }

  mpfr_log2(t1, t1, GMP_RNDD);
  mpfr_neg(t1, t1, GMP_RNDU);
  mpfr_ceil(t1, t1);

  if (mpfr_inf_p(t1) || mpfr_nan_p(t1)) {
    __sollyaRecycleMpfrClear(t1);
    __sollyaRecycleMpfrClear(t2);
    return constantFromCopy(c);
  }

  if (mpfr_cmp_si(t1, 12) < 0) {
    mpfr_set_si(t1, 12, GMP_RNDU);
  }

  if (!mpfr_fits_slong_p(t1, GMP_RNDU)) {
    __sollyaRecycleMpfrClear(t1);
    __sollyaRecycleMpfrClear(t2);
    return constantFromCopy(c);
  }

  pl = mpfr_get_si(t1, GMP_RNDU);
  p = (mp_prec_t) pl;
  pll = (long) p;
  if (!((pl == pll) && (pl >= ((long) 0)))) {
    __sollyaRecycleMpfrClear(t1);
    __sollyaRecycleMpfrClear(t2);
    return constantFromCopy(c);
  }
  if (p < ((mp_prec_t) 12)) {
    p = (mp_prec_t) 12;
  }
  __sollyaRecycleMpfrClear(t1);
  __sollyaRecycleMpfrClear(t2);
  
  /* Round the constant to precision p, using the round-to-precision
     function 
  */
  return constantRound(c, p);
}

static inline int constantReferencesExpression(constant_t c, node *expr) {
  /* Handle stupid input */
  if (c == NULL) return 0;

  /* Handle funny circular dependency with caches */
  if (c->expressionCache.cached) {
    if ((c->expressionCache.res == expr) ||
	(accessThruMemRef(c->expressionCache.res) == expr)) {
      freeThing(c->expressionCache.res);
      c->expressionCache.cached = 0;
      c->expressionCache.res = NULL;
    }
  }
  
  /* Evaluation depending on the representation type */
  switch (c->type) {
  case EXPRESSION:
    if (c->value.expr == expr) return 1;
    if (accessThruMemRef(c->value.expr) == expr) return 1;
    return 0;
    break;
  case INTEGER:
  case MPFR:
  case SCALEDMPQ:
    return 0;
    break;
  }

  return 0;
}

static inline size_t constantDeepReferenceCount(constant_t c, node *expr) {
  size_t res;
  
  /* Handle stupid input */
  if (c == NULL) return ((size_t) 0);
  if (expr == NULL) return ((size_t) 0);

  /* Evaluation depending on the representation type */
  res = (size_t) 0;
  switch (c->type) {
  case EXPRESSION:
    if (c->value.expr == expr) res++;
    break;
  case INTEGER:
  case MPFR:
  case SCALEDMPQ:    
    break;
  }

  /* Account for caches */
  if (c->expressionCache.cached) {
    if (c->expressionCache.res == expr) res++;
  }

  /* Return result */
  return res;
}

static inline void __constantInvalidateExpressionCache(constant_t c, node *expr) {
  node *tmp;

  /* Handle stupid input */
  if (c == NULL) return;
  if (expr == NULL) return;
  
  /* Invalidate cache if needed */
  if (expr->nodeType != MEMREF) return;
  if (c->expressionCache.cached) {
    if (c->expressionCache.res == expr) {
      c->expressionCache.cached = 0;
      tmp = c->expressionCache.res;
      c->expressionCache.res = NULL;
      freeThing(tmp);
    }
  }
}

static inline int constantUsesExpressionConstant(constant_t c) {
  /* Handle stupid input */
  if (c == NULL) return 0;

  /* Evaluation depending on the representation type */
  return (c->type == EXPRESSION);
}


static inline uint64_t constantHash(constant_t c) {
  uint64_t hash;

  /* Handle stupid input */
  if (c == NULL) return hashPointer(NULL);

  /* Check if the hash has already been computed */
  if (c->hash.hasHash) {
    return c->hash.hash;
  }

  /* Compute the hash */
  hash = hashInt((int) (c->type));
  switch (c->type) {
  case INTEGER:
    hash = hashCombine(hash, hashInt(c->value.integer));
    break;
  case EXPRESSION:
    hash = hashCombine(hash, hashThingNoPolynomialHandling(c->value.expr));
    break;
  case MPFR:
    hash = hashCombine(hash, hashMpfr(c->value.mpfr));
    break;
  case SCALEDMPQ:
    hash = hashCombine(hash,
		       hashCombine(hashInt64((int64_t) (c->value.scaledMpq.expo)),
				   hashMpq(c->value.scaledMpq.significand)));
    break;
  }

  /* Cache the result */
  c->hash.hash = hash;
  c->hash.hasHash = 1;

  /* Return the hash */
  return hash;
}

/* End of part for constants */

/* Start of part for sparse polynomials */

static inline sparse_polynomial_t __polynomialGetSparsePolynomial(polynomial_t);
static inline int sparsePolynomialPowConstant(sparse_polynomial_t *, sparse_polynomial_t, constant_t);
static inline int sparsePolynomialGetDegreeAsInt(sparse_polynomial_t);
static inline int sparsePolynomialCoefficientsAreRational(sparse_polynomial_t, int);
static inline sparse_polynomial_t sparsePolynomialPowUnsignedInt(sparse_polynomial_t, unsigned int);
static inline constant_t sparsePolynomialGetIthCoefficientAsConstantIntIndex(sparse_polynomial_t, int);
static inline int sparsePolynomialEvalMpz(mpz_t, sparse_polynomial_t, mpz_t);
static inline sparse_polynomial_t sparsePolynomialMul(sparse_polynomial_t, sparse_polynomial_t);
static inline int sparsePolynomialCoefficientsAreInteger(sparse_polynomial_t, int);
static inline int __sparsePolynomialCoefficientIntegerBounds(mp_bitcnt_t *, sparse_polynomial_t);
static inline int __sparsePolynomialEvalMpz(mpz_t, sparse_polynomial_t, mpz_t, mpz_t);
static inline int __sparsePolynomialCoefficientDenominatorLcm(mpz_t, sparse_polynomial_t);


static inline sparse_polynomial_t __sparsePolynomialAllocate() {
  return (sparse_polynomial_t) safeMalloc(sizeof(struct __sparse_polynomial_struct_t));
}

static inline void __sparsePolynomialFreeMem(sparse_polynomial_t p) {
  safeFree(p);
}

static inline void sparsePolynomialInitializeCaches() {
  constantInitializeCaches();
}

static inline void sparsePolynomialFreeCaches() {
  constantFreeCaches();
}

static inline void __sparsePolynomialAdjustDegree(sparse_polynomial_t p) {
  unsigned int i, k;

  if (p == NULL) return;
  for (k=0u,i=p->monomialCount-1u;i>=1u;i--,k++) {
    if (!constantIsZero(p->coeffs[i],0)) break;
  }
  if (k == 0u) return;
  for (i=p->monomialCount-k;i<p->monomialCount;i++) {
    constantFree(p->coeffs[i]);
    constantFree(p->monomialDegrees[i]);
  }
  p->monomialCount -= k;
  constantFree(p->deg);
  p->deg = constantFromCopy(p->monomialDegrees[p->monomialCount-1u]);
  p->coeffs = (constant_t *) safeRealloc(p->coeffs,
					 ((size_t) (p->monomialCount)) * sizeof(constant_t));
  p->monomialDegrees = (constant_t *) safeRealloc(p->monomialDegrees,
						  ((size_t) (p->monomialCount)) * sizeof(constant_t));
}

static inline int __sparsePolynomialAscendingDegrees(sparse_polynomial_t p, int defVal) {
  unsigned int i;
  int ord, sure;

  if (p == NULL) return defVal;
  if (p->monomialCount <= 1u) return 1;
  sure = 1;
  for (i=1u;i<p->monomialCount;i++) {
    ord = constantIsGreater(p->monomialDegrees[i], p->monomialDegrees[i-1u], 19);
    if (ord != !!ord) {
      sure = 0;
      ord = !!ord;
    }
    if (!ord) return 0;
  }
  if (sure) return 1;
  return defVal;
}

/* Given a array a of n constants, sorted in ascending order, find
   the lowest index of an element greater than or equal to d.

   If n is zero, return n.
   Otherwise:

   - if the 0-th (first) element of the array is greater than or
   equal to d, return 0.

   - if the (n-1)-th (last) element of the array is less than d,
   return n.

   - otherwise return the lowest index of an element greater than
   or equal to d.

   The algorithm is just supposed to work; it can have a ridiculous
   complexity.

*/
static inline unsigned int __sparsePolynomialFindDegreeNaive(constant_t d,
							     constant_t *a,
							     unsigned int n) {
  unsigned int i;

  if (n == 0u) return n;
  for (i=0u; i<n; i++) {
    if (constantIsGreaterOrEqual(a[i],d,0)) return i;
  }
  return n;
}

/* Given a array a of n constants, sorted in ascending order, find
   the lowest index of an element greater than or equal to d.

   If n is zero, return n.
   Otherwise:

   - if the 0-th (first) element of the array is greater than or
   equal to d, return 0.

   - if the (n-1)-th (last) element of the array is less than d,
   return n.

   - otherwise return the lowest index of an element greater than
   or equal to d.

   The complexity of the algorithm is supposed to be O(log(n)).

   The algorithm can take g into account, which is supposed to be "a
   good guess" for the sought index for the first element greater than
   or equal to d.

*/
static inline unsigned int __sparsePolynomialFindDegree(constant_t d,
							constant_t *a,
							unsigned int n,
							unsigned int g) {
  unsigned int l, r, m, i, j, p;
  unsigned long long int t;
  int tv;

  /* Easy case */
  if (n == 0u) return n;

  /* Check if 0-th (first) element is greater than or equal to d. */
  tv = constantIsGreaterOrEqual(a[0],d,42);
  if (tv == 42) return __sparsePolynomialFindDegreeNaive(d, a, n);
  if (tv) return 0;

  /* Check if (n-1)-th (last) element is less than, i.e. not greater
     than nor equal to, d.
  */
  tv = constantIsGreaterOrEqual(a[n-1u],d,1);
  if (tv == 42) return __sparsePolynomialFindDegreeNaive(d, a, n);
  if (!tv) return n;

  /* If g is in the valid range of indices (0 <= g <= n - 1), try to
     find the answer up to 64 steps around g.
  */
  if (g <= n - 1u) { /* 0 <= g is always true because g is unsigned */
    for (p=n+1u, i=g, j=0; (j<64u) && (i <= n - 1u); j++) {
      tv = constantIsGreaterOrEqual(a[i],d,42);
      if (tv == 42) return __sparsePolynomialFindDegreeNaive(d, a, n);
      if (tv) {
	/* The i-th element of the array is greater than or equal to
	   d. If we just made a step to the right, we know the
	   answer. If i is zero, we stop the search loop without an
	   answer (the guess was incorrect). Otherwise we make a step
	   to the left.
	*/
	if (p == i-1u) return i;
	if (i == 0u) break;
	p = i;
	i--;
      } else {
	/* The i-th element of the array is less than d. If we just
	   made a step to the left, we know the answer. If i is n-1,
	   we stop the search loop without an answer (the guess was
	   incorrect). Otherwise we make a step to the right.
	*/
	if (p == i+1u) return i+1u;
	if (i == n-1u) break;
	p = i;
	i++;
      }
    }
  }

  /* We still didn't find the right index. Perform a bisection.

     We maintain to indices l and r such that we are sure that:

     *    a[l] < d
     *    d <= a[r]

     In order to be sure that m = floor((l+r)/2) satisfies

     l + 1 <= m <= r - 1,

     i.e. that the interval becomes strictly smaller in each step,
     we have to suppose that

     l <= r - 2,

     i.e. that

     r - l > 1.

  */
  for (l=0u, r=n-1u; r - l > 1u; /* nothing */) {
    /* Compute the middle of the interval between l and r */
    t = l; t += r; t >>= 1; m = t;

    /* Check value at the middle of the interval */
    tv = constantIsGreaterOrEqual(a[m],d,42);
    if (tv == 42) return __sparsePolynomialFindDegreeNaive(d, a, n);
    if (tv) {
      /* Here, d <= a[m], so the new interval becomes [l;m] */
      r = m;
    } else {
      /* Here, a[m] < d, so the new interval becomes [m;r] */
      l = m;
    }
  }

  /* Here, we know that a[l] < d and d <= a[r] */
  for (i=l; l<=r; i++) {
    tv = constantIsGreaterOrEqual(a[i],d,42);
    if (tv == 42) return __sparsePolynomialFindDegreeNaive(d, a, n);
    if (tv) return i;
  }

  /* Can only be reached if comparison not possible */
  return __sparsePolynomialFindDegreeNaive(d, a, n);
}

static inline sparse_polynomial_t sparsePolynomialFromMpfrConstant(mpfr_t c) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromMpfr(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromMpzConstant(mpz_t c) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromMpz(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromMpqConstant(mpq_t c) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  mpq_canonicalize(c);
  res->coeffs[0] = constantFromMpq(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromConstant(constant_t c) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromCopy(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromIntConstant(int c) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromInt(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline int __sparsePolynomialFromConstantExpression(sparse_polynomial_t *r, node *c) {
  sparse_polynomial_t res;
  if (c == NULL) return 0;
  if (!isConstant(c)) return 0;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromExpression(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  *r = res;
  return 1;
}

static inline int __sparsePolynomialFromConstantExpressionOnlyRealCoeffs(sparse_polynomial_t *r, node *c) {
  sparse_polynomial_t res;
  if (c == NULL) return 0;
  if (!isConstant(c)) return 0;
  if (!containsOnlyRealNumbers(c)) return 0;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromExpression(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromInt(0);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  *r = res;
  return 1;
}

static inline sparse_polynomial_t sparsePolynomialFromIdentity() {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromInt(1);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromCopy(res->coeffs[0]);
  res->deg = constantFromCopy(res->monomialDegrees[0]);
  res->hash.hasHash = 0;
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromMpfrCoefficients(mpfr_t *coeffs, unsigned int deg) {
  unsigned int i, startSize;
  sparse_polynomial_t res;
  constant_t c;

  if (coeffs == NULL) return NULL;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  startSize = deg + 1u;
  if (startSize == 0u) startSize = UINT_MAX;
  res->monomialCount = 0u;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<=deg;i++) {
    c = constantFromMpfr(coeffs[i]);
    if (!constantIsZero(c,0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromUnsignedInt(i);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }
  /* If res->monomialCount still is zero, we never added anything
     because all constants were zero. We add a zero coefficient in
     this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  return res;
}

static inline sparse_polynomial_t sparsePolynomialFromMpqCoefficients(mpq_t *coeffs, unsigned int deg) {
  unsigned int i, startSize;
  sparse_polynomial_t res;
  constant_t c;

  if (coeffs == NULL) return NULL;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  startSize = deg + 1u;
  if (startSize == 0u) startSize = UINT_MAX;
  res->monomialCount = 0u;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<=deg;i++) {
    c = constantFromMpq(coeffs[i]);
    if (!constantIsZero(c,0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromUnsignedInt(i);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }
  /* If res->monomialCount still is zero, we never added anything
     because all constants were zero. We add a zero coefficient in
     this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  return res;
}

static inline int sparsePolynomialFromConstantExpressionCoefficients(sparse_polynomial_t *r, node **coeffs, unsigned int deg) {
  unsigned int i, startSize;
  sparse_polynomial_t res;
  constant_t c;

  if (coeffs == NULL) return 0;
  for (i=0;i<=deg;i++) {
    if ((coeffs[i] != NULL) && (!isConstant(coeffs[i]))) return 0;
  }
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  startSize = deg + 1u;
  if (startSize == 0u) startSize = UINT_MAX;
  res->monomialCount = 0u;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<=deg;i++) {
    if (coeffs[i] != NULL) {
      c = constantFromExpression(coeffs[i]);
    } else {
      c = constantFromInt(0);
    }
    if (!constantIsZero(c,0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromUnsignedInt(i);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }
  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials multiplied gave zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  *r = res;
  return 1;
}

static inline sparse_polynomial_t sparsePolynomialFromCopy(sparse_polynomial_t p) {
  if (p == NULL) return NULL;
  p->refCount++;
  return p;
}

static inline void sparsePolynomialFree(sparse_polynomial_t p) {
  unsigned int i;

  if (p == NULL) return;
  p->refCount--;
  if (p->refCount > 0u) return;
  constantFree(p->deg);
  for (i=0;i<p->monomialCount;i++) {
    constantFree(p->coeffs[i]);
    constantFree(p->monomialDegrees[i]);
  }
  safeFree(p->coeffs);
  safeFree(p->monomialDegrees);
  __sparsePolynomialFreeMem(p);
}

static inline int sparsePolynomialIsConstant(sparse_polynomial_t p, int defVal) {
  int degZero;

  if (p == NULL) return defVal;
  if (p->monomialCount == 0u) return 1;
  degZero = constantIsZero(p->deg, 42);
  if (degZero == 42) return defVal;
  if (!degZero) return 0;
  return 1;
}

static inline int sparsePolynomialConstantGetConstant(constant_t *c, sparse_polynomial_t p) {
  unsigned int i;
  constant_t t;

  if (p == NULL) return 0;
  if (!sparsePolynomialIsConstant(p, 0)) return 0;
  if (p->monomialCount == 0u) {
    *c = constantFromInt(0);
    return 1;
  }
  if (p->monomialCount == 1u) {
    *c = constantFromCopy(p->coeffs[0]);
    return 1;
  }
  *c = constantFromCopy(p->coeffs[0]);
  for (i=1;i<p->monomialCount;i++) {
    t = constantAdd(*c, p->coeffs[i]);
    constantFree(*c);
    *c = t;
  }
  return 1;
}

static inline int sparsePolynomialIsConstantZero(sparse_polynomial_t p, int defVal) {
  int isConst, res;
  constant_t c;

  if (p == NULL) return defVal;
  isConst = sparsePolynomialIsConstant(p, 42);
  if (isConst == 42) return defVal;
  if (!isConst) return 0;
  if (sparsePolynomialConstantGetConstant(&c, p)) {
    res = constantIsZero(c, defVal);
    constantFree(c);
    return res;
  }
  return defVal;
}

static inline int sparsePolynomialIsConstantOne(sparse_polynomial_t p, int defVal) {
  int isConst, res;
  constant_t c;

  if (p == NULL) return defVal;
  isConst = sparsePolynomialIsConstant(p, 42);
  if (isConst == 42) return defVal;
  if (!isConst) return 0;
  if (sparsePolynomialConstantGetConstant(&c, p)) {
    res = constantIsOne(c, defVal);
    constantFree(c);
    return res;
  }
  return defVal;
}

static inline int sparsePolynomialIsConstantInteger(sparse_polynomial_t p, int k, int defVal) {
  int isConst, res;
  constant_t c, d;

  if (p == NULL) return defVal;
  isConst = sparsePolynomialIsConstant(p, 42);
  if (isConst == 42) return defVal;
  if (!isConst) return 0;
  if (sparsePolynomialConstantGetConstant(&c, p)) {
    d = constantFromInt(k);
    res = constantIsEqual(c, d, defVal);
    constantFree(d);
    constantFree(c);
    return res;
  }
  return defVal;
}

static inline sparse_polynomial_t sparsePolynomialAdd(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t res;
  unsigned int i,j,startSize;
  constant_t newCoeff, newMonomial;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Handle addition of zero polynomial */
  if (sparsePolynomialIsConstantZero(p, 0)) {
    return sparsePolynomialFromCopy(q);
  }
  if (sparsePolynomialIsConstantZero(q, 0)) {
    return sparsePolynomialFromCopy(p);
  }

  /* General case */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  startSize = p->monomialCount + q->monomialCount;
  if (startSize < p->monomialCount) {
    startSize = UINT_MAX;
  }
  res->coeffs = (constant_t *) safeCalloc(startSize,
					  sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize,
						   sizeof(constant_t));
  res->monomialCount = 0;
  for (i=0u, j=0u; (i<p->monomialCount) && (j<q->monomialCount); /* nothing */) {
    /* We have 3 cases:

       (i)   The degree of the i-th monomial of p and the one of the
       j-th monomial of q are the same.

       In this case we add the coefficients and increment both i and j.

       (ii)  The degree of the i-th monomial of p is greater than the one
       of the j-th monomial of q.

       In this case we copy the j-th coefficient of q and increment j.

       (iii) The degree of the i-th monomial of p is less than the one
       of the j-th monomial of q.

       In this case we copy the i-th coefficient of p and increment i.

       In case (i) it is possible that after addition, the coefficient
       gets zero. In this case we not add the monomial to the
       polynomial.

    */
    if (constantIsEqual(p->monomialDegrees[i], q->monomialDegrees[j], 0)) {
      /* Case (i) */
      newCoeff = constantAdd(p->coeffs[i], q->coeffs[j]);
      newMonomial = constantFromCopy(p->monomialDegrees[i]);
      i++;
      j++;
    } else {
      if (constantIsGreater(p->monomialDegrees[i], q->monomialDegrees[j], 0)) {
	/* Case (iii) */
	newCoeff = constantFromCopy(q->coeffs[j]);
	newMonomial = constantFromCopy(q->monomialDegrees[j]);
	j++;
      } else {
	/* Case (ii) */
	newCoeff = constantFromCopy(p->coeffs[i]);
	newMonomial = constantFromCopy(p->monomialDegrees[i]);
	i++;
      }
    }
    /* Now check if we have to add the monomial */
    if (!constantIsZero(newCoeff, 0)) {
      /* Add the coefficient */
      res->coeffs[res->monomialCount] = newCoeff;
      res->monomialDegrees[res->monomialCount] = newMonomial;
      (res->monomialCount)++;
    } else {
      /* Just free the new coefficient */
      constantFree(newCoeff);
      constantFree(newMonomial);
    }
  }
  /* Now add the left-over monomials of p and q */
  for (/* nothing */; i<p->monomialCount; i++) {
    res->coeffs[res->monomialCount] = constantFromCopy(p->coeffs[i]);
    res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
    (res->monomialCount)++;
  }
  for (/* nothing */; j<q->monomialCount; j++) {
    res->coeffs[res->monomialCount] = constantFromCopy(q->coeffs[j]);
    res->monomialDegrees[res->monomialCount] = constantFromCopy(q->monomialDegrees[j]);
    (res->monomialCount)++;
  }
  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials cancelled out. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  return res;
}

static inline sparse_polynomial_t sparsePolynomialNeg(sparse_polynomial_t);

static inline sparse_polynomial_t sparsePolynomialSub(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t res;
  unsigned int i,j,startSize;
  constant_t newCoeff, newMonomial;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Handle subtraction of zero polynomial */
  if (sparsePolynomialIsConstantZero(p, 0)) {
    return sparsePolynomialNeg(q);
  }
  if (sparsePolynomialIsConstantZero(q, 0)) {
    return sparsePolynomialFromCopy(p);
  }

  /* General case */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  startSize = p->monomialCount + q->monomialCount;
  if (startSize < p->monomialCount) {
    startSize = UINT_MAX;
  }
  res->coeffs = (constant_t *) safeCalloc(startSize,
					  sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize,
						   sizeof(constant_t));
  res->monomialCount = 0;
  for (i=0u, j=0u; (i<p->monomialCount) && (j<q->monomialCount); /* nothing */) {
    /* We have 3 cases:

       (i)   The degree of the i-th monomial of p and the one of the
       j-th monomial of q are the same.

       In this case we subtract the coefficients and increment
       both i and j.

       (ii)  The degree of the i-th monomial of p is greater than the one
       of the j-th monomial of q.

       In this case we negate the j-th coefficient of q and increment j.

       (iii) The degree of the i-th monomial of p is less than the one
       of the j-th monomial of q.

       In this case we copy the i-th coefficient of p and increment i.

       In case (i) it is possible that after addition, the coefficient
       gets zero. In this case we not add the monomial to the
       polynomial.

    */
    if (constantIsEqual(p->monomialDegrees[i], q->monomialDegrees[j], 0)) {
      /* Case (i) */
      newCoeff = constantSub(p->coeffs[i], q->coeffs[j]);
      newMonomial = constantFromCopy(p->monomialDegrees[i]);
      i++;
      j++;
    } else {
      if (constantIsGreater(p->monomialDegrees[i], q->monomialDegrees[j], 0)) {
	/* Case (ii) */
	newCoeff = constantNeg(q->coeffs[j]);
	newMonomial = constantFromCopy(q->monomialDegrees[j]);
	j++;
      } else {
	/* Case (iii) */
	newCoeff = constantFromCopy(p->coeffs[i]);
	newMonomial = constantFromCopy(p->monomialDegrees[i]);
	i++;
      }
    }
    /* Now check if we have to add the monomial */
    if (!constantIsZero(newCoeff, 0)) {
      /* Add the coefficient */
      res->coeffs[res->monomialCount] = newCoeff;
      res->monomialDegrees[res->monomialCount] = newMonomial;
      (res->monomialCount)++;
    } else {
      /* Just free the new coefficient */
      constantFree(newCoeff);
      constantFree(newMonomial);
    }
  }
  /* Now add the left-over monomials of p and q */
  for (/* nothing */; i<p->monomialCount; i++) {
    res->coeffs[res->monomialCount] = constantFromCopy(p->coeffs[i]);
    res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
    (res->monomialCount)++;
  }
  for (/* nothing */; j<q->monomialCount; j++) {
    res->coeffs[res->monomialCount] = constantNeg(q->coeffs[j]);
    res->monomialDegrees[res->monomialCount] = constantFromCopy(q->monomialDegrees[j]);
    (res->monomialCount)++;
  }
  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials cancelled out. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  return res;
}

static inline int sparsePolynomialEqual(sparse_polynomial_t p, sparse_polynomial_t q, int defVal) {
  unsigned int i;
  int monDegEqual, coeffEqual, degreeEqual;
  int res, sure;
  sparse_polynomial_t d;

  if (p == NULL) return defVal;
  if (q == NULL) return defVal;
  if (p == q) return 1;
  if (p->hash.hasHash && q->hash.hasHash) {
    if (p->hash.hash != q->hash.hash) return 0;
  }
  if (p->monomialCount == 0u) {
    return sparsePolynomialIsConstantZero(q, defVal);
  }
  if (q->monomialCount == 0u) {
    return sparsePolynomialIsConstantZero(p, defVal);
  }
  if (p->monomialCount != q->monomialCount) {
    if (__sparsePolynomialAscendingDegrees(p, 0) &&
	__sparsePolynomialAscendingDegrees(q, 0))
      return 0;
    d = sparsePolynomialSub(p, q);
    res = sparsePolynomialIsConstantZero(d, defVal);
    sparsePolynomialFree(d);
    return res;
  }
  degreeEqual = constantIsEqual(p->deg, q->deg, 42);
  if (degreeEqual == 42) return defVal;
  if (!degreeEqual) return 0;
  sure = 1;
  for (i=0;i<p->monomialCount;i++) {
    monDegEqual = constantIsEqual(p->monomialDegrees[i], q->monomialDegrees[i], 42);
    if (monDegEqual != !!monDegEqual) {
      sure = 0;
      monDegEqual = !!monDegEqual;
    }
    if (!monDegEqual) return 0;
    coeffEqual = constantIsEqual(p->coeffs[i], q->coeffs[i], 42);
    if (coeffEqual != !!coeffEqual) {
      sure = 0;
      coeffEqual = !!coeffEqual;
    }
    if (!coeffEqual) return 0;
  }

  if (sure) return 1;
  return defVal;
}

static inline int sparsePolynomialIsIdentity(sparse_polynomial_t p, int defVal) {
  sparse_polynomial_t q;
  int res;

  if (p == NULL) return defVal;
  if (sparsePolynomialGetDegreeAsInt(p) != 1) return 0;

  /* Can be optimized */
  q = sparsePolynomialFromIdentity();
  res = sparsePolynomialEqual(p, q, defVal);
  sparsePolynomialFree(q);

  return res;
}

static inline void __sparsePolynomialAddMonomialToArrays(constant_t *coeffs,
							 constant_t *degrees,
							 unsigned int *n,
							 unsigned int *g,
							 constant_t c,
							 constant_t d) {
  unsigned int i, j;
  constant_t t;

  /* Find the index where to add the coefficient */
  j = __sparsePolynomialFindDegree(d, degrees, *n, *g);

  /* Keep the value of the index as a guess for the next time */
  *g = j;

  /* If the coefficient's degree is greater than all the others, it
     goes at the end.
  */
  if (j == *n) {
    /* Adding the coefficient at the end */
    coeffs[j] = c;
    degrees[j] = d;
    (*n)++;
    return;
  }

  /* Here, we know that 0 <= j <= n - 1

     There are two cases:

     a) degree[j] == d. In this case we add c to degree[j].

     We have two subcases:

     (i)  After addition the new coefficient is non-zero. We leave
     it untouched.

     (ii) After addition the new coefficient is zero. We remove it by
     moving all following coefficients one to the left.

     b) degree[j] > d. In this case we deplace all existing
     coefficients and degrees of indices j thru n-1 and put the new
     coefficient and degree at index j.

  */
  if (constantIsEqual(degrees[j],d,0)) {
    /* Case a) */
    t = constantAdd(coeffs[j], c);
    constantFree(coeffs[j]);
    if (constantIsZero(t, 0)) {
      constantFree(t);
      constantFree(degrees[j]);
      for (i=j;i<*n;i++) {
	coeffs[i] = coeffs[i+1u];
	degrees[i] = degrees[i+1u];
      }
      (*n)--;
    } else {
      coeffs[j] = t;
    }
    constantFree(c);
    constantFree(d);
    return;
  }

  /* Case b)

     Deplace the existing coefficients and degrees.
  */
  for (i=*n;i>j;i--) {
    coeffs[i] = coeffs[i-1u];
    degrees[i] = degrees[i-1u];
  }

  /* Account for the new entry */
  (*n)++;

  /* Put the new entry */
  coeffs[j] = c;
  degrees[j] = d;
}

static inline sparse_polynomial_t sparsePolynomialAddConstant(sparse_polynomial_t p, constant_t c) {
  sparse_polynomial_t t, res;

  /* Handle the stupid cases */
  if (p == NULL) return NULL;
  if (c == NULL) return NULL;

  /* Can be optimized */
  t = sparsePolynomialFromConstant(c);
  res = sparsePolynomialAdd(p, t);
  sparsePolynomialFree(t);

  return res;
}

static inline sparse_polynomial_t sparsePolynomialSubConstant(sparse_polynomial_t p, constant_t c) {
  sparse_polynomial_t t, res;

  /* Handle the stupid cases */
  if (p == NULL) return NULL;
  if (c == NULL) return NULL;

  /* Can be optimized */
  t = sparsePolynomialFromConstant(c);
  res = sparsePolynomialSub(p, t);
  sparsePolynomialFree(t);

  return res;
}

static inline int __sparsePolynomialMulOptimizedKaratsubaCut(sparse_polynomial_t *rh, sparse_polynomial_t *rl, sparse_polynomial_t p, unsigned int d) {
  constant_t dd;
  sparse_polynomial_t resh, resl;
  unsigned int i;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle trivial input */
  if (d == 0u) {
    *rh = sparsePolynomialFromCopy(p);
    *rl = sparsePolynomialFromIntConstant(0);
    return 1;
  }

  /* Handle strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    *rh = sparsePolynomialFromIntConstant(0);
    *rl = sparsePolynomialFromIntConstant(0);
    return 1;
  }

  /* Represent d as a constant */
  dd = constantFromUnsignedInt(d);

  /* Handle trivial case when d is greater than the degree of p */
  if (constantIsGreater(dd,p->deg,0)) {
    *rh = sparsePolynomialFromIntConstant(0);
    *rl = sparsePolynomialFromCopy(p);
    constantFree(dd);
    return 1;
  }

  /* Form polynomials */
  resh = __sparsePolynomialAllocate();
  resh->refCount = 1;
  resh->hash.hasHash = 0;
  resh->monomialCount = 0u;
  resh->coeffs = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  resh->monomialDegrees = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  resl = __sparsePolynomialAllocate();
  resl->refCount = 1;
  resl->hash.hasHash = 0;
  resl->monomialCount = 0u;
  resl->coeffs = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  resl->monomialDegrees = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  for (i=0u;i<p->monomialCount;i++) {
    if (constantIsGreater(p->monomialDegrees[i],dd,0)) {
      resh->coeffs[resh->monomialCount] = constantFromCopy(p->coeffs[i]);
      resh->monomialDegrees[resh->monomialCount] = constantSub(p->monomialDegrees[i],dd);
      (resh->monomialCount)++;
    } else {
      resl->coeffs[resl->monomialCount] = constantFromCopy(p->coeffs[i]);
      resl->monomialDegrees[resl->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
      (resl->monomialCount)++;
    }
  }
  if (resh->monomialCount == 0u) {
    resh->coeffs[resh->monomialCount] = constantFromInt(0);
    resh->monomialDegrees[resh->monomialCount] = constantFromInt(0);
    (resh->monomialCount)++;
  }
  if (resl->monomialCount == 0u) {
    resl->coeffs[resl->monomialCount] = constantFromInt(0);
    resl->monomialDegrees[resl->monomialCount] = constantFromInt(0);
    (resl->monomialCount)++;
  }
  resh->deg = constantFromCopy(resh->monomialDegrees[resh->monomialCount - 1u]);
  resl->deg = constantFromCopy(resl->monomialDegrees[resl->monomialCount - 1u]);
  if (resh->monomialCount != p->monomialCount) {
    resh->coeffs = (constant_t *) safeRealloc(resh->coeffs,
					      ((size_t) (resh->monomialCount)) * sizeof(constant_t));
    resh->monomialDegrees = (constant_t *) safeRealloc(resh->monomialDegrees,
						       ((size_t) (resh->monomialCount)) * sizeof(constant_t));
  }
  if (resl->monomialCount != p->monomialCount) {
    resl->coeffs = (constant_t *) safeRealloc(resl->coeffs,
					      ((size_t) (resl->monomialCount)) * sizeof(constant_t));
    resl->monomialDegrees = (constant_t *) safeRealloc(resl->monomialDegrees,
						       ((size_t) (resl->monomialCount)) * sizeof(constant_t));
  }
  __sparsePolynomialAdjustDegree(resh);
  __sparsePolynomialAdjustDegree(resl);

  /* Free constant representing d */
  constantFree(dd);

  /* Set results and indicate success */
  *rh = resh;
  *rl = resl;
  return 1;
}

static inline sparse_polynomial_t __sparsePolynomialMulOptimizedKaratsubaShift(sparse_polynomial_t p, unsigned int d) {
  constant_t dd;
  sparse_polynomial_t res;
  unsigned int i;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle trivial case */
  if (d == 0u) return sparsePolynomialFromCopy(p);

  /* Handle a strange case that should never happen */
  if (p->monomialCount == 0u) {
    return sparsePolynomialFromIntConstant(0);
  }

  /* Represent d as a constant */
  dd = constantFromUnsignedInt(d);

  /* Do the work */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->deg = constantAdd(p->deg, dd);
  res->monomialCount = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<res->monomialCount;i++) {
    res->coeffs[i] = constantFromCopy(p->coeffs[i]);
    res->monomialDegrees[i] = constantAdd(p->monomialDegrees[i],dd);
  }
  __sparsePolynomialAdjustDegree(res);

  /* Free constant representing d */
  constantFree(dd);

  /* Return result */
  return res;
}

static inline int __sparsePolynomialMulOptimizedKaratsuba(sparse_polynomial_t *r, sparse_polynomial_t p, sparse_polynomial_t q) {
  unsigned int degP, degQ, deg, d;
  sparse_polynomial_t ph, pl, qh, ql, pk, qk, a, b, c, t, m, rr, hh, mm;

  /* Handle stupid input */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Try to represent degrees as unsigned machine integers */
  if (!tryConstantToUnsignedInt(&degP, p->deg)) return 0;
  if (!tryConstantToUnsignedInt(&degQ, q->deg)) return 0;

  /* Get maximum of both degrees */
  deg = degP;
  if (degQ > deg) deg = degQ;

  /* Do nothing if maximum of both degrees is too small */
  if (deg < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_KARATSUBA_MIN_DEGREE)) return 0;

  /* Compute "half" degree for Karatsuba cut */
  d = deg >> 1u;
  if (d < 1u) d = 1u;

  /* Try to cut the polynomials into two parts

     p = ph * x^d + pl
     q = qh * x^d + ql

  */
  if (!__sparsePolynomialMulOptimizedKaratsubaCut(&ph, &pl, p, d)) return 0;
  if (p == q) {
    qh = sparsePolynomialFromCopy(ph);
    ql = sparsePolynomialFromCopy(pl);
  } else {
    if (!__sparsePolynomialMulOptimizedKaratsubaCut(&qh, &ql, q, d)) {
      sparsePolynomialFree(ph);
      sparsePolynomialFree(pl);
      return 0;
    }
  }

  /* Compute

     pk = ph + pl
     qk = qh + ql

  */
  pk = sparsePolynomialAdd(ph, pl);
  if ((qh == ph) && (ql == pl)) {
    qk = sparsePolynomialFromCopy(pk);
  } else {
    qk = sparsePolynomialAdd(qh, ql);
  }

  /* Compute

     a = ph * qh
     b = pk * qk
     c = pl * ql

     This may result in a recursive call.

  */
  a = sparsePolynomialMul(ph, qh);
  b = sparsePolynomialMul(pk, qk);
  c = sparsePolynomialMul(pl, ql);

  /* Compute

     m = b - a - c

     It is easy to see that

     m = ph * ql + pl * qh

  */
  t = sparsePolynomialSub(b, a);
  m = sparsePolynomialSub(t, c);
  sparsePolynomialFree(t);

  /* Now we have

     p * q = (ph * x^d + pl) * (qh * x^d + ql)
     = ph * qh * x^(2 * d) + (ph * ql + pl * qh) * x^d + pl * ql
     = a * x^(2 * d) + m * x^d + c

     Start by computing

     hh = a * x^(2 * d)
     mm = m * x^d

     Then sum

     rr = p * q = hh + mm + c

  */
  hh = __sparsePolynomialMulOptimizedKaratsubaShift(a, d << 1u);
  mm = __sparsePolynomialMulOptimizedKaratsubaShift(m, d);

  t = sparsePolynomialAdd(hh, mm);
  rr = sparsePolynomialAdd(t, c);
  sparsePolynomialFree(t);

  /* Free intermediate polynomials */
  sparsePolynomialFree(mm);
  sparsePolynomialFree(hh);
  sparsePolynomialFree(m);
  sparsePolynomialFree(c);
  sparsePolynomialFree(b);
  sparsePolynomialFree(a);
  sparsePolynomialFree(qk);
  sparsePolynomialFree(pk);
  sparsePolynomialFree(ql);
  sparsePolynomialFree(qh);
  sparsePolynomialFree(pl);
  sparsePolynomialFree(ph);

  /* Set result and indicate success */
  *r = rr;
  return 1;
}

static inline int __sparsePolynomialMulOptimizedRational(sparse_polynomial_t *r, sparse_polynomial_t p, sparse_polynomial_t q) {
  unsigned int degP, degQ, deg;
  sparse_polynomial_t g, h, w, lcmp, oneOverLcmSquaredP, res;
  mpz_t lcm;
  mpq_t oneOverLcmSquared;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Try to represent degrees as unsigned machine integers */
  if (!tryConstantToUnsignedInt(&degP, p->deg)) return 0;
  if (!tryConstantToUnsignedInt(&degQ, q->deg)) return 0;

  /* Get maximum of both degrees */
  deg = degP;
  if (degQ > deg) deg = degQ;

  /* Do nothing if maximum of both degrees is too small */
  if (deg < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_RATIONAL_MIN_DEGREE)) return 0;

  /* Do nothing if we cannot ascertain that all coefficients are rational */
  if (!sparsePolynomialCoefficientsAreRational(p, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreRational(q, 0)) return 0;

  /* Do nothing if one of the polynomials might have only integer
     coefficients. We must stop the recursion. As a matter of course
     we would like to have another optimized function for polynomials
     with integer coefficients.
  */
  if (sparsePolynomialCoefficientsAreInteger(p, 1)) return 0;
  if (sparsePolynomialCoefficientsAreInteger(p, 1)) return 0;

  /* Try to compute the lcm of the denominators of both polynomials */
  __sollyaRecycleMpzInit(lcm);
  mpz_set_ui(lcm, 1u);
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, p)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, q)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* If lcm still is 1 or -1, bail out to prevent infinite recursion. This
     case should never happen.
  */
  if (mpz_cmpabs_ui(lcm, 1u) == 0) {
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* If lcm is 0, bail out as something very bad is happening.
   */
  if (mpz_cmpabs_ui(lcm, 0u) == 0) {
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* Multiply both polynomials by the lcm, to make them polynomials
     with integer coefficients.

     These polynomial multiplications will not recurse as
     the degrees of the constant polynomials is 1.

  */
  lcmp = sparsePolynomialFromMpzConstant(lcm);
  g = sparsePolynomialMul(p, lcmp);
  if (p == q) {
    h = sparsePolynomialFromCopy(g);
  } else {
    h = sparsePolynomialMul(q, lcmp);
  }
  sparsePolynomialFree(lcmp);

  /* Call polynomial multiplication to multiply g * h.

     This polynomial multiplication will not recurse
     as we made sure not to work on polynomials whose
     coefficients are all integers.

     Ideally, we have a specialized optimized polynomial
     multiplication that does the work to multiply these integer
     polynomials.

  */
  w = sparsePolynomialMul(g, h);
  sparsePolynomialFree(g);
  sparsePolynomialFree(h);

  /* Now we have to multiply w = g * h by 1/(lcm^2) */
  __sollyaRecycleMpqInit(oneOverLcmSquared);
  mpz_mul(lcm, lcm, lcm);
  mpq_set_z(oneOverLcmSquared, lcm);
  mpq_inv(oneOverLcmSquared, oneOverLcmSquared);
  oneOverLcmSquaredP = sparsePolynomialFromMpqConstant(oneOverLcmSquared);
  __sollyaRecycleMpqClear(oneOverLcmSquared);
  __sollyaRecycleMpzClear(lcm);

  /* This multiplication will not recurse as the constant polynomial
     is of degree 1.
  */
  res = sparsePolynomialMul(w, oneOverLcmSquaredP);

  /* Free intermediate polynomials */
  sparsePolynomialFree(w);
  sparsePolynomialFree(oneOverLcmSquaredP);

  /* Set result and indicate success */
  *r = res;
  return 1;
}

static inline int __sparsePolynomialMulOptimizedIntegerDecompose(mpz_t f, mpz_t **c, unsigned int **d, unsigned int *m, mp_bitcnt_t *s, sparse_polynomial_t p) {
  unsigned int i, n, k;
  mpz_t *coeffs;
  unsigned int *degrees;
  mpz_t g;
  mp_bitcnt_t sb, sc;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle strange case when p has no monomials */
  if (p->monomialCount == 0u) return 0;

  /* Get monomial count */
  n = p->monomialCount;

  /* Allocate array for the coefficients and the degrees.
     Initialize the coefficients.
  */
  coeffs = (mpz_t *) safeCalloc((size_t) n, sizeof(mpz_t));
  degrees = (unsigned int *) safeCalloc((size_t) n, sizeof(mpz_t));
  for (i=0u;i<n;i++) {
    __sollyaRecycleMpzInit(coeffs[i]);
  }

  /* Get the raw coefficients of the polynomial. If there is a
     coefficient that we cannot evaluate to an integer, bail out.
  */
  for (i=0u;i<n;i++) {
    if (!tryConstantToMpz(coeffs[i],p->coeffs[i])) {
      for (k=0u;k<n;k++) {
	__sollyaRecycleMpzClear(coeffs[k]);
      }
      safeFree(coeffs);
      safeFree(degrees);
      return 0;
    }
  }

  /* Get the degrees of the monomials of the polynomial. If there is a
     monomial with a degree that cannot be represented on an unsigned
     int machine integer, bail out.
  */
  for (i=0u;i<n;i++) {
    if (!tryConstantToUnsignedInt(&degrees[i],p->monomialDegrees[i])) {
      for (k=0u;k<n;k++) {
	__sollyaRecycleMpzClear(coeffs[k]);
      }
      safeFree(coeffs);
      safeFree(degrees);
      return 0;
    }
  }

  /* Compute the gcd of all coefficients.

     We are sure that we have at least one coefficient.

  */
  __sollyaRecycleMpzInit(g);
  mpz_set(g, coeffs[0]);
  for (i=1u;i<n;i++) {
    mpz_gcd(g, g, coeffs[i]);
    if (mpz_cmp_ui(g, 1u) == 0)
      break;
  }

  /* If the gcd is different from 1, divide all coefficients by that gcd */
  if (mpz_cmp_ui(g, 1u) != 0) {
    for (i=0u;i<n;i++) {
      mpz_divexact(coeffs[i], coeffs[i], g);
    }
  }

  /* Compute the maximum size in bits of all coefficients */
  sb = (mp_bitcnt_t) 1;
  for (i=0u;i<n;i++) {
    sc = (mp_bitcnt_t) mpz_sizeinbase(coeffs[i], 2);
    if (sc > sb) sb = sc;
  }

  /* Now we got all information. We can hence set the results and
     indicate success.
  */
  mpz_set(f, g);
  __sollyaRecycleMpzClear(g);
  *c = coeffs;
  *d = degrees;
  *m = n;
  *s = sb;
  return 1;
}

static inline void __sparsePolynomialMulOptimizedIntegerKroneckerHelperMul64(uint64_t *h, uint64_t *l, uint64_t a, uint64_t b) {
  uint64_t ahi, alo, bhi, blo, ahibhi, ahiblo, alobhi, aloblo, ahiblohi, ahiblolo, alobhihi, alobhilo, rh, rl;

  ahi = a >> 32;
  alo = a - (ahi << 32);
  bhi = b >> 32;
  blo = b - (bhi << 32);

  ahibhi = ahi * bhi;
  ahiblo = ahi * blo;
  alobhi = alo * bhi;
  aloblo = alo * blo;

  ahiblohi = ahiblo >> 32;
  ahiblolo = ahiblo - (ahiblohi << 32);
  alobhihi = alobhi >> 32;
  alobhilo = alobhi - (alobhihi << 32);

  ahiblolo <<= 32;
  alobhilo <<= 32;

  rh = ahibhi;
  rl = aloblo + ahiblolo;
  if (rl < ahiblolo) rh++;
  rl += alobhilo;
  if (rl < alobhilo) rh++;
  rh += ahiblohi;
  rh += alobhihi;

  *h = rh;
  *l = rl;
}

static inline int __sparsePolynomialMulOptimizedIntegerKroneckerHelperMul(size_t *n, unsigned int d, size_t w) {
  uint64_t dd, ww, h, l, lll;
  unsigned int ddd;
  size_t www, ll;

  /* One of the inputs is zero */
  if ((w == ((size_t) 0)) ||
      (d == 0u)) {
    *n = (size_t) 0;
    return 1;
  }

  /* Here, the inputs are non-zero. If w is less than 1, it is
     negative and we refuse to work
  */
  if (w < ((size_t) 1)) {
    return 0;
  }

  /* Here both inputs are positive.

     Test if both inputs hold on a uint64_t.

  */
  dd = (uint64_t) d;
  ww = (uint64_t) w;
  ddd = (unsigned int) dd;
  www = (size_t) ww;
  if ((d != ddd) || (www != w)) {
    return 0;
  }

  /* Multiply dd by ww, yielding h * 2^64 + l = dd * ww */
  __sparsePolynomialMulOptimizedIntegerKroneckerHelperMul64(&h, &l, dd, ww);

  /* If h is non-zero, refuse to work */
  if (h != ((uint64_t) 0)) {
    return 0;
  }

  /* Try to represent l on a size_t */
  ll = (size_t) l;
  lll = (uint64_t) ll;
  if ((lll != l) || ((ll == ((size_t) 0)) || (ll < ((size_t) 1)))) {
    return 0;
  }

  /* Everything worked. Set the result and indicate success. */
  *n = ll;
  return 1;
}

static inline int __sparsePolynomialMulOptimizedIntegerKronecker(mpz_t a, mpz_t b, mpz_t *c, unsigned int *d, unsigned int k, size_t w) {
  size_t n, j;
  uint64_t *wordsA, *wordsB;
  mpz_t t;
  unsigned int m, i;

  /* Handle the strange case when w is less than 1. This should never
     happen.
  */
  if (w < ((size_t) 1)) return 0;

  /* Handle the strange case when k is less than 1. This should never
     happen.
  */
  if (k < 1u) return 0;

  /* Allocate two temporary arrays of n 64 bit words each.

     Take

     n = (d[k-1] + 1) * w.

     Perform that multiplication carefully. Bail out if it overflows
     on size_t.

  */
  m = d[k-1u] + 1u;
  if (m == 0u) return 0;
  if (!__sparsePolynomialMulOptimizedIntegerKroneckerHelperMul(&n, m, w)) return 0;
  wordsA = (uint64_t *) safeCalloc(n, sizeof(uint64_t));
  wordsB = (uint64_t *) safeCalloc(n, sizeof(uint64_t));

  /* Go over all coefficients and "add" in the positive coefficients
     times 2^(d * w) to wordsA, the negative coefficients to wordsB.

     No need to perform the multiplication carefully. All d[i] <= d[k-1].

  */
  __sollyaRecycleMpzInit(t);
  for (i=0u;i<k;i++) {
    j = w * ((size_t) d[i]);
    mpz_abs(t, c[i]);
    mpz_tdiv_r_2exp(t, t, (((mp_bitcnt_t) w) * ((mp_bitcnt_t) 64)));
    if (mpz_sgn(c[i]) >= 0) {
      if (mpz_sgn(t) != 0) {
	mpz_export(&wordsA[j], NULL, -1, sizeof(uint64_t), 0, (size_t) 0, t);
      }
    } else {
      if (mpz_sgn(t) != 0) {
	mpz_export(&wordsB[j], NULL, -1, sizeof(uint64_t), 0, (size_t) 0, t);
      }
    }
  }
  __sollyaRecycleMpzClear(t);

  /* Transform the arrays of 64 bit words into integers */
  mpz_set_ui(a, 0u);
  mpz_set_ui(b, 0u);
  mpz_import(a, n, -1, sizeof(uint64_t), 0, (size_t) 0, wordsA);
  mpz_import(b, n, -1, sizeof(uint64_t), 0, (size_t) 0, wordsB);

  /* Free the temporary arrays */
  safeFree(wordsA);
  safeFree(wordsB);

  /* Indicate success */
  return 1;
}

static inline int __sparsePolynomialMulOptimizedIntegerLift(sparse_polynomial_t *r, mpz_t a, size_t w) {
  uint64_t *words;
  size_t n;
  sparse_polynomial_t p;
  mpz_t t;
  size_t i;
  unsigned int d, imc;

  /* Handle the strange case when w is less than 1. This should never
     happen.
  */
  if (w < ((size_t) 1)) return 0;

  /* Handle the strange case when a is negative. This should never
     happen.
  */
  if (mpz_sgn(a) < 0) return 0;

  /* Handle the case when a is zero. */
  if (mpz_sgn(a) == 0) {
    *r = sparsePolynomialFromIntConstant(0);
    return 1;
  }

  /* Here a is positive and at least 1. Represent a as an array of 64
     bit words. Make sure the number of elements of the 64 bit word
     array is divisible by w.
  */
  n = (mpz_sizeinbase(a, 2) + ((size_t) (64 - 1))) / ((size_t) 64);
  n = w * ((n + (w - ((size_t) 1))) / w);
  words = (uint64_t *) safeCalloc(n, sizeof(uint64_t));
  mpz_export((void *) words, NULL, -1, sizeof(uint64_t), 0, (size_t) 0, a);

  /* Perform the lifting, i.e. construct a polynomial p with integer
     coefficients such that

     p(2^w) = a.

  */
  imc = (unsigned int) (n / w);
  if (imc < 1u) imc = 1u;
  p = __sparsePolynomialAllocate();
  p->refCount = 1;
  p->hash.hasHash = 0;
  p->monomialCount = 0u;
  p->coeffs = (constant_t *) safeCalloc((size_t) imc, sizeof(constant_t));
  p->monomialDegrees = (constant_t *) safeCalloc((size_t) imc, sizeof(constant_t));
  __sollyaRecycleMpzInit(t);
  for (i=((size_t) 0),d=0u;i<=n-w;i+=w,d++) {
    mpz_set_ui(t, 0u);
    mpz_import(t, w, -1, sizeof(uint64_t), 0, (size_t) 0, (void *) (&words[i]));
    mpz_abs(t, t);
    if (mpz_sgn(t) != 0) {
      p->coeffs[p->monomialCount] = constantFromMpz(t);
      p->monomialDegrees[p->monomialCount] = constantFromUnsignedInt(d);
      (p->monomialCount)++;
    }
  }
  __sollyaRecycleMpzClear(t);
  if (p->monomialCount == 0u) {
    p->coeffs[p->monomialCount] = constantFromInt(0);
    p->monomialDegrees[p->monomialCount] = constantFromInt(0);
    (p->monomialCount)++;
  }
  p->deg = constantFromCopy(p->monomialDegrees[p->monomialCount - 1u]);
  if (p->monomialCount != imc) {
    p->coeffs = (constant_t *) safeRealloc(p->coeffs,
					   ((size_t) (p->monomialCount)) * sizeof(constant_t));
    p->monomialDegrees = (constant_t *) safeRealloc(p->monomialDegrees,
						    ((size_t) (p->monomialCount)) * sizeof(constant_t));
  }
  __sparsePolynomialAdjustDegree(p);

  /* Free the temporary array. */
  safeFree(words);

  /* Set the result and indicate success */
  *r = p;
  return 1;
}

static inline int __sparsePolynomialMulOptimizedInteger(sparse_polynomial_t *res, sparse_polynomial_t p, sparse_polynomial_t q) {
  unsigned int degP, degQ, deg;
  mpz_t fp, fq;
  mpz_t *cp, *cq;
  unsigned int *dp, *dq;
  unsigned int kp, kq;
  mp_bitcnt_t sp, sq;
  unsigned int i;
  size_t wp, wq, w;
  mpz_t a, b, c, d, r, s, t1, t2;
  sparse_polynomial_t g, h, z, rr, fpfqp;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Try to represent degrees as unsigned machine integers */
  if (!tryConstantToUnsignedInt(&degP, p->deg)) return 0;
  if (!tryConstantToUnsignedInt(&degQ, q->deg)) return 0;

  /* Get maximum of both degrees */
  deg = degP;
  if (degQ > deg) deg = degQ;

  /* Do nothing if maximum of both degrees is too small */
  if (deg < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_INTEGER_MIN_DEGREE)) return 0;

  /* Do nothing if sum of the degrees does not hold on an unsigned integer */
  if ((degP + degQ) < degP) return 0;

  /* Do nothing if we cannot ascertain that all coefficients are integer */
  if (!sparsePolynomialCoefficientsAreInteger(p, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreInteger(q, 0)) return 0;

  /* Decompose the polynomials p and q as follows:

     p = fp * sum_i=0^kp cp_i * x^dp_i    with fp and all cp_i in mpz_t and dp_i in unsigned int

     q = fq * sum_i=0^kq cq_i * x^dq_i    with fq and all cq_i in mpz_t and dq_i in unsigned int

     Also compute sp and sq such that

     abs(cp_i) < 2^sp for all i  and

     abs(cq_i) < 2^sq for all i.

     Bail out if this decomposition is not possible.

  */
  __sollyaRecycleMpzInit(fp);
  if (!__sparsePolynomialMulOptimizedIntegerDecompose(fp, &cp, &dp, &kp, &sp, p)) {
    __sollyaRecycleMpzClear(fp);
    return 0;
  }
  __sollyaRecycleMpzInit(fq);
  if (!__sparsePolynomialMulOptimizedIntegerDecompose(fq, &cq, &dq, &kq, &sq, q)) {
    __sollyaRecycleMpzClear(fp);
    for (i=0u;i<kp;i++) {
      __sollyaRecycleMpzClear(cp[i]);
    }
    safeFree(cp);
    safeFree(dp);
    __sollyaRecycleMpzClear(fq);
    return 0;
  }

  /* Compute the number of 64 bit words maximally needed to hold the
     absolute value of the coefficients of p and q.

     wp = ceil(sp / 64)
     wq = ceil(sq / 64)

  */
  wp = (size_t) ((sp + ((mp_bitcnt_t) (64 - 1))) / ((mp_bitcnt_t) 64));
  wq = (size_t) ((sq + ((mp_bitcnt_t) (64 - 1))) / ((mp_bitcnt_t) 64));

  /* Now we know that absolute values of the coefficients of the
     product p * q hold on wp + wq + sigma words of 64 bits, where
     sigma is given by

     sigma = ceil(log2(max(dp, dq) + 1)/64).

     As dp < 2^(8 * sizeof(unsigned int)) and dq < 2^(8 * sizeof(unsigned int)),
     sigma can by computed as

     sigma = ceil(8 * sizeof(unsigned int) / 64).

     Let

     w = wp + wq + sigma.
  */
  w = wp + wq + (((((size_t) 8) * sizeof(unsigned int)) + ((size_t) (64 - 1))) / ((size_t) 64));

  /* If w is not at least 1, bail out */
  if (w < ((size_t) 1)) {
    __sollyaRecycleMpzClear(fp);
    for (i=0u;i<kp;i++) {
      __sollyaRecycleMpzClear(cp[i]);
    }
    safeFree(cp);
    safeFree(dp);
    __sollyaRecycleMpzClear(fq);
    for (i=0u;i<kq;i++) {
      __sollyaRecycleMpzClear(cq[i]);
    }
    safeFree(cq);
    safeFree(dq);
    return 0;
  }

  /* Let p = pp - pn and q = qp - qn where pp, pn, qp, qn are
     polynomials with non-negative integer coefficients.

     Perform Kronecker substitution, i.e. compute four non-negative integers
     a,b,c,d such that

     a = pp(2^w),

     b = pn(2^w),

     c = qp(2^w) and

     d = qn(2^w).

     If for some reason one of the evaluation steps fails, just bail
     out.

  */
  __sollyaRecycleMpzInit(a);
  __sollyaRecycleMpzInit(b);
  __sollyaRecycleMpzInit(c);
  __sollyaRecycleMpzInit(d);
  if (!__sparsePolynomialMulOptimizedIntegerKronecker(a, b, cp, dp, kp, w)) {
    __sollyaRecycleMpzClear(d);
    __sollyaRecycleMpzClear(c);
    __sollyaRecycleMpzClear(b);
    __sollyaRecycleMpzClear(a);
    __sollyaRecycleMpzClear(fq);
    __sollyaRecycleMpzClear(fp);
    for (i=0u;i<kp;i++) {
      __sollyaRecycleMpzClear(cp[i]);
    }
    safeFree(cp);
    safeFree(dp);
    for (i=0u;i<kq;i++) {
      __sollyaRecycleMpzClear(cq[i]);
    }
    safeFree(cq);
    safeFree(dq);
    return 0;
  }
  if (!__sparsePolynomialMulOptimizedIntegerKronecker(c, d, cq, dq, kq, w)) {
    __sollyaRecycleMpzClear(d);
    __sollyaRecycleMpzClear(c);
    __sollyaRecycleMpzClear(b);
    __sollyaRecycleMpzClear(a);
    __sollyaRecycleMpzClear(fq);
    __sollyaRecycleMpzClear(fp);
    for (i=0u;i<kp;i++) {
      __sollyaRecycleMpzClear(cp[i]);
    }
    safeFree(cp);
    safeFree(dp);
    for (i=0u;i<kq;i++) {
      __sollyaRecycleMpzClear(cq[i]);
    }
    safeFree(cq);
    safeFree(dq);
    return 0;
  }

  /* Now free the coefficient and degree arrays as we do not need them
     anymore.
  */
  for (i=0u;i<kp;i++) {
    __sollyaRecycleMpzClear(cp[i]);
  }
  safeFree(cp);
  safeFree(dp);
  for (i=0u;i<kq;i++) {
    __sollyaRecycleMpzClear(cq[i]);
  }
  safeFree(cq);
  safeFree(dq);

  /* Now compute

     r = a * c + b * d

     and

     s = a * d + b * c.

     These four multiplications do the real work.

  */
  __sollyaRecycleMpzInit(r);
  __sollyaRecycleMpzInit(s);
  __sollyaRecycleMpzInit(t1);
  __sollyaRecycleMpzInit(t2);
  mpz_mul(t1, a, c);
  mpz_mul(t2, b, d);
  mpz_add(r, t1, t2);
  mpz_mul(t1, a, d);
  mpz_mul(t2, b, c);
  mpz_add(s, t1, t2);
  __sollyaRecycleMpzClear(t1);
  __sollyaRecycleMpzClear(t2);

  /* Clear a,b,c and d, as we don't need them anymore */
  __sollyaRecycleMpzClear(d);
  __sollyaRecycleMpzClear(c);
  __sollyaRecycleMpzClear(b);
  __sollyaRecycleMpzClear(a);

  /* Now perform a lifting from the integers r and s
     to two polynomials g and h such that

     g(2^w) = r

     and

     h(2^w) = s.

     If one of these operations does not work, just bail out.
  */
  if (!__sparsePolynomialMulOptimizedIntegerLift(&g, r, w)) {
    __sollyaRecycleMpzClear(fp);
    __sollyaRecycleMpzClear(fq);
    __sollyaRecycleMpzClear(r);
    __sollyaRecycleMpzClear(s);
    return 0;
  }
  if (!__sparsePolynomialMulOptimizedIntegerLift(&h, s, w)) {
    sparsePolynomialFree(g);
    __sollyaRecycleMpzClear(fp);
    __sollyaRecycleMpzClear(fq);
    __sollyaRecycleMpzClear(r);
    __sollyaRecycleMpzClear(s);
    return 0;
  }


  /* Now compute z = g - h */
  z = sparsePolynomialSub(g, h);

  /* Free g and h as we don't need them anymore. */
  sparsePolynomialFree(g);
  sparsePolynomialFree(h);

  /* Now multiply z by fp * fq */
  mpz_mul(fp, fp, fq);
  fpfqp = sparsePolynomialFromMpzConstant(fp);
  __sollyaRecycleMpzClear(fp);
  __sollyaRecycleMpzClear(fq);
  __sollyaRecycleMpzClear(r);
  __sollyaRecycleMpzClear(s);
  rr = sparsePolynomialMul(fpfqp, z);
  sparsePolynomialFree(z);
  sparsePolynomialFree(fpfqp);

  /* Set the result and indicate success */
  *res = rr;
  return 1;
}


static inline int __sparsePolynomialMulOptimized(sparse_polynomial_t *r, sparse_polynomial_t p, sparse_polynomial_t q) {
  unsigned int degP, degQ;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Do nothing if monomial count of polynomials is too small */
  if (p->monomialCount < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_MONOMIAL_COUNT)) return 0;
  if (q->monomialCount < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_MONOMIAL_COUNT)) return 0;

  /* Try to represent the degrees as unsigned machine integers */
  if (!tryConstantToUnsignedInt(&degP, p->deg)) return 0;
  if (!tryConstantToUnsignedInt(&degQ, q->deg)) return 0;

  /* Do nothing if degree of polynomials is too small */
  if (degP < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_DEGREE)) return 0;
  if (degQ < ((unsigned int) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_DEGREE)) return 0;

  /* Do nothing if sparsity is too high */
  if (p->monomialCount < ((unsigned int) (((double) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_SPARSITY) * ((double) degP)))) return 0;
  if (q->monomialCount < ((unsigned int) (((double) SPARSE_POLYNOMIAL_MUL_OPTIMIZED_MIN_SPARSITY) * ((double) degQ)))) return 0;

  /* Here, it seems reasonable to try certain special algorithms

     Add additional optimized multiplication functions here. Add them
     at the head of the list of possible methods.

  */
  if (__sparsePolynomialMulOptimizedInteger(r, p, q)) return 1;
  if (__sparsePolynomialMulOptimizedRational(r, p, q)) return 1;
  if (__sparsePolynomialMulOptimizedKaratsuba(r, p, q)) return 1;

  /* No optimized method worked, let regular multiplication take over */
  return 0;
}

static inline sparse_polynomial_t sparsePolynomialMul(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t res;
  unsigned int i,j,startSize,g,d;
  constant_t pp, md, t;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Handle multiplication of zero polynomial */
  if (sparsePolynomialIsConstantZero(p, 0)) {
    return sparsePolynomialFromCopy(p);
  }
  if (sparsePolynomialIsConstantZero(q, 0)) {
    return sparsePolynomialFromCopy(q);
  }

  /* Handle multiplication of polynomial one */
  if (sparsePolynomialIsConstantOne(p, 0)) {
    return sparsePolynomialFromCopy(q);
  }
  if (sparsePolynomialIsConstantOne(q, 0)) {
    return sparsePolynomialFromCopy(p);
  }

  /* Try optimized multiplication */
  if (__sparsePolynomialMulOptimized(&res, p, q)) {
    return res;
  }

  /* General case

     Start by determining the maximum number of coefficients. This number is
     upper bounded by

     min(p->monomialCount * q->monomialCount, deg(p) + deg(q) + 1)

  */
  if (!tryExactUnsignedIntMultiplication(&startSize,
					 p->monomialCount, q->monomialCount)) {
    startSize = UINT_MAX;
  }
  t = constantAdd(p->deg, q->deg);
  if (tryConstantToUnsignedInt(&d, t)) {
    d++;
    if (d != 0u) {
      if (d < startSize) startSize = d;
    }
  }
  constantFree(t);

  /* Allocate the result polynomial */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->coeffs = (constant_t *) safeCalloc(startSize,
					  sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize,
						   sizeof(constant_t));
  res->monomialCount = 0;

  /* Produce all partial products p_i * q_i * x^(m_i + n_i) */
  g = 0;
  for (i=0u;i<p->monomialCount;i++) {
    for (j=0u;j<q->monomialCount;j++) {
      pp = constantMul(p->coeffs[i], q->coeffs[j]);
      if (!constantIsZero(pp, 0)) {
	md = constantAdd(p->monomialDegrees[i],q->monomialDegrees[j]);
	__sparsePolynomialAddMonomialToArrays(res->coeffs, res->monomialDegrees,
					      &(res->monomialCount), &g, pp, md);
      } else {
	constantFree(pp);
      }
    }
  }

  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials multiplied gave zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  return res;
}

static inline sparse_polynomial_t sparsePolynomialNeg(sparse_polynomial_t p) {
  unsigned int i;
  sparse_polynomial_t res;

  if (p == NULL) return NULL;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->deg = constantFromCopy(p->deg);
  res->monomialCount = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<res->monomialCount;i++) {
    res->coeffs[i] = constantNeg(p->coeffs[i]);
    res->monomialDegrees[i] = constantFromCopy(p->monomialDegrees[i]);
  }
  __sparsePolynomialAdjustDegree(res);
  return res;
}

static inline sparse_polynomial_t sparsePolynomialCompose(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t res, t, m;
  constant_t d;
  unsigned int i;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) return sparsePolynomialFromIntConstant(0);

  /* Perform Horner evaluation of p in q */
  res = sparsePolynomialFromConstant(p->coeffs[p->monomialCount-1u]);
  for (i=p->monomialCount-1u;i>=1u;i--) {
    d = constantSub(p->monomialDegrees[i], p->monomialDegrees[i-1u]);
    if (!sparsePolynomialPowConstant(&m, q, d)) {
      sollyaFprintf(stderr,"Error: sparsePolynomialCompose: monomial degrees not appropriately ordered\n");
      exit(1);
    }
    t = sparsePolynomialMul(res, m);
    sparsePolynomialFree(res);
    res = t;
    sparsePolynomialFree(m);
    constantFree(d);
    t = sparsePolynomialAddConstant(res, p->coeffs[i-1u]);
    sparsePolynomialFree(res);
    res = t;
  }
  if (!sparsePolynomialPowConstant(&m, q, p->monomialDegrees[0u])) {
    sollyaFprintf(stderr,"Error: sparsePolynomialCompose: monomial degrees not appropriately ordered\n");
    exit(1);
  }
  t = sparsePolynomialMul(res, m);
  sparsePolynomialFree(res);
  res = t;
  sparsePolynomialFree(m);

  return res;
}

static inline void __sparsePolynomialGetLeadingCoefficient(constant_t *c, constant_t *d, sparse_polynomial_t *r, sparse_polynomial_t p) {
  sparse_polynomial_t res;
  unsigned int i;

  /* Handle stupid cases */
  if (p == NULL) {
    *c = NULL;
    *d = NULL;
    *r = NULL;
    return;
  }

  /* Handle a strange case that should never happen */
  if (p->monomialCount == 0u) {
    *c = constantFromInt(0);
    *d = constantFromInt(0);
    *r = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* Handle the case where the polynomial has just one monomial */
  if (p->monomialCount == 1u) {
    *c = constantFromCopy(p->coeffs[0]);
    *d = constantFromCopy(p->monomialDegrees[0]);
    *r = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* Here, the polynomial has at least two monomials */
  *c = constantFromCopy(p->coeffs[p->monomialCount - 1u]);
  *d = constantFromCopy(p->monomialDegrees[p->monomialCount - 1u]);
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = p->monomialCount - 1u;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<res->monomialCount;i++) {
    res->coeffs[i] = constantFromCopy(p->coeffs[i]);
    res->monomialDegrees[i] = constantFromCopy(p->monomialDegrees[i]);
  }
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount-1u]);
  __sparsePolynomialAdjustDegree(res);
  *r = res;
}

static inline sparse_polynomial_t __sparsePolynomialFromMonomial(constant_t c, constant_t d) {
  sparse_polynomial_t res;
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = 1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->coeffs[0] = constantFromCopy(c);
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees[0] = constantFromCopy(d);
  res->deg = constantFromCopy(d);
  return res;
}

/* a = c * x^d * quot + rest */
static inline int __sparsePolynomialDivOneMonomial(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, constant_t c, constant_t d) {
  sparse_polynomial_t q, r;
  unsigned int i;

  /* Handle stupid cases */
  if (a == NULL) return 0;
  if (c == NULL) return 0;
  if (d == NULL) return 0;

  /* Refuse to work if a has no monomials */
  if (a->monomialCount == 0u) return 0;

  /* Refuse to work if c is zero */
  if (constantIsZero(c, 1)) return 0;

  /* Handle the easy case when d is zero and c is one */
  if (constantIsZero(d, 0) &&
      constantIsOne(c, 0)) {
    *quot = sparsePolynomialFromCopy(a);
    *rest = sparsePolynomialFromIntConstant(0);
    return 1;
  }

  /* Handle the general case */
  q = __sparsePolynomialAllocate();
  q->refCount = 1;
  q->hash.hasHash = 0;
  q->monomialCount = 0u;
  q->coeffs = (constant_t *) safeCalloc((size_t) a->monomialCount, sizeof(constant_t));
  q->monomialDegrees = (constant_t *) safeCalloc((size_t) a->monomialCount, sizeof(constant_t));
  r = __sparsePolynomialAllocate();
  r->refCount = 1;
  r->hash.hasHash = 0;
  r->monomialCount = 0u;
  r->coeffs = (constant_t *) safeCalloc((size_t) a->monomialCount, sizeof(constant_t));
  r->monomialDegrees = (constant_t *) safeCalloc((size_t) a->monomialCount, sizeof(constant_t));
  for (i=0u;i<a->monomialCount;i++) {
    if (constantIsGreaterOrEqual(a->monomialDegrees[i],d,0)) {
      q->coeffs[q->monomialCount] = constantDiv(a->coeffs[i],c);
      q->monomialDegrees[q->monomialCount] = constantSub(a->monomialDegrees[i],d);
      (q->monomialCount)++;
    } else {
      r->coeffs[r->monomialCount] = constantFromCopy(a->coeffs[i]);
      r->monomialDegrees[r->monomialCount] = constantFromCopy(a->monomialDegrees[i]);
      (r->monomialCount)++;
    }
  }
  if (q->monomialCount == 0u) {
    q->coeffs[q->monomialCount] = constantFromInt(0);
    q->monomialDegrees[q->monomialCount] = constantFromInt(0);
    (q->monomialCount)++;
  }
  if (r->monomialCount == 0u) {
    r->coeffs[r->monomialCount] = constantFromInt(0);
    r->monomialDegrees[r->monomialCount] = constantFromInt(0);
    (r->monomialCount)++;
  }
  q->deg = constantFromCopy(q->monomialDegrees[q->monomialCount - 1u]);
  r->deg = constantFromCopy(r->monomialDegrees[r->monomialCount - 1u]);
  if (q->monomialCount != a->monomialCount) {
    q->coeffs = (constant_t *) safeRealloc(q->coeffs,
					   ((size_t) (q->monomialCount)) * sizeof(constant_t));
    q->monomialDegrees = (constant_t *) safeRealloc(q->monomialDegrees,
						    ((size_t) (q->monomialCount)) * sizeof(constant_t));
  }
  if (r->monomialCount != a->monomialCount) {
    r->coeffs = (constant_t *) safeRealloc(r->coeffs,
					   ((size_t) (r->monomialCount)) * sizeof(constant_t));
    r->monomialDegrees = (constant_t *) safeRealloc(r->monomialDegrees,
						    ((size_t) (r->monomialCount)) * sizeof(constant_t));
  }
  __sparsePolynomialAdjustDegree(q);
  __sparsePolynomialAdjustDegree(r);

  /* Set the result and indicate success */
  *quot = q;
  *rest = r;
  return 1;
}

static inline void __sparsePolynomialDivBaseCase(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, sparse_polynomial_t b) {
  sparse_polynomial_t q, r, rt, bt, t1, t2, qp, recprPB;
  constant_t rc, rd, bc, bd, qc, qd, recprB, one;

  /* Handle stupid cases */
  if ((a == NULL) || (b == NULL)) {
    *quot = NULL;
    *rest = NULL;
    return;
  }

  /* If both polynomials are equal, the quotient is one, the rest is
     zero.
  */
  if (sparsePolynomialEqual(a, b, 0)) {
    *quot = sparsePolynomialFromIntConstant(1);
    *rest = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* If b is constant, do some special handling */
  if (sparsePolynomialConstantGetConstant(&bc, b)) {
    if (constantIsOne(bc, 0)) {
      *quot = sparsePolynomialFromCopy(a);
      *rest = sparsePolynomialFromIntConstant(0);
    } else {
      if (!constantIsZero(bc, 1)) {
	one = constantFromInt(1);
	recprB = constantDiv(one, bc);
	constantFree(one);
	recprPB = sparsePolynomialFromConstant(recprB);
	constantFree(recprB);
	*quot = sparsePolynomialMul(a, recprPB);
	*rest = sparsePolynomialFromIntConstant(0);
	sparsePolynomialFree(recprPB);
      } else {
	*quot = sparsePolynomialFromIntConstant(0);
	*rest = sparsePolynomialFromCopy(a);
      }
    }
    constantFree(bc);
    return;
  }

  /* If b has just one monomial, try to do some special handling */
  if (b->monomialCount == 1u) {
    if (__sparsePolynomialDivOneMonomial(&q, &r, a, b->coeffs[0u], b->monomialDegrees[0u])) {
      *quot = q;
      *rest = r;
      return;
    }
  }

  /* Initialize q and r to

     q = 0
     r = a

     such that

     a = q * b + r

  */
  q = sparsePolynomialFromIntConstant(0);
  r = sparsePolynomialFromCopy(a);

  /* Euclidean division */
  __sparsePolynomialGetLeadingCoefficient(&bc,&bd,&bt,b);
  while ((!constantIsGreater(b->deg,r->deg,1)) &&
	 (!sparsePolynomialIsConstantZero(r,1))) {
    __sparsePolynomialGetLeadingCoefficient(&rc,&rd,&rt,r);
    qc = constantDiv(rc,bc);
    constantFree(rc);
    qd = constantSub(rd,bd);
    constantFree(rd);
    qp = __sparsePolynomialFromMonomial(qc,qd);
    constantFree(qc);
    constantFree(qd);
    t1 = sparsePolynomialAdd(q, qp);
    sparsePolynomialFree(q);
    q = t1;
    t1 = sparsePolynomialMul(qp,bt);
    t2 = sparsePolynomialSub(rt,t1);
    sparsePolynomialFree(t1);
    sparsePolynomialFree(rt);
    sparsePolynomialFree(qp);
    sparsePolynomialFree(r);
    r = t2;
  }
  constantFree(bc);
  constantFree(bd);
  sparsePolynomialFree(bt);

  /* Set the result polynomials */
  *quot = q;
  *rest = r;
}

static inline int __sparsePolynomialDivIntegerCoefficientsOptimizedBounds(mp_bitcnt_t *s, sparse_polynomial_t a) {
  mpz_t c;
  mp_bitcnt_t ss, sc;
  unsigned int i;
  
  /* Handle stupid input */
  if (a == NULL) return 0;

  /* Handle strange case when a has no monomials */
  if (a->monomialCount == 0u) return 0;

  /* Initialize a temporary MPZ */
  __sollyaRecycleMpzInit(c);

  /* Get maximum bit count of all coefficients */
  ss = (mp_bitcnt_t) 1;
  for (i=0u;i<a->monomialCount;i++) {
    if (!tryConstantToMpz(c,a->coeffs[i])) {
      /* Could not evaluate a coefficient to a MPZ */
      __sollyaRecycleMpzClear(c);
      return 0;
    }
    sc = (mp_bitcnt_t) mpz_sizeinbase(c, 2);
    if (sc > ss) ss = sc;
  }
  
  /* Clear the temporary, set the result and signal success */
  __sollyaRecycleMpzClear(c);
  *s = ss;
  return 1;
}

static inline sparse_polynomial_t __sparsePolynomialDivIntegerCoefficientsOptimizedConstructOffset(mp_bitcnt_t s, unsigned int deg) {
  sparse_polynomial_t res;
  constant_t c;
  unsigned int i;
  mpz_t cc;

  __sollyaRecycleMpzInit(cc);
  mpz_set_ui(cc, 1u);
  mpz_mul_2exp(cc, cc, s);
  c = constantFromMpz(cc);
  __sollyaRecycleMpzClear(cc);
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = deg + ((unsigned int) 1u);
  res->deg = constantFromUnsignedInt(deg);
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<res->monomialCount;i++) {
    res->coeffs[i] = constantFromCopy(c);
    res->monomialDegrees[i] = constantFromUnsignedInt(i);
  }
  __sparsePolynomialAdjustDegree(res);
  constantFree(c);
  return res;
}

/* We need to evaluate the polynomial p with integer coefficients at
   2^w

   We need to make sure that we perform this operation in linear time.

*/
static inline int __sparsePolynomialDivIntegerCoefficientsOptimizedEvaluate(mpz_t y, sparse_polynomial_t p, size_t w) {
  mpz_t f, a, b;
  mpz_t *c;
  unsigned int *d;
  mp_bitcnt_t s, ww, wwww;
  size_t www;
  unsigned int i, k;
  int ko;

  /* Decompose the polynomial p as follows:

     p = f * sum_i=0^k c_i * x^d_i    with f and all c_i in mpz_t and d_i in unsigned int

     Also compute s

     abs(c_i) < 2^s for all i.

     Bail out if this decomposition is not possible.

  */  
  __sollyaRecycleMpzInit(f);
  if (!__sparsePolynomialMulOptimizedIntegerDecompose(f, &c, &d, &k, &s, p)) {
    __sollyaRecycleMpzClear(f);
    return 0;
  }

  /* Now check if ceil(s / 64) <= w 

     If not, bail out.

  */
  ww = s / ((mp_bitcnt_t) 64);
  if ((s % ((mp_bitcnt_t) 64)) != ((mp_bitcnt_t) 0)) ww++;
  www = (size_t) ww;
  wwww = (mp_bitcnt_t) www;
#if defined(__clang__)
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wtautological-overlap-compare"
#endif
  ko = (((ww < ((mp_bitcnt_t) 1)) && (ww != ((mp_bitcnt_t) 0))) ||
	(wwww != ww) ||
	(www > w));
#if defined(__clang__)
#   pragma clang diagnostic pop
#endif
  if (ko) {
    __sollyaRecycleMpzClear(f);
    for (i=0u;i<k;i++) {
      __sollyaRecycleMpzClear(c[i]);
    }
    safeFree(c);
    safeFree(d);
    return 0;
  }

  /* Now let p = pp -  pn where pp has all the positive and zero
     coefficients of p and pn the negative coefficients of pn, with
     their sign flipped.

     Compute a and b such that 

     f * a = pp(2^w) 
     
     and 

     f * b = pn(2^w)

     Then

     f * (a - b) = p(2^w)

     If the evaluation does not work, bail out.

  */
  __sollyaRecycleMpzInit(a);
  __sollyaRecycleMpzInit(b);
  if (!__sparsePolynomialMulOptimizedIntegerKronecker(a, b, c, d, k, w)) {
    __sollyaRecycleMpzClear(f);
    __sollyaRecycleMpzClear(a);
    __sollyaRecycleMpzClear(b);
    for (i=0u;i<k;i++) {
      __sollyaRecycleMpzClear(c[i]);
    }
    safeFree(c);
    safeFree(d);
    return 0;
  }

  /* Compute y = f * (a - b) */
  mpz_sub(y, a, b);
  mpz_mul(y, f, y);

  /* Clear the temporaries */
  __sollyaRecycleMpzClear(f);
  __sollyaRecycleMpzClear(a);
  __sollyaRecycleMpzClear(b);
  for (i=0u;i<k;i++) {
    __sollyaRecycleMpzClear(c[i]);
  }
  safeFree(c);
  safeFree(d);

  /* Signal success */
  return 1;
}

/* For this function, a and b are two polynomials with integer
   coefficients such that their polynomial quotient is a polynomial
   with integer coefficients that are all positive or zero. The 
   degree of b is no greater than the one of a. The degrees of 
   both polynomials are each less than 2^64.

   We perform no special case handling in this algorithm as we
   are already deep in a chain of special algorithms.
   
   We recycle code that we also use for Kronecker substitution for
   multiplication of polynomials with integer coefficients.

*/
static inline int __sparsePolynomialDivIntegerCoefficientsOptimizedInner(sparse_polynomial_t *quot, sparse_polynomial_t a, sparse_polynomial_t b) {
  mp_bitcnt_t sa, sb, saaa, sbbb;
  uint64_t saa, sbb, s, ww, www;
  size_t w;
  mpz_t aa, bb, qq;
  sparse_polynomial_t q;

  /* Try to compute bounds sa and sb such that every integer
     coefficient of a is bounded in magnitude by 2^sa and every
     integer coefficient of b is bounded in magnitude by 2^sb.

     If we cannot compute these bounds, we just bail out.

  */
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedBounds(&sa, a)) return 0;
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedBounds(&sb, b)) return 0;

  /* If the bounds are less than 1, something went wrong and we bail out. */
  if (sa < ((mp_bitcnt_t) 1)) return 0;
  if (sb < ((mp_bitcnt_t) 1)) return 0;

  /* Compute a bound on the coefficients of the quotient of the polynomial
     division of a by b. We know that the degrees of a and b are bounded by 2^64.

     We use the bound 

     s = sa + sb + log2((2^64)^2) + 2 = sa + sb + 130.

     If we cannot express the bound on a uint64_t, we bail out.

  */
  saa = (uint64_t) sa;
  sbb = (uint64_t) sb;
  saaa = (mp_bitcnt_t) saa;
  sbbb = (mp_bitcnt_t) sbb;
  if (sa != saaa) return 0;
  if (sb != sbbb) return 0;
  s = saa + sbb;
  if (s < saa) return 0;
  s += (uint64_t) 130u;
  if (s < ((uint64_t) 130u)) return 0;

  /* We now compute the number of 64bit words w necessary to 
     hold all positive or zero coefficients of the quotient.

     We have

     w = ceil(s / 64)

     We need to be careful with the computation of w, 
     as s + 64 - 1 could overflow on a uint64_t.

     If w does not hold on a size_t, we bail out.

  */
  ww = s >> 6;
  if ((s & ((((uint64_t) 1u) << 6) - ((uint64_t) 1u))) != ((uint64_t) 0))
    ww++;
  w = (size_t) ww;
  www = (uint64_t) w;
  if (www != ww) return 0;

  /* If w is less than 1, we set w to 1. */
  if (w < ((size_t) 1)) w = (size_t) 1;

  /* We now evaluate the polynomials a and b at 2^(64 * w) 

     If one of these evaluations does not work, we bail out.

  */
  __sollyaRecycleMpzInit(aa);
  __sollyaRecycleMpzInit(bb);
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedEvaluate(aa, a, w)) {
    __sollyaRecycleMpzClear(aa);
    __sollyaRecycleMpzClear(bb);
    return 0;
  }
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedEvaluate(bb, b, w)) {
    __sollyaRecycleMpzClear(aa);
    __sollyaRecycleMpzClear(bb);
    return 0;
  }

  
  /* Now we compute the integer quotient of aa divided by bb. 

     This integer division operation does the actual work.

  */
  __sollyaRecycleMpzInit(qq);  
  mpz_tdiv_q(qq, aa, bb);
  __sollyaRecycleMpzClear(aa);
  __sollyaRecycleMpzClear(bb);

  /* If qq is negative, something went wrong and we bail out */
  if (mpz_sgn(qq) < 0) {
    __sollyaRecycleMpzClear(qq);
    return 0;
  }

  /* We compute a polynomial q such that q(2^w) = qq */
  if (!__sparsePolynomialMulOptimizedIntegerLift(&q, qq, w)) {
    __sollyaRecycleMpzClear(qq);
    return 0;
  }
  __sollyaRecycleMpzClear(qq);
  
  /* Set the result and signal success */
  *quot = q;
  return 1;
}

static inline int __sparsePolynomialDivIntegerCoefficientsOptimized(sparse_polynomial_t *quot, sparse_polynomial_t a, sparse_polynomial_t b) {
  unsigned int degA, degB;
  mp_bitcnt_t sa, sb, s, saaa, sbbb;
  uint64_t saa, sbb, ss, sss, tt, tttt;
  sparse_polynomial_t d, t, p, qq, q;
  unsigned int ttt;
  
  /* Handle stupid cases */
  if (a == NULL) return 0;
  if (b == NULL) return 0;

  /* Do nothing if we cannot ascertain that all coefficients are integer */
  if (!sparsePolynomialCoefficientsAreInteger(a, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreInteger(b, 0)) return 0;

  /* If we cannot represent the degree of both polynomials as unsigned integers,
     we cannot use this special algorithm.
  */
  if (!tryConstantToUnsignedInt(&degA, a->deg)) return 0;
  if (!tryConstantToUnsignedInt(&degB, b->deg)) return 0;

  /* If the degree of b is greater than the one of a, we cannot use
     this special algorithm.
  */
  if (degB > degA) return 0;

  /* If the degree of b is not at least 1, we cannot use this special
     algorithm. 
  */
  if (degB < ((uint64_t) 1u)) return 0;

  /* If degA - degB + 1 is greater than what we can represent on an
     unsigned int, we cannot use this special algorithm. 
  */
  tt = degA - degB;
  tt++;
  if (tt < ((uint64_t) 1u)) return 0;
  ttt = (unsigned int) tt;
  tttt = (uint64_t) ttt;
  if (tttt != tt) return 0;

  /* If on the machine we are on an unsigned int holds more than 
     64 bit, we bail out as well.
  */
  if (sizeof(unsigned int) > ((size_t) 8)) return 0;
  
  /* Try to compute bounds sa and sb such that every integer
     coefficient of a is bounded in magnitude by 2^sa and every
     integer coefficient of b is bounded in magnitude by 2^sb.

     If we cannot compute these bounds, we just bail out.

  */
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedBounds(&sa, a)) return 0;
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedBounds(&sb, b)) return 0;

  /* If the bounds are less than 1, something went wrong and we bail out. */
  if (sa < ((mp_bitcnt_t) 1)) return 0;
  if (sb < ((mp_bitcnt_t) 1)) return 0;

  /* Compute a bound on the coefficients of the quotient of the polynomial
     division of a by b. We know that the degrees of a and b are bounded by 2^64.

     We use the bound 

     s = sa + sb + log2((2^64)^2) + 2 = sa + sb + 130.

     If we cannot express the bound on a mp_bitcnt_t, we bail out.

  */
  saa = (uint64_t) sa;
  sbb = (uint64_t) sb;
  saaa = (mp_bitcnt_t) saa;
  sbbb = (mp_bitcnt_t) sbb;
  if (sa != saaa) return 0;
  if (sb != sbbb) return 0;
  ss = saa + sbb;
  if (ss < saa) return 0;
  ss += (uint64_t) 130u;
  if (ss < ((uint64_t) 130u)) return 0;
  s = (mp_bitcnt_t) ss;
#if defined(__clang__)
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wtautological-overlap-compare"
#endif
  if ((s < ((mp_bitcnt_t) 1)) && (s != ((mp_bitcnt_t) 0))) return 0;
#if defined(__clang__)
#   pragma clang diagnostic pop
#endif
  sss = (uint64_t) s;
  if (sss != ss) return 0;

  /* We now construct the polynomial 

     d = sum 2^s * x^i

     with degree degA - degB.

  */
  d = __sparsePolynomialDivIntegerCoefficientsOptimizedConstructOffset(s, ((unsigned int) (degA - degB)));

  /* We now compute 

     p = a + d * b

  */
  t = sparsePolynomialMul(d, b);
  p = sparsePolynomialAdd(a, t);
  sparsePolynomialFree(t);

  /* We now divide p by b. 

     If that does not work, we bail out.

  */
  if (!__sparsePolynomialDivIntegerCoefficientsOptimizedInner(&qq, p, b)) {
    sparsePolynomialFree(d);
    sparsePolynomialFree(p);
    return 0;
  }

  /* Here, qq is the polynomial quotient of p by b. 

     We subtract d from qq to get the quotient of a by b.
     
  */
  q = sparsePolynomialSub(qq, d);
  sparsePolynomialFree(p);
  sparsePolynomialFree(d);
  sparsePolynomialFree(qq);

  /* Set the result and signal success */
  *quot = q;
  return 1;
}

static inline void __sparsePolynomialDivIntegerCoefficientsBase(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, sparse_polynomial_t b) {
  sparse_polynomial_t q, r, t;

  if (__sparsePolynomialDivIntegerCoefficientsOptimized(&q, a, b)) {
    /* The optimized version of the code worked. We still need to
       compute the remainder. 
    */
    t = sparsePolynomialMul(q, b);
    r = sparsePolynomialSub(a, t);
    sparsePolynomialFree(t);

    /* Check that the remainder r has a degree less than b */
    if ((!constantIsGreater(b->deg,r->deg,0)) &&
	(!sparsePolynomialIsConstantZero(r,0))) {
      sparsePolynomialFree(q);
      sparsePolynomialFree(r);
      __sparsePolynomialDivBaseCase(&q, &r, a, b);
    }
    *quot = q;
    *rest = r;
  } else {
    /* The optimized version of the code did not work, just call the
       base case.
    */
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
  }
}

static inline void __sparsePolynomialDivIntegerCoefficientsInnerHelper(uint64_t *h, uint64_t *l, uint64_t a, uint64_t b) {
  uint64_t ahi, alo, bhi, blo, ahibhi, ahiblo, alobhi, aloblo, ahiblohi, ahiblolo, alobhihi, alobhilo, rh, rl;

  ahi = a >> 32;
  alo = a - (ahi << 32);
  bhi = b >> 32;
  blo = b - (bhi << 32);

  ahibhi = ahi * bhi;
  ahiblo = ahi * blo;
  alobhi = alo * bhi;
  aloblo = alo * blo;

  ahiblohi = ahiblo >> 32;
  ahiblolo = ahiblo - (ahiblohi << 32);
  alobhihi = alobhi >> 32;
  alobhilo = alobhi - (alobhihi << 32);

  ahiblolo <<= 32;
  alobhilo <<= 32;

  rh = ahibhi;
  rl = aloblo + ahiblolo;
  if (rl < ahiblolo) rh++;
  rl += alobhilo;
  if (rl < alobhilo) rh++;
  rh += ahiblohi;
  rh += alobhihi;

  *h = rh;
  *l = rl;
}

static inline int __sparsePolynomialDivIntegerCoefficientsInner(sparse_polynomial_t *quot, sparse_polynomial_t *rest,
								sparse_polynomial_t a, sparse_polynomial_t b, mpz_t g, unsigned int t) {
  unsigned long int tt;
  unsigned int ttt, ttttt;
  mpz_t gt;
  constant_t alpha, one, beta;
  sparse_polynomial_t alphaP, f, qq, rr, betaP, q, r;
  size_t s, sss;
  uint64_t tttt, ss, h, l;
  
  /* Here, a and b are polynomials with nothing but integer
     coefficients. g is the head coefficient of b.  Further t = degA -
     degB + 1, where degA is the degree of a and degB the degree of b.

     We try to compute g^t.

     If this powering does not work (typically it takes too much
     memory), we just bail out.

     Otherwise, we compute 

     f = g^t * b.

     Then we divide f by b. This division will have a quotient and a
     remainder that are both polynomials with integer coefficients.

     We then correct the quotient and the remainder by multiplying
     them by 1/g^t.

  */
  tt = (unsigned long int) t;
  ttt = (unsigned int) tt;
  if (ttt != t) return 0;

  /* Check if the powering will not explose the memory */
  s = mpz_sizeinbase(g, 2);
  ss = (uint64_t) s;
  sss = (size_t) ss;
  if (s != sss) return 0;
  tttt = (uint64_t) t;
  ttttt = (unsigned int) tttt;
  if (t != ttttt) return 0;
  __sparsePolynomialDivIntegerCoefficientsInnerHelper(&h, &l, ss, tttt);
  /* Here 2^64 * h + l = s * t, where s is the size in bit of g.

     Hence 2^64 * h + l is the size in bits of the power g^t.

     If the high word h is non-zero or if l is greater than a certain
     bound, we bail out.

  */
  if (h != ((uint64_t) 0u)) return 0;
  if (l > ((uint64_t) SPARSE_POLYNOMIAL_DIV_INTEGER_MAX_BITS_ALPHA)) return 0;
  
  /* Compute alpha = g^t */
  __sollyaRecycleMpzInit(gt);
  mpz_pow_ui(gt, g, tt);
  alpha = constantFromMpz(gt);
  __sollyaRecycleMpzClear(gt);

  /* Compute f = alpha * a */
  alphaP = sparsePolynomialFromConstant(alpha);
  f = sparsePolynomialMul(alphaP, a);
  sparsePolynomialFree(alphaP);

  /* Divide f by b, giving qq and rr */
  __sparsePolynomialDivIntegerCoefficientsBase(&qq, &rr, f, b);
  sparsePolynomialFree(f);

  /* Compute beta = 1/alpha */
  one = constantFromInt(1);
  beta = constantDiv(one, alpha);
  constantFree(one);
  constantFree(alpha);
  betaP = sparsePolynomialFromConstant(beta);
  constantFree(beta);

  /* Compute q = beta * qq and r = beta * rr */
  q = sparsePolynomialMul(betaP, qq);
  r = sparsePolynomialMul(betaP, rr);

  /* Free stuff */
  sparsePolynomialFree(betaP);
  sparsePolynomialFree(qq);
  sparsePolynomialFree(rr);

  /* Set the result and signal success */
  *quot = q;
  *rest = r;
  return 1;
}

static inline int __sparsePolynomialDivIntegerCoefficients(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, sparse_polynomial_t b) {
  unsigned int degA, degB, t;
  sparse_polynomial_t q, r, z;
  constant_t c, d;
  mpz_t cc;
  
  /* Handle stupid cases */
  if (a == NULL) return 0;
  if (b == NULL) return 0;

  /* Do nothing if we cannot ascertain that all coefficients are integer */
  if (!sparsePolynomialCoefficientsAreInteger(a, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreInteger(b, 0)) return 0;

  /* If we cannot represent the degree of both polynomials as unsigned integers, 
     we can do nothing but use the base case division 
  */
  if ((!tryConstantToUnsignedInt(&degA, a->deg)) ||
      (!tryConstantToUnsignedInt(&degB, b->deg))) {
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
    return 1;
  }

  /* Here, we have the degrees of the two polynomials. 

     If a is of a degree less than the one of b, we know the answer.

  */
  if (degA < degB) {
    *quot = sparsePolynomialFromIntConstant(0);
    *rest = sparsePolynomialFromCopy(a);
    return 1;
  }

  /* Here, degA >= degB 

     Compute 

     t = degA - degB + 1

     If that value does not hold on an unsigned integer, 
     just call the base case division.
     
  */
  t = degA - degB;
  t++;
  if (t == ((unsigned int) 0u)) {
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
    return 1;
  }

  /* Get the leading coefficient of b as an mpz_t. If 
     something goes wrong, call the base case division.
  */
  if (sparsePolynomialIsConstantZero(b, 1)) {
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
    return 1;
  }
  __sparsePolynomialGetLeadingCoefficient(&c, &d, &z, b);
  sparsePolynomialFree(z);
  constantFree(d);

  __sollyaRecycleMpzInit(cc);
  if (!tryConstantToMpz(cc, c)) {
    /* Could not evaluate the head coefficient of b to an mpz */
    constantFree(c);
    __sollyaRecycleMpzClear(cc);
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
    return 1;
  }
  constantFree(c);

  /* Here we know that the head coefficient of b is cc 

     We call an auxilliary function that does the rest of the work.
     If that function does not work, we call the base case division.
     
  */
  if (!__sparsePolynomialDivIntegerCoefficientsInner(&q, &r, a, b, cc, t)) {
    __sollyaRecycleMpzClear(cc);
    __sparsePolynomialDivBaseCase(&q, &r, a, b);
    *quot = q;
    *rest = r;
    return 1;
  }

  /* The special integer multiplication code worked, let's set the result */
  __sollyaRecycleMpzClear(cc);
  *quot = q;
  *rest = r;
  return 1;
}

static inline int __sparsePolynomialDivRationalCoefficients(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, sparse_polynomial_t b) {
  mpz_t lcm;
  sparse_polynomial_t lcmp, g, h, q, r, rcprLcmP, rr;
  constant_t one, lcmc, rcprLcm;
  
  /* Handle stupid cases */
  if (a == NULL) return 0;
  if (b == NULL) return 0;

  /* Do nothing if we cannot ascertain that all coefficients are rational */
  if (!sparsePolynomialCoefficientsAreRational(a, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreRational(b, 0)) return 0;

  /* Try to compute the lcm of the denominators of the fractions of
     both polynomials 
  */
  __sollyaRecycleMpzInit(lcm);
  mpz_set_ui(lcm, 1u);
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, a)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, b)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }
  
  /* If lcm is 0, bail out as something very bad is happening. */
  if (mpz_cmpabs_ui(lcm, 0u) == 0) {
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* Multiply both polynomials by the lcm, to make them polynomials
     with integer coefficients. As we are dividing polynomials, this
     multiplication does not change the Euclidian quotient but
     it does change the remainder.
  */
  lcmp = sparsePolynomialFromMpzConstant(lcm);
  g = sparsePolynomialMul(a, lcmp);
  h = sparsePolynomialMul(b, lcmp);
  sparsePolynomialFree(lcmp);

  /* Try integer division of g by h, producing q and r */
  if (!__sparsePolynomialDivIntegerCoefficients(&q, &r, g, h)) {
    /* Integer division did not work. Signal failure. */
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* Integer division worked. 

     The quotient is the same, but the remainder needs to be 
     multiplied by 1/lcm.

  */
  sparsePolynomialFree(g);
  sparsePolynomialFree(h);
  one = constantFromInt(1);
  lcmc = constantFromMpz(lcm);
  __sollyaRecycleMpzClear(lcm);
  rcprLcm = constantDiv(one, lcmc);
  constantFree(one);
  constantFree(lcmc);
  rcprLcmP = sparsePolynomialFromConstant(rcprLcm);
  constantFree(rcprLcm);
  rr = sparsePolynomialMul(r, rcprLcmP);
  sparsePolynomialFree(r);
  sparsePolynomialFree(rcprLcmP);  
  *quot = q;
  *rest = rr;
  return 1;
}

static inline void sparsePolynomialDiv(sparse_polynomial_t *quot, sparse_polynomial_t *rest, sparse_polynomial_t a, sparse_polynomial_t b) {
  constant_t bc, one, recprB;
  sparse_polynomial_t recprPB, q, r;
  
  /* Handle stupid cases */
  if ((a == NULL) || (b == NULL)) {
    *quot = NULL;
    *rest = NULL;
    return;
  }

  /* If both polynomials are equal, the quotient is one, the rest is
     zero.
  */
  if (sparsePolynomialEqual(a, b, 0)) {
    *quot = sparsePolynomialFromIntConstant(1);
    *rest = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* If b is constant, do some special handling */
  if (sparsePolynomialConstantGetConstant(&bc, b)) {
    if (constantIsOne(bc, 0)) {
      *quot = sparsePolynomialFromCopy(a);
      *rest = sparsePolynomialFromIntConstant(0);
    } else {
      if (!constantIsZero(bc, 1)) {
	one = constantFromInt(1);
	recprB = constantDiv(one, bc);
	constantFree(one);
	recprPB = sparsePolynomialFromConstant(recprB);
	constantFree(recprB);
	*quot = sparsePolynomialMul(a, recprPB);
	*rest = sparsePolynomialFromIntConstant(0);
	sparsePolynomialFree(recprPB);
      } else {
	*quot = sparsePolynomialFromIntConstant(0);
	*rest = sparsePolynomialFromCopy(a);
      }
    }
    constantFree(bc);
    return;
  }

  /* If b has just one monomial, try to do some special handling */
  if (b->monomialCount == 1u) {
    if (__sparsePolynomialDivOneMonomial(&q, &r, a, b->coeffs[0u], b->monomialDegrees[0u])) {
      *quot = q;
      *rest = r;
      return;
    }
  }

  /* Try special algorithm for polynomials with integer coefficients */
  if (__sparsePolynomialDivIntegerCoefficients(&q, &r, a, b)) {
    *quot = q;
    *rest = r;
    return;
  }

  /* Try special algorithm for polynomials with rational coefficients */
  if (__sparsePolynomialDivRationalCoefficients(&q, &r, a, b)) {
    *quot = q;
    *rest = r;
    return;
  }
  
  /* General case: use the base algorithm */
  __sparsePolynomialDivBaseCase(&q, &r, a, b);
  *quot = q;
  *rest = r;  
}

static inline void __sparsePolynomialCutIntoHalves(sparse_polynomial_t *r, sparse_polynomial_t *q, sparse_polynomial_t p) {
  unsigned int i, monomialCount1, monomialCount2;
  sparse_polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) {
    *r = NULL;
    *q = NULL;
    return;
  }

  /* Handle the case when p has no monomials */
  if (p->monomialCount == 0u) {
    *r = sparsePolynomialFromIntConstant(0);
    *q = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* Handle the case when p has only one monomial */
  if (p->monomialCount == 1u) {
    *r = sparsePolynomialFromCopy(p);
    *q = sparsePolynomialFromIntConstant(0);
    return;
  }

  /* Now, we know that we have at least 2 monomials. This implies that
     both polynomials have at least one monomial.
  */
  monomialCount1 = p->monomialCount >> 1;
  monomialCount2 = p->monomialCount - monomialCount1;

  /* Form polynomial p */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = monomialCount1;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<monomialCount1;i++) {
    res->coeffs[i] = constantFromCopy(p->coeffs[i]);
    res->monomialDegrees[i] = constantFromCopy(p->monomialDegrees[i]);
  }
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount-1u]);
  __sparsePolynomialAdjustDegree(res);
  *r = res;

  /* Form polynomial q */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = monomialCount2;
  res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
  for (i=0u;i<monomialCount2;i++) {
    res->coeffs[i] = constantFromCopy(p->coeffs[i + monomialCount1]);
    res->monomialDegrees[i] = constantFromCopy(p->monomialDegrees[i + monomialCount1]);
  }
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount-1u]);
  __sparsePolynomialAdjustDegree(res);
  *q = res;
}

/* If p is of the form p(x) = x^m * (c + d * x^n) then decompose p into
   c, m, d and n and return a non-zero value.

   Otherwise return 0 and don't touch the pointers c, m, d and n.
*/
static inline int sparsePolynomialDecomposeTwoMonomials(constant_t *c, constant_t *m, constant_t *d, constant_t *n,
							sparse_polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Check if there are really just two monomials */
  if (p->monomialCount != 2u) return 0;

  /* Here we are sure that we only have two monomials.

     Do the decomposition work.

  */
  *c = constantFromCopy(p->coeffs[0]);
  *d = constantFromCopy(p->coeffs[1]);
  *m = constantFromCopy(p->monomialDegrees[0]);
  *n = constantSub(p->monomialDegrees[1],p->monomialDegrees[0]);

  /* Signal success */
  return 1;
}

static inline sparse_polynomial_t __sparsePolynomialPowUnsignedIntAlternate(sparse_polynomial_t p, unsigned int n) {
  constant_t nC, bin;
  unsigned int i;
  sparse_polynomial_t r, q, rToTheI, qToTheNMinusI, binPoly, tempPoly1, tempPoly2, tempPoly3, res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle easy cases */
  if (n == 0u) return sparsePolynomialFromIntConstant(1);
  if (n == 1u) return sparsePolynomialFromCopy(p);
  if (n == 2u) return sparsePolynomialMul(p,p);

  /* Handle the case when the polynomial only has one monomial */
  if (p->monomialCount == 1u) {
    nC = constantFromUnsignedInt(n);
    res = __sparsePolynomialAllocate();
    res->refCount = 1;
    res->hash.hasHash = 0;
    res->monomialCount = 1;
    res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->coeffs[0] = constantPow(p->coeffs[0],nC);
    res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->monomialDegrees[0] = constantMul(p->monomialDegrees[0],nC);
    res->deg = constantFromCopy(res->monomialDegrees[0]);
    constantFree(nC);
    return res;
  }

  /* The polynomial has at least two monomials

     Write p(x) = r(x) + q(x), with r and q having approximately half
     of the monomials,

     and obtain

     (p(x))^n = sum_i=0^n bin(n,i) * (r(x))^i * (q(x))^(n - i)
     = r(x)^n + sum_i=0^(n-1) bin(n,i) * (r(x))^i * (q(x))^(n - i)
  */
  __sparsePolynomialCutIntoHalves(&r, &q, p);
  res = sparsePolynomialPowUnsignedInt(r, n);
  for (i=0u;i<n;i++) {
    bin = constantFromBinomialUnsignedInt(n, i);
    binPoly = sparsePolynomialFromConstant(bin);
    constantFree(bin);
    rToTheI = sparsePolynomialPowUnsignedInt(r, i);
    qToTheNMinusI = sparsePolynomialPowUnsignedInt(q, n - i);
    tempPoly1 = sparsePolynomialMul(binPoly, rToTheI);
    tempPoly2 = sparsePolynomialMul(tempPoly1, qToTheNMinusI);
    tempPoly3 = sparsePolynomialAdd(res, tempPoly2);
    sparsePolynomialFree(binPoly);
    sparsePolynomialFree(rToTheI);
    sparsePolynomialFree(qToTheNMinusI);
    sparsePolynomialFree(tempPoly1);
    sparsePolynomialFree(tempPoly2);
    sparsePolynomialFree(res);
    res = tempPoly3;
  }
  sparsePolynomialFree(r);
  sparsePolynomialFree(q);

  return res;
}

static inline sparse_polynomial_t sparsePolynomialPowUnsignedInt(sparse_polynomial_t p, unsigned int n) {
  sparse_polynomial_t res, t, tmp;
  constant_t nC;
  unsigned int nn;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle easy cases */
  if (n == 0u) return sparsePolynomialFromIntConstant(1);
  if (n == 1u) return sparsePolynomialFromCopy(p);
  if (n == 2u) return sparsePolynomialMul(p,p);

  /* Handle the case when the polynomial only has one monomial */
  if (p->monomialCount == 1u) {
    nC = constantFromUnsignedInt(n);
    res = __sparsePolynomialAllocate();
    res->refCount = 1;
    res->hash.hasHash = 0;
    res->monomialCount = 1;
    res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->coeffs[0] = constantPow(p->coeffs[0],nC);
    res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->monomialDegrees[0] = constantMul(p->monomialDegrees[0],nC);
    res->deg = constantFromCopy(res->monomialDegrees[0]);
    constantFree(nC);
    return res;
  }

  /* Use alternate algorithm for polynomials that might have
     non-rational coefficients
  */
  if (!sparsePolynomialCoefficientsAreRational(p, 0)) {
    return __sparsePolynomialPowUnsignedIntAlternate(p, n);
  }

  /* Square and multiply loop */
  t = sparsePolynomialFromCopy(p);
  res = sparsePolynomialFromIntConstant(1);
  nn = n;
  while (nn > 0u) {
    if (nn & 1u) {
      tmp = sparsePolynomialMul(res,t);
      sparsePolynomialFree(res);
      res = tmp;
    }
    nn >>= 1;
    if (nn > 0u) {
      tmp = sparsePolynomialMul(t,t);
      sparsePolynomialFree(t);
      t = tmp;
    }
  }
  sparsePolynomialFree(t);

  return res;
}

static inline int sparsePolynomialPowConstant(sparse_polynomial_t *r, sparse_polynomial_t p, constant_t n) {
  unsigned int nI;
  constant_t k, l, m;
  sparse_polynomial_t res, a, b, t;

  /* Make compiler happy */
  k = NULL;
  l = NULL;
  /* End of compiler happiness */

  if (p == NULL) return 0;
  if (n == NULL) return 0;
  if (!constantIsNonNegativeInteger(n,0)) return 0;

  /* Handle the cases n = 0 and n = 1 */
  if (constantIsZero(n,0)) {
    *r = sparsePolynomialFromIntConstant(1);
    return 1;
  }
  if (constantIsOne(n,0)) {
    *r = sparsePolynomialFromCopy(p);
    return 1;
  }

  /* Handle the case when p only has one monomial */
  if (p->monomialCount == 1u) {
    res = __sparsePolynomialAllocate();
    res->refCount = 1;
    res->hash.hasHash = 0;
    res->monomialCount = 1;
    res->coeffs = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->coeffs[0] = constantPow(p->coeffs[0],n);
    res->monomialDegrees = (constant_t *) safeCalloc(res->monomialCount, sizeof(constant_t));
    res->monomialDegrees[0] = constantMul(p->monomialDegrees[0],n);
    res->deg = constantFromCopy(res->monomialDegrees[0]);
    *r = res;
    return 1;
  }

  /* Try to use the powering function with an unsigned int argument */
  if (tryConstantToUnsignedInt(&nI, n)) {
    *r = sparsePolynomialPowUnsignedInt(p, nI);
    return 1;
  }

  /* If we are here, n does not hold on an unsigned int

     Chances are low that we actually have enough memory but we still
     try to handle the case.

     We rewrite p^n as

     p^n = p^(2^14 * k + l) = (p^k)^(2^14) * p^l

     In order to compute t = p^k, t^(2^14) and p^l, we call ourselves
     recursively. As we know that l is integer and l <= 2^14-1 <
     2^16-1 and 2^14 < 2^16 - 1, we know that for these exponents, we
     will be able to use the branch where the exponent holds on an
     unsigned int.

  */
  constantCutTwo14(&k, &l, n);
  m = constantFromInt(1 << 14);

  if (!sparsePolynomialPowConstant(&t,p,k)) {
    constantFree(k);
    constantFree(l);
    constantFree(m);
    return 0;
  }
  constantFree(k);

  if (!sparsePolynomialPowConstant(&a,t,m)) {
    sparsePolynomialFree(t);
    constantFree(l);
    constantFree(m);
    return 0;
  }
  sparsePolynomialFree(t);
  constantFree(m);

  if (!sparsePolynomialPowConstant(&b,p,l)) {
    sparsePolynomialFree(a);
    constantFree(l);
    return 0;
  }
  constantFree(l);

  *r = sparsePolynomialMul(a,b);

  sparsePolynomialFree(b);
  sparsePolynomialFree(a);
  return 1;
}

static inline int sparsePolynomialPow(sparse_polynomial_t *r, sparse_polynomial_t p, sparse_polynomial_t q) {
  constant_t n;
  int res;

  if (p == NULL) return 0;
  if (q == NULL) return 0;
  if (!sparsePolynomialConstantGetConstant(&n, q)) return 0;
  if (!constantIsNonNegativeInteger(n,0)) {
    constantFree(n);
    return 0;
  }
  res = sparsePolynomialPowConstant(r, p, n);
  constantFree(n);
  return res;
}

static inline sparse_polynomial_t sparsePolynomialDeriv(sparse_polynomial_t p) {
  unsigned int i;
  sparse_polynomial_t res;
  constant_t one;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    return sparsePolynomialFromIntConstant(0);
  }

  /* Handle the case when p is a constant polynomial */
  if (sparsePolynomialIsConstant(p, 0)) {
    return sparsePolynomialFromIntConstant(0);
  }

  /* Handle the general case */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->coeffs = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(p->monomialCount, sizeof(constant_t));
  one = constantFromInt(1);
  res->monomialCount = 0u;
  for (i=0u;i<p->monomialCount;i++) {
    if (constantIsGreaterOrEqual(p->monomialDegrees[i], one, 1)) {
      res->coeffs[res->monomialCount] = constantMul(p->coeffs[i],p->monomialDegrees[i]);
      res->monomialDegrees[res->monomialCount] = constantSub(p->monomialDegrees[i],one);
      (res->monomialCount)++;
    }
  }
  constantFree(one);

  /* If res->monomialCount still is zero, we never added anything. We
     add a zero coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != p->monomialCount) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  return res;
}

static inline sparse_polynomial_t __sparsePolynomialGcdBaseCase(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t u, v, t, z;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Euclidian algorithm */
  u = sparsePolynomialFromCopy(p);
  v = sparsePolynomialFromCopy(q);
  while (!sparsePolynomialIsConstantZero(v, 1)) {
    sparsePolynomialDiv(&z, &t, u, v);
    sparsePolynomialFree(z);
    sparsePolynomialFree(u);
    u = v;
    v = t;
  }
  if (!sparsePolynomialIsConstantZero(v, 0)) {
    printMessage(1, SOLLYA_MSG_POLYNOMIAL_GCD_CANNOT_BE_DECIDED, "Warning: In a gcd computation for polynomials, the tool could not decide if the gcd is 1 or a polynomial of a higher degree.\n");
    t = sparsePolynomialFromIntConstant(1);
    sparsePolynomialFree(u);
    sparsePolynomialFree(v);
    return t;
  }
  sparsePolynomialFree(v);

  /* Return a gcd */
  return u;
}

static inline int __sparsePolynomialCoefficientDenominatorLcm(mpz_t lcm, sparse_polynomial_t p) {
  unsigned int i;
  mpz_t t;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 1;

  /* Go over the coefficients and compute the denominator lcm */
  __sollyaRecycleMpzInit(t);
  mpz_set(t, lcm);
  for (i=0u;i<p->monomialCount;i++) {
    if (!tryConstantDenominatorLcm(t, p->coeffs[i])) {
      __sollyaRecycleMpzClear(t);
      return 0;
    }
  }
  mpz_set(lcm, t);
  __sollyaRecycleMpzClear(t);
  return 1;
}

static inline int __sparsePolynomialCoefficientIntegerBounds(mp_bitcnt_t *b, sparse_polynomial_t p) {
  unsigned int i;
  mp_bitcnt_t bound, bc;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) {
    *b = (mp_bitcnt_t) 1u;
    return 1;
  }

  /* Go over the coefficients and get their integer size */
  bound = (mp_bitcnt_t) 1u;
  for (i=0u;i<p->monomialCount;i++) {
    if (!tryConstantIntegerBound(&bc, p->coeffs[i])) return 0;
    if (bc > bound) bound = bc;
  }
  *b = bound;
  return 1;
}

static inline void __sparsePolynomialGcdHeuristicGenPolyModsDiv(mpz_t c, mpz_t g, mpz_t zeta, mpz_t scratch, mpz_t scratch2) {
  mpz_fdiv_qr(scratch, c, g, zeta);
  mpz_mul_ui(scratch2, c, 2u);
  if (mpz_cmpabs(scratch2, zeta) >= 0) {
    mpz_sub(c, c, zeta);
    mpz_add_ui(scratch, scratch, 1u);
  }
  mpz_set(g, scratch);
}

static inline sparse_polynomial_t __sparsePolynomialGcdHeuristicGenPoly(mpz_t gamma, mpz_t zeta) {
  sparse_polynomial_t p, t, q;
  int i;
  mpz_t g, c, scratch, scratch2;
  constant_t cc, ci;

  p = sparsePolynomialFromIntConstant(0);
  i = 0;
  __sollyaRecycleMpzInit(g);
  mpz_set(g, gamma);
  __sollyaRecycleMpzInit(c);
  __sollyaRecycleMpzInit(scratch);
  __sollyaRecycleMpzInit(scratch2);
  while (mpz_sgn(g) != 0) {
    __sparsePolynomialGcdHeuristicGenPolyModsDiv(c, g, zeta, scratch, scratch2);
    cc = constantFromMpz(c);
    ci = constantFromInt(i);
    t = __sparsePolynomialFromMonomial(cc, ci);
    constantFree(cc);
    constantFree(ci);
    q = sparsePolynomialAdd(p, t);
    sparsePolynomialFree(p);
    sparsePolynomialFree(t);
    p = q;
    i++;
  }
  __sollyaRecycleMpzClear(g);
  __sollyaRecycleMpzClear(c);
  __sollyaRecycleMpzClear(scratch);
  __sollyaRecycleMpzClear(scratch2);

  return p;
}

static inline int __sparsePolynomialGcdHeuristicDivides(sparse_polynomial_t a, sparse_polynomial_t b) {
  sparse_polynomial_t q, r;
  int res;

  sparsePolynomialDiv(&q, &r, b, a);
  res = sparsePolynomialIsConstantZero(r, 0);
  sparsePolynomialFree(q);
  sparsePolynomialFree(r);
  return res;
}

static inline int __sparsePolynomialGcdHeuristic(sparse_polynomial_t *u, sparse_polynomial_t p, sparse_polynomial_t q) {
  int n;
  mpz_t lcm;
  sparse_polynomial_t g, h, lcmp, G;
  mp_bitcnt_t b, b1, b2;
  int i;
  mpz_t zeta, alpha, beta, gamma;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Both polynomials must not have too many coefficients */
  if (p->monomialCount > SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_MONOMIAL_COUNT) return 0;
  if (q->monomialCount > SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_MONOMIAL_COUNT) return 0;

  /* The degrees of both polynomials must not be greater than some bound */
  n = sparsePolynomialGetDegreeAsInt(p);
  if (n < 0) return 0;
  if (n > SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_DEGREE) return 0;
  n = sparsePolynomialGetDegreeAsInt(q);
  if (n < 0) return 0;
  if (n > SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_DEGREE) return 0;

  /* Both polynomials must have rational coefficients */
  if (!sparsePolynomialCoefficientsAreRational(p, 0)) return 0;
  if (!sparsePolynomialCoefficientsAreRational(q, 0)) return 0;

  /* Compute the lcm of the denominators of all coefficients of both polynomials */
  __sollyaRecycleMpzInit(lcm);
  mpz_set_ui(lcm, 1u);
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, p)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }
  if (!__sparsePolynomialCoefficientDenominatorLcm(lcm, q)) {
    /* Could not compute lcm */
    __sollyaRecycleMpzClear(lcm);
    return 0;
  }

  /* Multiply both polynomials by the lcm, to make them polynomials
     with integer coefficients
  */
  lcmp = sparsePolynomialFromMpzConstant(lcm);
  __sollyaRecycleMpzClear(lcm);
  g = sparsePolynomialMul(p, lcmp);
  h = sparsePolynomialMul(q, lcmp);
  sparsePolynomialFree(lcmp);

  /* Now get the maximum number of bits needed to store the
     coefficients of both polynomials
  */
  if (!__sparsePolynomialCoefficientIntegerBounds(&b1, g)) {
    /* Could not compute the bound */
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return 0;
  }
  if (!__sparsePolynomialCoefficientIntegerBounds(&b2, h)) {
    /* Could not compute the bound */
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return 0;
  }
  b = b1;
  if (b2 > b1) b = b2;

  /* The bound on the integer coefficients must not be too large */
  if (b > SPARSE_POLYNOMIAL_GCD_HEURISTIC_MAX_INTEGER_BOUND) {
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return 0;
  }

  /* Now take zeta = 2^(b + 5) + 1. Clearly zeta > 2 * abs(c_i) for
     all coefficients c_i of both polynomials.

     See Theorem 1 of

     Bruce W. Char, Keith O. Geddes, Gaston H. Gonnet, GCDHEU:
     Heuristic polynomial GCD algorithm based on integer GCD
     computation, Journal of Symbolic Computation, Volume 7, Issue 1,
     1989, Pages 31-48.

     to understand why this is important.

  */
  __sollyaRecycleMpzInit(zeta);
  mpz_ui_pow_ui(zeta, 2u, (unsigned int) (b + 5u));
  mpz_add_ui(zeta, zeta, 1u);

  /* Initialize alpha, beta and gamma */
  __sollyaRecycleMpzInit(alpha);
  __sollyaRecycleMpzInit(beta);
  __sollyaRecycleMpzInit(gamma);

  /* Loop for the heuristic */
  for (i=0;i<SPARSE_POLYNOMIAL_GCD_HEURISTIC_TRIALS;i++) {
    /* Evaluate both polynomials at zeta, yielding alpha and beta

       alpha = g(zeta)
       beta  = h(zeta)

    */
    if (!sparsePolynomialEvalMpz(alpha, g, zeta)) {
      /* Evaluation did not work */
      __sollyaRecycleMpzClear(zeta);
      __sollyaRecycleMpzClear(alpha);
      __sollyaRecycleMpzClear(beta);
      __sollyaRecycleMpzClear(gamma);
      sparsePolynomialFree(g);
      sparsePolynomialFree(h);
      return 0;
    }
    if (!sparsePolynomialEvalMpz(beta, h, zeta)) {
      /* Evaluation did not work */
      __sollyaRecycleMpzClear(zeta);
      __sollyaRecycleMpzClear(alpha);
      __sollyaRecycleMpzClear(beta);
      __sollyaRecycleMpzClear(gamma);
      sparsePolynomialFree(g);
      sparsePolynomialFree(h);
      return 0;
    }

    /* Compute integer gcd: gamma = gcd(alpha, beta) */
    mpz_gcd(gamma, alpha, beta);

    /* If gamma is zero, continue directly with the next zeta */
    if (mpz_sgn(gamma) != 0) {
      /* Lift the integer gcd onto the polynomials */
      G = __sparsePolynomialGcdHeuristicGenPoly(gamma, zeta);

      /* Check if G divides g and h */
      if (__sparsePolynomialGcdHeuristicDivides(G, g) &&
	  __sparsePolynomialGcdHeuristicDivides(G, h)) {
	/* G divides both g and h and hence is the gcd of g and h

	   Set the result.
	*/
	*u = G;

	/* Clear the variables */
	__sollyaRecycleMpzClear(zeta);
	__sollyaRecycleMpzClear(alpha);
	__sollyaRecycleMpzClear(beta);
	__sollyaRecycleMpzClear(gamma);
	sparsePolynomialFree(g);
	sparsePolynomialFree(h);

	/* Return success */
	return 1;
      }
      /* The heuristic did not work, clear G */
      sparsePolynomialFree(G);
    }

    /* Prepare the next step */
    mpz_mul_ui(zeta, zeta, 4u);
    mpz_add_ui(zeta, zeta, 1u);
  }

  /* Clear variables */
  __sollyaRecycleMpzClear(zeta);
  __sollyaRecycleMpzClear(alpha);
  __sollyaRecycleMpzClear(beta);
  __sollyaRecycleMpzClear(gamma);
  sparsePolynomialFree(g);
  sparsePolynomialFree(h);

  /* The heuristic did not work */
  return 0;
}

static inline sparse_polynomial_t sparsePolynomialGcd(sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t u, v, t, z;
  constant_t a, b, c, d;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Handle the case when both polynomials are constants.

     In this case return the constant polynomial built from the gcd of
     these two constants. See the definition for gcd of two constants
     above.
  */
  if (sparsePolynomialIsConstant(p, 0) &&
      sparsePolynomialIsConstant(q, 0)) {
    a = sparsePolynomialGetIthCoefficientAsConstantIntIndex(p, 0);
    b = sparsePolynomialGetIthCoefficientAsConstantIntIndex(q, 0);
    c = constantGcd(a, b);
    u = sparsePolynomialFromConstant(c);
    constantFree(c);
    constantFree(b);
    constantFree(a);
    return u;
  }

  /* Handle the case when the input polynomials are equal. */
  if (sparsePolynomialEqual(p, q, 0)) {
    return sparsePolynomialFromCopy(p);
  }

  /* Try a heuristic algorithm. If it does not work, use the general
     Euclidian algorithm.
  */
  if (!__sparsePolynomialGcdHeuristic(&u, p, q)) {
    /* General case: Euclidian algorithm */
    u = __sparsePolynomialGcdBaseCase(p, q);
  }

  /* Make the polynomial u = gcd(a,b) unitary */
  if (!sparsePolynomialIsConstantZero(u, 1)) {
    __sparsePolynomialGetLeadingCoefficient(&c, &d, &z, u);
    sparsePolynomialFree(z);
    constantFree(d);
    v = sparsePolynomialFromConstant(c);
    constantFree(c);
    sparsePolynomialDiv(&z, &t, u, v);
    sparsePolynomialFree(t);
    sparsePolynomialFree(v);
    sparsePolynomialFree(u);
    u = z;
  }

  /* Normalize the polynomial u = gcd(p,q) in such a way that its
     leading coefficient is the gcd of the leading coefficients of the
     input polynomials p and q.
  */
  __sparsePolynomialGetLeadingCoefficient(&a, &d, &z, p);
  constantFree(d);
  sparsePolynomialFree(z);
  __sparsePolynomialGetLeadingCoefficient(&b, &d, &z, q);
  constantFree(d);
  sparsePolynomialFree(z);
  c = constantGcd(a, b);
  v = sparsePolynomialFromConstant(c);
  constantFree(a);
  constantFree(b);
  constantFree(c);
  z = sparsePolynomialMul(u, v);
  sparsePolynomialFree(v);
  sparsePolynomialFree(u);
  u = z;

  /* Return the normalized u = gcd(p,q) */
  return u;
}

static inline void __sparsePolynomialExtendedEuclidBaseCase(sparse_polynomial_t *gp, sparse_polynomial_t *rp,
							    sparse_polynomial_t *ap, sparse_polynomial_t *bp,
							    sparse_polynomial_t p, sparse_polynomial_t q) {
  sparse_polynomial_t g, r, a, b, s, t, qu, z, qs, qt, u, v;
  
  /* Handle stupid inputs */
  if ((p == NULL) ||
      (q == NULL)) {
    *gp = NULL;
    *rp = NULL;
    *ap = NULL;
    *bp = NULL;
    return;
  }

  /* General case */
  g = sparsePolynomialFromCopy(p);
  r = sparsePolynomialFromCopy(q);
  a = sparsePolynomialFromIntConstant(1);
  s = sparsePolynomialFromIntConstant(0);
  b = sparsePolynomialFromCopy(s);
  t = sparsePolynomialFromCopy(a);
  while (!sparsePolynomialIsConstantZero(r, 1)) {
    sparsePolynomialDiv(&qu, &z, g, r);
    sparsePolynomialFree(g);
    g = r;
    r = z;
    qs = sparsePolynomialMul(qu, s);
    u = sparsePolynomialSub(a, qs);
    sparsePolynomialFree(qs);
    sparsePolynomialFree(a);
    a = s;
    s = u;
    qt = sparsePolynomialMul(qu, t);
    v = sparsePolynomialSub(b, qt);
    sparsePolynomialFree(qt);
    sparsePolynomialFree(b);
    b = t;
    t = v;
    sparsePolynomialFree(qu);
  }
  
  /* Clean-up and return */
  sparsePolynomialFree(s);
  sparsePolynomialFree(t);
  *gp = g;
  *rp = r;
  *ap = a;
  *bp = b;  
}

static inline int __sparsePolynomialExtendedEuclidHeuristic(sparse_polynomial_t *gp, sparse_polynomial_t *rp,
							    sparse_polynomial_t *ap, sparse_polynomial_t *bp,
							    sparse_polynomial_t p, sparse_polynomial_t q) {
  
  sparse_polynomial_t g, v, gg, rr, aa, bb, z, t, pp, qq;
  constant_t c, d, a, b;

  /* Check if our heuristic for the gcd works */
  if (!__sparsePolynomialGcdHeuristic(&g, p, q)) return 0;

  /* The heuristic works: make the gcd polynomial have the gcd of the
     head coefficients as a head coefficient 
  */
  /* Make the polynomial g = gcd(a,b) unitary */
  if (!sparsePolynomialIsConstantZero(g, 1)) {
    __sparsePolynomialGetLeadingCoefficient(&c, &d, &z, g);
    sparsePolynomialFree(z);
    constantFree(d);
    v = sparsePolynomialFromConstant(c);
    constantFree(c);
    sparsePolynomialDiv(&z, &t, g, v);
    sparsePolynomialFree(t);
    sparsePolynomialFree(v);
    sparsePolynomialFree(g);
    g = z;
  }

  /* Normalize the polynomial g = gcd(p,q) in such a way that its
     leading coefficient is the gcd of the leading coefficients of the
     input polynomials p and q.
  */
  __sparsePolynomialGetLeadingCoefficient(&a, &d, &z, p);
  constantFree(d);
  sparsePolynomialFree(z);
  __sparsePolynomialGetLeadingCoefficient(&b, &d, &z, q);
  constantFree(d);
  sparsePolynomialFree(z);
  c = constantGcd(a, b);
  v = sparsePolynomialFromConstant(c);
  constantFree(a);
  constantFree(b);
  constantFree(c);
  z = sparsePolynomialMul(g, v);
  sparsePolynomialFree(v);
  sparsePolynomialFree(g);
  g = z;

  /* Now divide p and q by g, the gcd. The result will be two 
     polynomials with integer coefficients such that all 
     intermediate results in the extended Euclidian algorithm 
     will be polynomials with integer coefficients.
  */
  sparsePolynomialDiv(&pp, &t, p, g);
  if (!sparsePolynomialIsConstantZero(t, 0)) {
    /* This case should never happen */
    sparsePolynomialFree(g);
    sparsePolynomialFree(pp);
    sparsePolynomialFree(t);
    return 0;
  }
  sparsePolynomialFree(t);
  sparsePolynomialDiv(&qq, &t, q, g);
  if (!sparsePolynomialIsConstantZero(t, 0)) {
    /* This case should never happen */
    sparsePolynomialFree(g);
    sparsePolynomialFree(pp);
    sparsePolynomialFree(qq);
    sparsePolynomialFree(t);
    return 0;
  }
  sparsePolynomialFree(t);
  
  /* Now call the base extended Euclidian algorithm for pp and qq */
  __sparsePolynomialExtendedEuclidBaseCase(&gg, &rr, &aa, &bb, pp, qq);
  sparsePolynomialFree(pp);
  sparsePolynomialFree(qq);

  /* Double-check the result */
  if (!sparsePolynomialIsConstantZero(rr, 0)) {
    /* This case should never happen */
    sparsePolynomialFree(g);
    sparsePolynomialFree(aa);
    sparsePolynomialFree(bb);
    return 0;
  }
  
  /* Multiply the current gcd, which will be 1, by g */
  t = sparsePolynomialMul(g, gg);
  sparsePolynomialFree(g);
  sparsePolynomialFree(gg);
  gg = t;

  /* "Return" gg, rr, aa, bb and signal success */
  *gp = gg;
  *rp = rr;
  *ap = aa;
  *bp = bb;
  
  /* Signal success */
  return 1;
}

/* Computes g, r, a and b such that 

   (1) gcd(g, r) = gcd(p, q)

   and 

   (2) g = a * p + b * q

   while trying to ensure that 

   (3) r = 0.

   That latter property (3) cannot always be ensured. The first two
   properties (1) and (2) are always guaranteed to be true.

   In addition, if p and q are not constant polynomials, 
   the functions tries to ensure that g is unitary. This 
   property cannot always be ensured.

   If r ends up being 0, then g is the gcd of p and q and a and b are
   the co-factors (Bezout coefficients).

*/
static inline void sparsePolynomialExtendedEuclid(sparse_polynomial_t *gp, sparse_polynomial_t *rp,
						  sparse_polynomial_t *ap, sparse_polynomial_t *bp,
						  sparse_polynomial_t p, sparse_polynomial_t q) {
  constant_t pc, qc, gc, rc, ac, bc, c, d;
  sparse_polynomial_t g, r, a, b, z, t, v;

  /* Handle stupid inputs */
  if ((p == NULL) ||
      (q == NULL)) {
    *gp = NULL;
    *rp = NULL;
    *ap = NULL;
    *bp = NULL;
    return;
  }

  /* Handle the case when both polynomials are constants.

     In this case return the constant polynomial built from the gcd of
     these two constants. See the definition for extended Euclid of
     two constants above.
  */
  if (sparsePolynomialIsConstant(p, 0) &&
      sparsePolynomialIsConstant(q, 0)) {
    pc = sparsePolynomialGetIthCoefficientAsConstantIntIndex(p, 0);
    qc = sparsePolynomialGetIthCoefficientAsConstantIntIndex(q, 0);
    constantExtendedEuclid(&gc, &rc, &ac, &bc, pc, qc);
    g = sparsePolynomialFromConstant(gc);
    r = sparsePolynomialFromConstant(rc);
    a = sparsePolynomialFromConstant(ac);
    b = sparsePolynomialFromConstant(bc);
    constantFree(pc);
    constantFree(qc);
    constantFree(gc);
    constantFree(rc);
    constantFree(ac);
    constantFree(bc);
    *gp = g;
    *rp = r;
    *ap = a;
    *bp = b;
    return;
  }

  /* Handle the case when the input polynomials are equal. */
  if (sparsePolynomialEqual(p, q, 0)) {
    g = sparsePolynomialFromCopy(p);
    r = sparsePolynomialFromIntConstant(0);
    a = sparsePolynomialFromIntConstant(1);
    b = sparsePolynomialFromIntConstant(0);
    *gp = g;
    *rp = r;
    *ap = a;
    *bp = b;
    return;
  }

  /* Try a heuristic algorithm. If it does not work, use the general
     extended Euclidian algorithm.
  */
  if (!__sparsePolynomialExtendedEuclidHeuristic(&g, &r, &a, &b, p, q)) {
    /* General case: extended Euclidian algorithm */
    __sparsePolynomialExtendedEuclidBaseCase(&g, &r, &a, &b, p, q);
  }

  /* Make the polynomial g = gcd(p, q) unitary */
  if (!sparsePolynomialIsConstantZero(g, 1)) {
    __sparsePolynomialGetLeadingCoefficient(&c, &d, &z, g);
    sparsePolynomialFree(z);
    constantFree(d);
    v = sparsePolynomialFromConstant(c);
    constantFree(c);
    sparsePolynomialDiv(&z, &t, g, v);
    sparsePolynomialFree(t);
    sparsePolynomialFree(g);
    g = z;
    sparsePolynomialDiv(&z, &t, a, v);
    sparsePolynomialFree(t);
    sparsePolynomialFree(a);
    a = z;
    sparsePolynomialDiv(&z, &t, b, v);
    sparsePolynomialFree(t);
    sparsePolynomialFree(b);
    b = z;    
    sparsePolynomialFree(v);
  }

  /* Normalize the polynomial g = gcd(p, q) in such a way that its
     leading coefficient is the gcd of the leading coefficients of the
     input polynomials p and q.
  */
  __sparsePolynomialGetLeadingCoefficient(&ac, &d, &z, p);
  constantFree(d);
  sparsePolynomialFree(z);
  __sparsePolynomialGetLeadingCoefficient(&bc, &d, &z, q);
  constantFree(d);
  sparsePolynomialFree(z);
  c = constantGcd(ac, bc);
  v = sparsePolynomialFromConstant(c);
  constantFree(ac);
  constantFree(bc);
  constantFree(c);
  z = sparsePolynomialMul(g, v);
  sparsePolynomialFree(g);
  g = z;
  z = sparsePolynomialMul(a, v);
  sparsePolynomialFree(a);
  a = z;
  z = sparsePolynomialMul(b, v);
  sparsePolynomialFree(b);
  b = z;
  sparsePolynomialFree(v);
  
  /* Return the normalized g, r, a, b */
  *gp = g;
  *rp = r;
  *ap = a;
  *bp = b;
}

int sparsePolynomialFromExpression(sparse_polynomial_t *r, node *p) {
  sparse_polynomial_t a, b, quot, rest;
  int res;

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* Try to decompose expressions built on c, x, +, -, *, /, -, ^ */
  switch (p->nodeType) {
  case MEMREF:
    if (p->cache->polynomialRepresentation != NULL) {
      *r = __polynomialGetSparsePolynomial(p->cache->polynomialRepresentation);
      return 1;
    }
    return sparsePolynomialFromExpression(r, getMemRefChild(p));
    break;
  case VARIABLE:
    *r = sparsePolynomialFromIdentity();
    return 1;
    break;
  case CONSTANT:
    *r = sparsePolynomialFromMpfrConstant(*(p->value));
    return 1;
    break;
  case NEG:
    if (!sparsePolynomialFromExpression(&a, p->child1))
      break;
    *r = sparsePolynomialNeg(a);
    sparsePolynomialFree(a);
    return 1;
    break;
  case ADD:
  case SUB:
  case MUL:
  case DIV:
  case POW:
    if (!sparsePolynomialFromExpression(&a, p->child1))
      break;
    if (!sparsePolynomialFromExpression(&b, p->child2)) {
      sparsePolynomialFree(a);
      break;
    }
    res = 0;
    switch (p->nodeType) {
    case ADD:
      *r = sparsePolynomialAdd(a, b);
      res = 1;
      break;
    case SUB:
      *r = sparsePolynomialSub(a, b);
      res = 1;
      break;
    case MUL:
      *r = sparsePolynomialMul(a, b);
      res = 1;
      break;
    case DIV:
      if (sparsePolynomialIsConstantZero(b, 1)) {
	res = 0;
      } else {
	sparsePolynomialDiv(&quot, &rest, a, b);
	if (sparsePolynomialIsConstantZero(rest, 0)) {
	  *r = quot;
	  res = 1;
	} else {
	  sparsePolynomialFree(quot);
	  res = 0;
	}
	sparsePolynomialFree(rest);
      }
      break;
    case POW:
      res = sparsePolynomialPow(r, a, b);
      break;
    }
    sparsePolynomialFree(a);
    sparsePolynomialFree(b);
    if (res) return 1;
    break;
  }

  /* Here, the expression contains some other basic function. It can
     be a polynomial only if it is a constant expression.
  */
  if (isConstant(p)) {
    if (!__sparsePolynomialFromConstantExpression(r, p)) return 0;
    return 1;
  }

  return 0;
}

static inline void sparsePolynomialGetDegree(mpz_t deg, sparse_polynomial_t p) {
  if (p == NULL) {
    mpz_set_si(deg, -1);
    return;
  }
  if (!tryConstantToMpz(deg, p->deg)) {
    mpz_set_si(deg, -1);
  }
}

static inline int sparsePolynomialGetDegreeAsInt(sparse_polynomial_t p) {
  int deg;

  /* Make compiler happy */
  deg = -1;
  /* End of compiler happiness */

  if (p == NULL) return -1;
  if (!tryConstantToInt(&deg, p->deg)) return -1;
  return deg;
}

static inline constant_t sparsePolynomialGetIthCoefficientAsConstant(sparse_polynomial_t p, mpz_t i) {
  constant_t ic, coeffsum, t;
  unsigned int j, k;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* The index must be non-negative */
  if (mpz_sgn(i) < 0) {
    return constantFromInt(0);
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    return constantFromInt(0);
  }

  /* Construct a constant from i */
  ic = constantFromMpz(i);

  /* The index must be no greater than the degree of p */
  if (constantIsGreater(ic, p->deg, 0)) {
    constantFree(ic);
    return constantFromInt(0);
  }

  /* Get the index j where the coefficients for degree i might
     start
  */
  j = __sparsePolynomialFindDegree(ic, p->monomialDegrees, p->monomialCount, 0u);

  /* If j is greater than the last index of the monomials of p, there
     exists no monomial of degree i.
  */
  if (j >= p->monomialCount) {
    constantFree(ic);
    return constantFromInt(0);
  }

  /* Here, we know that 0 <= j <= n - 1.

     We add up all coefficients associated with a degree equal to i.

  */
  coeffsum = constantFromInt(0);
  for (k=j;k<p->monomialCount;k++) {
    if (!constantIsEqual(ic, p->monomialDegrees[k], 0))
      break;
    t = constantAdd(coeffsum, p->coeffs[k]);
    constantFree(coeffsum);
    coeffsum = t;
  }
  constantFree(ic);
  return coeffsum;
}

node *sparsePolynomialGetIthCoefficient(sparse_polynomial_t p, mpz_t i) {
  constant_t c;
  node *res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Get coefficient as a constant */
  c = sparsePolynomialGetIthCoefficientAsConstant(p, i);

  /* Convert to an expression */
  res = addMemRef(constantToExpression(c));
  constantFree(c);

  /* Return the result */
  return res;
}

node *sparsePolynomialGetIthCoefficientIntIndex(sparse_polynomial_t p, int i) {
  constant_t ic, coeffsum, t;
  unsigned int j, k;
  node *res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* The index must be non-negative */
  if (i < 0) {
    return addMemRef(makeConstantInt(0));
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    return addMemRef(makeConstantInt(0));
  }

  /* Construct a constant from i */
  ic = constantFromInt(i);

  /* The index must be no greater than the degree of p */
  if (constantIsGreater(ic, p->deg, 0)) {
    constantFree(ic);
    return addMemRef(makeConstantInt(0));
  }

  /* Get the index j where the coefficients for degree i might
     start
  */
  j = __sparsePolynomialFindDegree(ic, p->monomialDegrees, p->monomialCount, 0u);

  /* If j is greater than the last index of the monomials of p, there
     exists no monomial of degree i.
  */
  if (j >= p->monomialCount) {
    constantFree(ic);
    return addMemRef(makeConstantInt(0));
  }

  /* Here, we know that 0 <= j <= n - 1.

     We add up all coefficients associated with a degree equal to i.

  */
  coeffsum = constantFromInt(0);
  for (k=j;k<p->monomialCount;k++) {
    if (!constantIsEqual(ic, p->monomialDegrees[k], 0))
      break;
    t = constantAdd(coeffsum, p->coeffs[k]);
    constantFree(coeffsum);
    coeffsum = t;
  }
  constantFree(ic);
  res = constantToExpression(coeffsum);
  constantFree(coeffsum);

  return res;
}

static inline constant_t sparsePolynomialGetIthCoefficientAsConstantIntIndex(sparse_polynomial_t p, int i) {
  constant_t ic, coeffsum, t;
  unsigned int j, k;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* The index must be non-negative */
  if (i < 0) {
    return constantFromInt(0);
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    return constantFromInt(0);
  }

  /* Construct a constant from i */
  ic = constantFromInt(i);

  /* The index must be no greater than the degree of p */
  if (constantIsGreater(ic, p->deg, 0)) {
    constantFree(ic);
    return constantFromInt(0);
  }

  /* Get the index j where the coefficients for degree i might
     start
  */
  j = __sparsePolynomialFindDegree(ic, p->monomialDegrees, p->monomialCount, 0u);

  /* If j is greater than the last index of the monomials of p, there
     exists no monomial of degree i.
  */
  if (j >= p->monomialCount) {
    constantFree(ic);
    return constantFromInt(0);
  }

  /* Here, we know that 0 <= j <= n - 1.

     We add up all coefficients associated with a degree equal to i.

  */
  coeffsum = constantFromInt(0);
  for (k=j;k<p->monomialCount;k++) {
    if (!constantIsEqual(ic, p->monomialDegrees[k], 0))
      break;
    t = constantAdd(coeffsum, p->coeffs[k]);
    constantFree(coeffsum);
    coeffsum = t;
  }
  constantFree(ic);
  return coeffsum;
}

static inline int sparsePolynomialGetCoefficients(node ***coeffs, unsigned int *deg, sparse_polynomial_t p) {
  unsigned int degree, size, i, j, d, k;
  node **coefficients;
  constant_t c, t;

  /* Handle stupid inputs */
  if (p == NULL) {
    return 0;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    *deg = 0u;
    *coeffs = (node **) safeCalloc(1, sizeof(node *));
    (*coeffs)[0] = addMemRef(makeConstantInt(0));
    return 1;
  }

  /* Check if the degree of the polynomial holds on an unsigned
     int.
  */
  if (!tryConstantToUnsignedInt(&degree, p->deg)) {
    return 0;
  }

  /* Check if the size of the output (degree + 1) holds on an unsigned
     int.
  */
  size = degree + 1u;
  if (size == 0u) return 0;

  /* Allocate the output array and initialize all its elements to
     NULL.
  */
  coefficients = (node **) safeCalloc(size, sizeof(node *));
  for (i=0u;i<=degree;i++) {
    coefficients[i] = NULL;
  }

  /* Go over all monomials, add up equal degrees and set the output
     coefficients.

     If ever there is a degree we can't place, desallocate everything
     and signal failure.
  */
  for (i=0u;i<p->monomialCount;i++) {
    if (!tryConstantToUnsignedInt(&d, p->monomialDegrees[i])) {
      for (j=0u;j<=degree;j++) {
	if (coefficients[j] != NULL) freeThing(coefficients[j]);
      }
      safeFree(coefficients);
      return 0;
    }
    if (d > degree) {
      for (j=0u;j<=degree;j++) {
	if (coefficients[j] != NULL) freeThing(coefficients[j]);
      }
      safeFree(coefficients);
      return 0;
    }
    for (j=i;j<p->monomialCount-1u;j++) {
      if (!constantIsEqual(p->monomialDegrees[i], p->monomialDegrees[j+1u], 0))
	break;
    }
    if (i == j) {
      c = constantFromCopy(p->coeffs[i]);
    } else {
      c = constantFromInt(0);
      for (k=i;k<=j;k++) {
	t = constantAdd(c, p->coeffs[k]);
	constantFree(c);
	c = t;
      }
      i = j;
    }
    if (coefficients[d] == NULL) {
      coefficients[d] = addMemRef(constantToExpression(c));
    } else {
      coefficients[d] = addMemRef(makeAdd(coefficients[d], addMemRef(constantToExpression(c))));
    }
    constantFree(c);
  }

  /* Fill in the holes in the output coefficient array and set the
     output
  */
  for (i=0u;i<=degree;i++) {
    if (coefficients[i] == NULL) {
      coefficients[i] = addMemRef(makeConstantInt(0));
    }
  }
  *coeffs = coefficients;
  *deg = degree;
  return 1;
}

static inline unsigned int sparsePolynomialGetNumberOfNonZeroCoefficients(sparse_polynomial_t p) {

  /* Handle stupid inputs */
  if (p == NULL) {
    return 0u;
  }

  /* General case */
  return p->monomialCount;
}

static inline void __sparsePolynomialEvalMpfr(mpfr_t y, sparse_polynomial_t p, mpfr_t x, mpfr_t scratch) {
  unsigned int i, a, b, d;
  mp_prec_t prec;
  constant_t dc;

  /* Handle stupid inputs */
  if (p == NULL) {
    mpfr_set_nan(y);
    return;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    mpfr_set_si(y, 0, GMP_RNDN);
    return;
  }

  /* Save precision of y and increase it for a moment */
  prec = mpfr_get_prec(y);
  mpfr_set_prec(y, prec + 25);
  mpfr_set_prec(y, prec + 10);

  /* Set precision of scratch variable */
  mpfr_set_prec(scratch, prec + 25);
  mpfr_set_prec(scratch, prec + 10);

  /* Perform Horner evaluation of p in x */
  constantEvalMpfr(y, p->coeffs[p->monomialCount-1u]);
  for (i=p->monomialCount-1u;i>=1u;i--) {
    /* y <- x^(p->monomialDegrees[i] - p->monomialDegrees[i-1u]) * y */
    if (tryConstantToUnsignedInt(&a, p->monomialDegrees[i]) &&
	tryConstantToUnsignedInt(&b, p->monomialDegrees[i-1u])) {
      if (a < b) {
	sollyaFprintf(stderr,"Error: __sparsePolynomialEvalMpfr: monomial degrees not appropriately ordered\n");
	exit(1);
      }
      d = a - b;
      if (d != 0u) {
	if (d == 1u) {
	  mpfr_mul(y, x, y, GMP_RNDN);
	} else {
	  mpfr_pow_ui(scratch, x, d, GMP_RNDN);
	  mpfr_mul(y, scratch, y, GMP_RNDN);
	}
      }
    } else {
      dc = constantSub(p->monomialDegrees[i], p->monomialDegrees[i-1u]);
      constantEvalMpfr(scratch, dc);
      constantFree(dc);
      mpfr_pow(scratch, x, scratch, GMP_RNDN);
      mpfr_mul(y, scratch, y, GMP_RNDN);
    }
    /* y <- p->coeffs[i-1u] + y */
    constantEvalMpfr(scratch, p->coeffs[i-1u]);
    mpfr_add(y, y, scratch, GMP_RNDN);
  }
  /* y <- x^(p->monomialDegrees[0u]) * y */
  if (tryConstantToUnsignedInt(&a, p->monomialDegrees[0u])) {
    if (a != 0u) {
      if (a == 1u) {
	mpfr_mul(y, x, y, GMP_RNDN);
      } else {
	mpfr_pow_ui(scratch, x, a, GMP_RNDN);
	mpfr_mul(y, scratch, y, GMP_RNDN);
      }
    }
  } else {
    constantEvalMpfr(scratch, p->monomialDegrees[0u]);
    mpfr_pow(scratch, x, scratch, GMP_RNDN);
    mpfr_mul(y, scratch, y, GMP_RNDN);
  }

  /* Round result to original precision of y */
  mpfr_prec_round(y, prec, GMP_RNDN);
}

static inline void sparsePolynomialEvalMpfr(mpfr_t y, sparse_polynomial_t p, mpfr_t x) {
  mpfr_t scratch, Y;

  /* Handle stupid inputs */
  if (p == NULL) {
    mpfr_set_nan(y);
    return;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    mpfr_set_si(y, 0, GMP_RNDN);
    return;
  }

  /* Cover the case when x and y are the same MPFR variable */
  if (x == y) {
    __sollyaRecycleMpfrInit2(Y, mpfr_get_prec(y));
    sparsePolynomialEvalMpfr(Y, p, x);
    mpfr_set(y, Y, GMP_RNDN); /* exact */
    __sollyaRecycleMpfrClear(Y);
    return;
  }

  /* Try to use a global variable as scratch space */
  if  (__sparsePolynomialEvalMpfr_var_used) {
    /* The global scratch space is taken; use new scratch space on the
       stack
    */
    __sollyaRecycleMpfrInit2(scratch, mpfr_get_prec(y) + 25);

    /* Use inner evaluation function */
    __sparsePolynomialEvalMpfr(y, p, x, scratch);

    /* Clear the scratch variable */
    __sollyaRecycleMpfrClear(scratch);

    return;
  }

  /* Here the global scratch space variable still is free; so start
     using it.
  */
  __sparsePolynomialEvalMpfr_var_used = 1;

  /* Initialize the global scratch variable */
  if (__sparsePolynomialEvalMpfr_scratch_initialized) {
    mpfr_set_prec(__sparsePolynomialEvalMpfr_scratch, mpfr_get_prec(y) + 25);
  } else {
    __sollyaRecycleMpfrInit2(__sparsePolynomialEvalMpfr_scratch, mpfr_get_prec(y) + 25);
    __sparsePolynomialEvalMpfr_scratch_initialized = 1;
  }

  /* Use inner evaluation function */
  __sparsePolynomialEvalMpfr(y, p, x, __sparsePolynomialEvalMpfr_scratch);

  /* Free the access to the global scratch space variable again */
  __sparsePolynomialEvalMpfr_var_used = 0;
}

static inline void __sparsePolynomialEvalMpfi(sollya_mpfi_t y, sparse_polynomial_t p, sollya_mpfi_t x, sollya_mpfi_t scratch) {
  unsigned int i, a, b, d;
  mp_prec_t prec;
  constant_t dc;
  constant_t c;

  /* Handle stupid inputs */
  if (p == NULL) {
    sollya_mpfi_set_nan(y);
    return;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    sollya_mpfi_set_si(y, 0);
    return;
  }

  /* Handle the special case when x is a point and zero interval.

     The special case happens sufficiently often that it makes sense
     to handle it apart, for performance reasons.

  */
  if (sollya_mpfi_is_zero(x)) {
    c = sparsePolynomialGetIthCoefficientAsConstantIntIndex(p, 0);
    constantEvalMpfi(y, c);
    constantFree(c);
    return;
  }

  /* Save precision of y and increase it for a moment */
  prec = sollya_mpfi_get_prec(y);
  sollya_mpfi_set_prec(y, prec + 25);
  sollya_mpfi_set_prec(y, prec + 10);

  /* Set precision of scratch variable */
  sollya_mpfi_set_prec(scratch, prec + 25);
  sollya_mpfi_set_prec(scratch, prec + 10);

  /* Perform Horner evaluation of p in x */
  constantEvalMpfi(y, p->coeffs[p->monomialCount-1u]);
  for (i=p->monomialCount-1u;i>=1u;i--) {
    /* y <- x^(p->monomialDegrees[i] - p->monomialDegrees[i-1u]) * y */
    if (tryConstantToUnsignedInt(&a, p->monomialDegrees[i]) &&
	tryConstantToUnsignedInt(&b, p->monomialDegrees[i-1u])) {
      if (a < b) {
	sollyaFprintf(stderr,"Error: __sparsePolynomialEvalMpfi: monomial degrees not appropriately ordered\n");
	exit(1);
      }
      d = a - b;
      if (d != 0u) {
	if (d == 1u) {
	  sollya_mpfi_mul(y, x, y);
	} else {
	  sollya_mpfi_pow_ulong(scratch, x, d);
	  sollya_mpfi_mul(y, scratch, y);
	}
      }
    } else {
      dc = constantSub(p->monomialDegrees[i], p->monomialDegrees[i-1u]);
      constantEvalMpfi(scratch, dc);
      constantFree(dc);
      sollya_mpfi_pow(scratch, x, scratch);
      sollya_mpfi_mul(y, scratch, y);
    }
    /* y <- p->coeffs[i-1u] + y */
    constantEvalMpfi(scratch, p->coeffs[i-1u]);
    sollya_mpfi_add(y, y, scratch);
  }
  /* y <- x^(p->monomialDegrees[0u]) * y */
  if (tryConstantToUnsignedInt(&a, p->monomialDegrees[0u])) {
    if (a != 0u) {
      if (a == 1u) {
	sollya_mpfi_mul(y, x, y);
      } else {
	sollya_mpfi_pow_ulong(scratch, x, a);
	sollya_mpfi_mul(y, scratch, y);
      }
    }
  } else {
    constantEvalMpfi(scratch, p->monomialDegrees[0u]);
    sollya_mpfi_pow(scratch, x, scratch);
    sollya_mpfi_mul(y, scratch, y);
  }

  /* Round result to original precision of y */
  sollya_mpfi_prec_round(y, prec);
}

static inline void sparsePolynomialEvalMpfi(sollya_mpfi_t y, sparse_polynomial_t p, sollya_mpfi_t x) {
  sollya_mpfi_t scratch, Y;

  /* Handle stupid inputs */
  if (p == NULL) {
    sollya_mpfi_set_nan(y);
    return;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    sollya_mpfi_set_si(y, 0);
    return;
  }

  /* Cover the case when x and y are the same MPFI variable */
  if (x == y) {
    __sollyaRecycleMpfiInit2(Y, sollya_mpfi_get_prec(y));
    sparsePolynomialEvalMpfi(Y, p, x);
    sollya_mpfi_set(y, Y);
    __sollyaRecycleMpfiClear(Y);
    return;
  }

  /* Try to use a global variable as scratch space */
  if  (__sparsePolynomialEvalMpfi_var_used) {
    /* The global scratch space is taken; use new scratch space on the
       stack
    */
    __sollyaRecycleMpfiInit2(scratch, sollya_mpfi_get_prec(y) + 25);

    /* Use inner evaluation function */
    __sparsePolynomialEvalMpfi(y, p, x, scratch);

    /* Clear the scratch variable */
    __sollyaRecycleMpfiClear(scratch);

    return;
  }

  /* Here the global scratch space variable still is free; so start
     using it.
  */
  __sparsePolynomialEvalMpfi_var_used = 1;

  /* Initialize the global scratch variable */
  if (__sparsePolynomialEvalMpfi_scratch_initialized) {
    sollya_mpfi_set_prec(__sparsePolynomialEvalMpfi_scratch, sollya_mpfi_get_prec(y) + 25);
  } else {
    __sollyaRecycleMpfiInit2(__sparsePolynomialEvalMpfi_scratch, sollya_mpfi_get_prec(y) + 25);
    __sparsePolynomialEvalMpfi_scratch_initialized = 1;
  }

  /* Use inner evaluation function */
  __sparsePolynomialEvalMpfi(y, p, x, __sparsePolynomialEvalMpfi_scratch);

  /* Free the access to the global scratch space variable again */
  __sparsePolynomialEvalMpfi_var_used = 0;
}

static inline int __sparsePolynomialEvalMpz(mpz_t y, sparse_polynomial_t p, mpz_t x, mpz_t scratch) {
  unsigned int i, a, b, d;
  constant_t dc;

  /* Handle stupid inputs */
  if (p == NULL) {
    return 0;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    mpz_set_si(y, 0);
    return 1;
  }

  /* Perform Horner evaluation of p in x */
  if (!tryConstantToMpz(y, p->coeffs[p->monomialCount-1u])) return 0;
  for (i=p->monomialCount-1u;i>=1u;i--) {
    /* y <- x^(p->monomialDegrees[i] - p->monomialDegrees[i-1u]) * y */
    if (tryConstantToUnsignedInt(&a, p->monomialDegrees[i]) &&
	tryConstantToUnsignedInt(&b, p->monomialDegrees[i-1u])) {
      if (a < b) {
	sollyaFprintf(stderr,"Error: __sparsePolynomialEvalMpz: monomial degrees not appropriately ordered\n");
	exit(1);
      }
      d = a - b;
      if (d != 0u) {
	if (d == 1u) {
	  mpz_mul(y, x, y);
	} else {
	  mpz_pow_ui(scratch, x, d);
	  mpz_mul(y, scratch, y);
	}
      }
    } else {
      dc = constantSub(p->monomialDegrees[i], p->monomialDegrees[i-1u]);
      if (!tryConstantToMpz(scratch, dc)) {
	constantFree(dc);
	return 0;
      }
      constantFree(dc);
      if (!sollya_mpz_pow(scratch, x, scratch)) return 0;
      mpz_mul(y, scratch, y);
    }
    /* y <- p->coeffs[i-1u] + y */
    if (!tryConstantToMpz(scratch, p->coeffs[i-1u])) return 0;
    mpz_add(y, y, scratch);
  }
  /* y <- x^(p->monomialDegrees[0u]) * y */
  if (tryConstantToUnsignedInt(&a, p->monomialDegrees[0u])) {
    if (a != 0u) {
      if (a == 1u) {
	mpz_mul(y, x, y);
      } else {
	mpz_pow_ui(scratch, x, a);
	mpz_mul(y, scratch, y);
      }
    }
  } else {
    if (!tryConstantToMpz(scratch, p->monomialDegrees[0u])) return 0;
    if (!sollya_mpz_pow(scratch, x, scratch)) return 0;
    mpz_mul(y, scratch, y);
  }

  /* Indicate success */
  return 1;
}

static inline int sparsePolynomialEvalMpz(mpz_t y, sparse_polynomial_t p, mpz_t x) {
  mpz_t scratch, Y;
  int res;

  /* Handle stupid inputs */
  if (p == NULL) {
    return 0;
  }

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) {
    mpz_set_si(y, 0);
    return 1;
  }

  /* Cover the case when x and y are the same MPFI variable */
  if (x == y) {
    __sollyaRecycleMpzInit(Y);
    res = sparsePolynomialEvalMpz(Y, p, x);
    mpz_set(y, Y);
    __sollyaRecycleMpzClear(Y);
    return res;
  }

  /* Initialize a scratch variable */
  __sollyaRecycleMpzInit(scratch);

  /* Use inner evaluation function */
  res = __sparsePolynomialEvalMpz(y, p, x, scratch);

  /* Clear the scratch variable */
  __sollyaRecycleMpzClear(scratch);

  /* Return success flag */
  return res;
}

static inline node *__sparsePolynomialGetExpressionCanonical(sparse_polynomial_t p) {
  node *res, *temp;
  unsigned int i;
  int negate;
  constant_t t;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle the case when p has no monomials */
  if (p->monomialCount == 0u) {
    return addMemRef(makeConstantInt(0));
  }

  /* Handle the case when p only has one monomial */
  if (p->monomialCount == 1u) {
    if (constantIsZero(p->monomialDegrees[0],0)) {
      return addMemRef(constantToExpression(p->coeffs[0]));
    }
    if (constantIsOne(p->monomialDegrees[0],0)) {
      if (constantIsOne(p->coeffs[0],0)) {
	return addMemRef(makeVariable());
      }
      if (constantIsMinusOne(p->coeffs[0],0)) {
	return addMemRef(makeNeg(makeVariable()));
      }      
      return addMemRef(makeMul(constantToExpression(p->coeffs[0]),
			       makeVariable()));
    }
    if (constantIsOne(p->coeffs[0],0)) {
      return addMemRef(makePow(makeVariable(),
			       constantToExpression(p->monomialDegrees[0])));
    }
    if (constantIsMinusOne(p->coeffs[0],0)) {
      return addMemRef(makeNeg(makePow(makeVariable(),
				       constantToExpression(p->monomialDegrees[0]))));
    }    
    return addMemRef(makeMul(constantToExpression(p->coeffs[0]),
			     addMemRef(makePow(makeVariable(),
					       constantToExpression(p->monomialDegrees[0])))));
  }

  /* Here, p has at least two monomials */
  if (constantIsZero(p->monomialDegrees[0],0)) {
    res = constantToExpression(p->coeffs[0]);
  } else {
    if (constantIsOne(p->monomialDegrees[0],0)) {
      if (constantIsOne(p->coeffs[0], 0)) {
	res = makeVariable();
      } else {
	if (constantIsMinusOne(p->coeffs[0], 0)) {
	  res = addMemRef(makeNeg(makeVariable()));
	} else {
	  res = addMemRef(makeMul(constantToExpression(p->coeffs[0]),
				  makeVariable()));
	}
      }
    } else {
      if (constantIsOne(p->coeffs[0], 0)) {
	res = addMemRef(makePow(makeVariable(),
				constantToExpression(p->monomialDegrees[0])));
      } else {
	if (constantIsMinusOne(p->coeffs[0], 0)) {
	  res = addMemRef(makeNeg(makePow(makeVariable(),
					  constantToExpression(p->monomialDegrees[0]))));
	} else {
	  res = addMemRef(makeMul(constantToExpression(p->coeffs[0]),
				  addMemRef(makePow(makeVariable(),
						    constantToExpression(p->monomialDegrees[0])))));
	}
      }
    }
  }
  for (i=1u;i<p->monomialCount;i++) {
    negate = 0;
    if (constantIsZero(p->monomialDegrees[i],0)) {
      temp = constantToExpression(p->coeffs[i]);
    } else {
      if (constantIsOne(p->monomialDegrees[i],0)) {
	if (constantIsOne(p->coeffs[i], 0)) {
	  temp = makeVariable();
	} else {
	  if (constantIsMinusOne(p->coeffs[i], 0)) {
	    negate = 1;
	    temp = makeVariable();
	  } else {
	    if ((!constantIsRational(p->coeffs[i], 0)) ||
		constantIsPositive(p->coeffs[i], 1) ||
		constantIsZero(p->coeffs[i], 1)) {
	      temp = addMemRef(makeMul(constantToExpression(p->coeffs[i]),
				       makeVariable()));
	    } else {
	      negate = 1;
	      t = constantNeg(p->coeffs[i]);
	      temp = addMemRef(makeMul(constantToExpression(t),
				       makeVariable()));
	      constantFree(t);
	    }
	  }
	}
      } else {
	if (constantIsOne(p->coeffs[i], 0)) {
	  temp = addMemRef(makePow(makeVariable(),
				   constantToExpression(p->monomialDegrees[i])));
	} else {
	  if (constantIsMinusOne(p->coeffs[i], 0)) {
	    negate = 1;
	    temp = addMemRef(makePow(makeVariable(),
				     constantToExpression(p->monomialDegrees[i])));
	  } else {
	    if ((!constantIsRational(p->coeffs[i], 0)) ||
		constantIsPositive(p->coeffs[i], 1) ||
		constantIsZero(p->coeffs[i], 1)) {	    
	      temp = addMemRef(makeMul(constantToExpression(p->coeffs[i]),
				       addMemRef(makePow(makeVariable(),
							 constantToExpression(p->monomialDegrees[i])))));
	    } else {
	      negate = 1;
	      t = constantNeg(p->coeffs[i]);	      
	      temp = addMemRef(makeMul(constantToExpression(t),
				       addMemRef(makePow(makeVariable(),
							 constantToExpression(p->monomialDegrees[i])))));
	      constantFree(t);
	    }
	  }
	}
      }
    }
    if (negate) {
      res = addMemRef(makeSub(addMemRef(res), addMemRef(temp)));
    } else {
      res = addMemRef(makeAdd(addMemRef(res), addMemRef(temp)));
    }
  }

  return addMemRef(res);
}

static inline node *__sparsePolynomialGetExpressionHorner(sparse_polynomial_t p) {
  unsigned int i, a, b, d;
  constant_t dc;
  node *res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Handle the strange case when p has no monomials */
  if (p->monomialCount == 0u) return addMemRef(makeConstantInt(0));

  /* Handle the case when the polynomial only has one monomial */
  if (p->monomialCount == 1u) {
    if (constantIsZero(p->monomialDegrees[0u], 0)) {
      return addMemRef(constantToExpression(p->coeffs[0u]));
    } else {
      if (constantIsOne(p->monomialDegrees[0u], 0)) {
	if (constantIsOne(p->coeffs[0u], 0)) {
	  return addMemRef(makeVariable());
	} else {
	  return addMemRef(makeMul(makeVariable(),
				   constantToExpression(p->coeffs[0u])));
	}
      } else {
	if (constantIsOne(p->coeffs[0u], 0)) {
	  return addMemRef(makePow(makeVariable(),
				   constantToExpression(p->monomialDegrees[0u])));
	} else {
	  return addMemRef(makeMul(addMemRef(makePow(makeVariable(),
						     constantToExpression(p->monomialDegrees[0u]))),
				   constantToExpression(p->coeffs[0u])));
	}
      }
    }
  }

  /* Perform Horner "evaluation" of p

     Here, the polynomial has at least 2 monomials.

  */
  if (constantIsOne(p->coeffs[p->monomialCount-1u], 0)) {
    /* Use NULL as a marker for a coefficient equal to one */
    res = NULL;
  } else {
    res = constantToExpression(p->coeffs[p->monomialCount-1u]);
  }
  for (i=p->monomialCount-1u;i>=1u;i--) {
    /* y <- x^(p->monomialDegrees[i] - p->monomialDegrees[i-1u]) * y */
    if (tryConstantToUnsignedInt(&a, p->monomialDegrees[i]) &&
	tryConstantToUnsignedInt(&b, p->monomialDegrees[i-1u])) {
      if (a < b) {
	sollyaFprintf(stderr,"Error: __sparsePolynomialGetExpressionHorner: monomial degrees not appropriately ordered\n");
	exit(1);
      }
      d = a - b;
      if (d != 0u) {
	if (d == 1u) {
	  if (res == NULL) {
	    res = makeVariable();
	  } else {
	    res = addMemRef(makeMul(makeVariable(), addMemRef(res)));
	  }
	} else {
	  if (res == NULL) {
	    res = addMemRef(makePow(makeVariable(),addMemRef(makeConstantInt(d))));
	  } else {
	    res = addMemRef(makeMul(addMemRef(makePow(makeVariable(),addMemRef(makeConstantInt(d)))),addMemRef(res)));
	  }
	}
      }
    } else {
      dc = constantSub(p->monomialDegrees[i], p->monomialDegrees[i-1u]);
      if (!constantIsZero(dc,0)) {
	if (constantIsOne(dc,0)) {
	  if (res == NULL) {
	    res = makeVariable();
	  } else {
	    res = addMemRef(makeMul(makeVariable(), addMemRef(res)));
	  }
	} else {
	  if (res == NULL) {
	    res = addMemRef(makePow(makeVariable(),constantToExpression(dc)));
	  } else {
	    res = addMemRef(makeMul(addMemRef(makePow(makeVariable(),constantToExpression(dc))),res));
	  }
	}
      }
      constantFree(dc);
    }
    /* y <- p->coeffs[i-1u] + y */
    if (res == NULL) {
      res = addMemRef(makeAdd(constantToExpression(p->coeffs[i-1u]), addMemRef(makeConstantInt(1))));
    } else {
      res = addMemRef(makeAdd(constantToExpression(p->coeffs[i-1u]), addMemRef(res)));
    }
  }
  /* y <- x^(p->monomialDegrees[0u]) * y */
  if (tryConstantToUnsignedInt(&a, p->monomialDegrees[0u])) {
    if (a != 0u) {
      if (a == 1u) {
	if (res == NULL) {
	  res = makeVariable();
	} else {
	  res = addMemRef(makeMul(makeVariable(), addMemRef(res)));
	}
      } else {
	if (res == NULL) {
	  res = addMemRef(makePow(makeVariable(),
				  addMemRef(makeConstantInt(a))));
	} else {
	  res = addMemRef(makeMul(addMemRef(makePow(makeVariable(),
						    addMemRef(makeConstantInt(a)))),
				  addMemRef(res)));
	}
      }
    }
  } else {
    if (!constantIsZero(p->monomialDegrees[0u],0)) {
      if (constantIsOne(p->monomialDegrees[0u],0)) {
	if (res == NULL) {
	  res = makeVariable();
	} else {
	  res = addMemRef(makeMul(makeVariable(), addMemRef(res)));
	}
      } else {
	if (res == NULL) {
	  res = addMemRef(makePow(makeVariable(),
				  constantToExpression(p->monomialDegrees[0u])));
	} else {
	  res = addMemRef(makeMul(addMemRef(makePow(makeVariable(),
						    constantToExpression(p->monomialDegrees[0u]))),
				  addMemRef(res)));
	}
      }
    }
  }

  if (res == NULL) {
    res = addMemRef(makeConstantInt(1));
  }

  /* Return the result */
  return addMemRef(res);
}

static inline node *sparsePolynomialGetExpression(sparse_polynomial_t p, int canonical) {
  if (canonical) return __sparsePolynomialGetExpressionCanonical(p);
  return __sparsePolynomialGetExpressionHorner(p);
}

void sparsePolynomialFPrintf(FILE *fd, sparse_polynomial_t p, int canonical) {
  node *t;

  /* Handle stupid cases */
  if (p == NULL) {
    sollyaFprintf(fd, "(null)");
  }

  /* Handle the general case.

     Can be optimized.

  */
  t = sparsePolynomialGetExpression(p,canonical);
  sollyaFprintf(fd, "%b",t);
  freeThing(t);
}

char *sparsePolynomialToString(sparse_polynomial_t p, int canonical) {
  node *t;
  char *str;
  char staticStr[8];
  int size, r;

  /* Handle stupid cases */
  if (p == NULL) return NULL;

  /* Handle the general case.

     Can be optimized.

  */
  t = sparsePolynomialGetExpression(p,canonical);
  size = sollya_snprintf(staticStr,8,"%b",t);
  if (size < 0) {
    freeThing(t);
    return NULL;
  }
  str = (char *) safeCalloc(size + 2, sizeof(char));
  r = sollya_snprintf(str,size,"%b",t);
  if (r < 0) {
    freeThing(t);
    safeFree(str);
    return NULL;
  }

  /* Return the string */
  return str;
}

static inline unsigned int sparsePolynomialGetMonomialCount(sparse_polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return 0u;

  /* Return the number of monomials */
  return p->monomialCount;
}

static inline int sparsePolynomialCoefficientsAreDyadic(sparse_polynomial_t p, int defVal) {
  unsigned int i;
  int t, sure;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 1;

  /* Go over the coefficients and check if they are dyadic */
  sure = 1;
  for (i=0u;i<p->monomialCount;i++) {
    t = constantIsDyadic(p->coeffs[i], 99);
    if (t != !!t) {
      sure = 0;
      t = !!t;
    }
    if (!t) return 0;
  }
  if (sure) return 1;
  return defVal;
}

static inline int sparsePolynomialCoefficientsAreRational(sparse_polynomial_t p, int defVal) {
  unsigned int i;
  int t, sure;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 1;

  /* Go over the coefficients and check if they are rational */
  sure = 1;
  for (i=0u;i<p->monomialCount;i++) {
    t = constantIsRational(p->coeffs[i], 99);
    if (t != !!t) {
      sure = 0;
      t = !!t;
    }
    if (!t) return 0;
  }
  if (sure) return 1;
  return defVal;
}

static inline int sparsePolynomialCoefficientsAreInteger(sparse_polynomial_t p, int defVal) {
  unsigned int i;
  int t, sure;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 1;

  /* Go over the coefficients and check if they are rational */
  sure = 1;
  for (i=0u;i<p->monomialCount;i++) {
    t = constantIsInteger(p->coeffs[i], 999);
    if (t != !!t) {
      sure = 0;
      t = !!t;
    }
    if (!t) return 0;
  }
  if (sure) return 1;
  return defVal;
}

static inline int sparsePolynomialCoefficientsHoldOnPrecBits(sparse_polynomial_t p, mp_prec_t prec, int defVal) {
  unsigned int i;
  int t, sure;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 1;

  /* Go over the coefficients and check if they hold on prec bits */
  sure = 1;
  for (i=0u;i<p->monomialCount;i++) {
    t = constantHoldsOnPrecBits(p->coeffs[i], prec, 997);
    if (t != !!t) {
      sure = 0;
      t = !!t;
    }
    if (!t) return 0;
  }
  if (sure) return 1;
  return defVal;
}

static inline sparse_polynomial_t sparsePolynomialRoundDyadic(sparse_polynomial_t p, mp_prec_t prec) {
  sparse_polynomial_t res;
  unsigned int startSize, i;
  constant_t c;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has dyadic coefficients, just return a
     copy of it.
  */
  if (sparsePolynomialCoefficientsAreDyadic(p, 0))
    return sparsePolynomialFromCopy(p);

  /* Here, at least one of the coefficients is not dyadic. Perform a
     "deep" copy while rounding the coefficients that are not
     dyadic.
  */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = 0u;
  startSize = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<p->monomialCount;i++) {
    c = constantRoundDyadic(p->coeffs[i], prec);
    if (!constantIsZero(c, 0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }

  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials multiplied gave zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  return res;
}

static inline sparse_polynomial_t sparsePolynomialRoundRational(sparse_polynomial_t p, mp_prec_t prec) {
  sparse_polynomial_t res;
  unsigned int startSize, i;
  constant_t c;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has rational coefficients, just return a
     copy of it.
  */
  if (sparsePolynomialCoefficientsAreRational(p, 0))
    return sparsePolynomialFromCopy(p);

  /* Here, at least one of the coefficients is not rational. Perform a
     "deep" copy while rounding the coefficients that are not
     rational.
  */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = 0u;
  startSize = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<p->monomialCount;i++) {
    c = constantRoundRational(p->coeffs[i], prec);
    if (!constantIsZero(c, 0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }

  /* If res->monomialCount still is zero, we never added anything
     because the two polynomials multiplied gave zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  return res;
}

static inline sparse_polynomial_t sparsePolynomialRound(sparse_polynomial_t p, mp_prec_t prec) {
  sparse_polynomial_t res;
  unsigned int startSize, i;
  constant_t c;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has coefficients that hold on prec bits,
     just return a copy of it.
  */
  if (sparsePolynomialCoefficientsHoldOnPrecBits(p, prec, 0))
    return sparsePolynomialFromCopy(p);

  /* Here, at least one of the coefficients does not hold on prec
     bits. Perform a "deep" copy while rounding the coefficients that
     are not dyadic.
  */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = 0u;
  startSize = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<p->monomialCount;i++) {
    c = constantRound(p->coeffs[i], prec);
    if (!constantIsZero(c, 0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }

  /* If res->monomialCount still is zero, we never added anything
     because the rounded polynomial became zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Return the polynomial */
  return res;
}

static inline sparse_polynomial_t sparsePolynomialRoundWithErrorBound(sparse_polynomial_t p, sollya_mpfi_t I, mpfr_t delta) {
  mp_prec_t prec;
  mpfr_t deltaCoeff, deg;
  sparse_polynomial_t res;
  unsigned int startSize, i;
  constant_t c;
  sollya_mpfi_t IpowDeg, degI;
  
  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle the cases when delta is NaN or zero

     Just return an unrounded copy of the polynomial.

  */
  if (mpfr_nan_p(delta) || mpfr_zero_p(delta)) {
    return sparsePolynomialFromCopy(p);
  }

  /* Handle the cases when I contains Inf or NaN 

     Just return an unrounded copy of the polynomial

  */
  if (sollya_mpfi_has_nan(I) || sollya_mpfi_has_infinity(I)) {
    return sparsePolynomialFromCopy(p);
  }

  /* Handle the case when delta is Inf 

     Just round each of the polynomial's coefficients to 53 bits.

  */
  if (mpfr_inf_p(delta)) {
    return sparsePolynomialRound(p, ((mp_prec_t) 53));
  }

  /* Handle the stupid case when p has no monomials

     Just return an unrounded copy of p.

  */
  if (p->monomialCount == 0u) {
    return sparsePolynomialFromCopy(p);
  }
  
  /* Here delta is neither NaN, nor Inf nor zero and I is neither NaN
     nor Inf

     Perform the rounding with an error bounded by abs(delta).

  */
  prec = mpfr_get_prec(delta);
  if (prec < ((mp_prec_t) (8 * sizeof(unsigned int) + 10))) {
    prec = (mp_prec_t) (8 * sizeof(unsigned int) + 10);
  }
  __sollyaRecycleMpfrInit2(deltaCoeff, prec);
  mpfr_abs(deltaCoeff, delta, GMP_RNDD);
  mpfr_div_ui(deltaCoeff, deltaCoeff, p->monomialCount, GMP_RNDD);

  /* Handle the strange (and rare) case when deltaCoeff is zero.

     Just return an unrounded copy of p.

     Subsume the cases when deltaCoeff is negative, Inf or NaN in the case
     special case code.

  */
  if (mpfr_inf_p(deltaCoeff) || (mpfr_nan_p(deltaCoeff) || (mpfr_sgn(deltaCoeff) <= 0))) {
    __sollyaRecycleMpfrClear(deltaCoeff);
    return sparsePolynomialFromCopy(p);
  }

  /* Initialize an interval variable to hold I^deg for the different
     degrees 
  */
  __sollyaRecycleMpfiInit2(IpowDeg, (sollya_mpfi_get_prec(I) + ((mp_prec_t) 64)));

  /* Initialize an FP variable to hold an upper rounding of the
     different degrees and a temporary interval variable of same
     precision
  */
  __sollyaRecycleMpfrInit2(deg, (sollya_mpfi_get_prec(I) + ((mp_prec_t) 64)));
  __sollyaRecycleMpfiInit2(degI, mpfr_get_prec(deg));
  
  /* Handle coefficient by coefficient */
  res = __sparsePolynomialAllocate();
  res->refCount = 1;
  res->hash.hasHash = 0;
  res->monomialCount = 0u;
  startSize = p->monomialCount;
  res->coeffs = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  res->monomialDegrees = (constant_t *) safeCalloc(startSize, sizeof(constant_t));
  for (i=0u;i<p->monomialCount;i++) {
    constantEvalMpfrUp(deg, p->monomialDegrees[i]);
    sollya_mpfi_set_fr(degI, deg);
    sollya_mpfi_pow(IpowDeg, I, degI);
    c = constantRoundWithErrorBound(p->coeffs[i], IpowDeg, deltaCoeff);
    if (!constantIsZero(c, 0)) {
      res->coeffs[res->monomialCount] = c;
      res->monomialDegrees[res->monomialCount] = constantFromCopy(p->monomialDegrees[i]);
      (res->monomialCount)++;
    } else {
      constantFree(c);
    }
  }

    /* If res->monomialCount still is zero, we never added anything
     because the rounded polynomial became zero. We add a zero
     coefficient in this case.
  */
  if (res->monomialCount == 0u) {
    res->coeffs[res->monomialCount] = constantFromInt(0);
    res->monomialDegrees[res->monomialCount] = constantFromInt(0);
    (res->monomialCount)++;
  }
  /* Set the degree of the polynomial */
  res->deg = constantFromCopy(res->monomialDegrees[res->monomialCount - 1u]);
  /* Adjust memory to the real amount of monomials */
  if (res->monomialCount != startSize) {
    res->coeffs = (constant_t *) safeRealloc(res->coeffs,
					     ((size_t) (res->monomialCount)) * sizeof(constant_t));
    res->monomialDegrees = (constant_t *) safeRealloc(res->monomialDegrees,
						      ((size_t) (res->monomialCount)) * sizeof(constant_t));
  }
  /* Adjust the degree for zero leading coefficients */
  __sparsePolynomialAdjustDegree(res);

  /* Clear temporaries */
  __sollyaRecycleMpfrClear(deltaCoeff);
  __sollyaRecycleMpfiClear(IpowDeg);
  __sollyaRecycleMpfrClear(deg);
  __sollyaRecycleMpfiClear(degI);
  
  /* Return the polynomial */
  return res;
}

static inline unsigned int sparsePolynomialGetReferenceCount(sparse_polynomial_t p) {
  if (p == NULL) return 0u;
  return p->refCount;
}

static inline int sparsePolynomialUsesExpressionConstant(sparse_polynomial_t p) {
  unsigned int i;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 0;

  /* Go over all monomials and check the constants */
  for (i=0u;i<p->monomialCount;i++) {
    if (constantUsesExpressionConstant(p->coeffs[i])) return 1;
    if (constantUsesExpressionConstant(p->monomialDegrees[i])) return 1;
  }
  if (constantUsesExpressionConstant(p->deg)) return 1;

  /* The expression has nowhere been found */
  return 0;
}

static inline int sparsePolynomialReferencesExpression(sparse_polynomial_t p, node *expr) {
  unsigned int i;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* If the polynomial does not use any expression, it cannot
     reference the given expression
  */
  if (!sparsePolynomialUsesExpressionConstant(p)) {
    return 0;
  }

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return 0;

  /* Go over all monomials and check the constants */
  for (i=0u;i<p->monomialCount;i++) {
    if (constantReferencesExpression(p->coeffs[i], expr)) return 1;
    if (constantReferencesExpression(p->monomialDegrees[i], expr)) return 1;
  }
  if (constantReferencesExpression(p->deg, expr)) return 1;

  /* The expression has nowhere been found */
  return 0;
}

static inline size_t sparsePolynomialDeepReferenceCount(sparse_polynomial_t p, node *expr) {
  unsigned int i;
  size_t res;
  
  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the case when the polynomial has no monomials */
  if (p->monomialCount == 0u) return ((size_t) 0);

  /* Go over all monomials and check the constants */
  res = (size_t) 0;
  for (i=0u;i<p->monomialCount;i++) {
    res += constantDeepReferenceCount(p->coeffs[i], expr);
    res += constantDeepReferenceCount(p->monomialDegrees[i], expr);
  }
  res += constantDeepReferenceCount(p->deg, expr);
  
  /* Return the result */
  return res;
}

static inline void __sparsePolynomialConstantInvalidateExpressionCache(sparse_polynomial_t p, node *expr) {

  /* Handle stupid input */
  if (p == NULL) return;
  if (expr == NULL) return;

  /* Handle general case */
  if (expr->nodeType != MEMREF) return;
  if (p->monomialCount != 1u) return;
  if (!constantIsZero(p->monomialDegrees[0u],1)) return;
  if (!constantIsZero(p->deg,1)) return;
  __constantInvalidateExpressionCache(p->coeffs[0u], expr);
}

static inline uint64_t sparsePolynomialHash(sparse_polynomial_t p) {
  uint64_t hash;
  unsigned int i;

  /* Handle stupid input */
  if (p == NULL) return hashPointer(NULL);

  /* Check if the hash has already been computed */
  if (p->hash.hasHash) {
    return p->hash.hash;
  }

  /* Compute the hash */
  hash = hashUnsignedInt(p->monomialCount);
  hash = hashCombine(hash, constantHash(p->deg));
  for (i=0u;i<p->monomialCount;i++) {
    hash = hashCombine(hash, constantHash(p->coeffs[i]));
    hash = hashCombine(hash, constantHash(p->monomialDegrees[i]));
  }

  /* Cache the result */
  p->hash.hash = hash;
  p->hash.hasHash = 1;

  /* Return the hash */
  return hash;
}

static inline int __sparsePolynomialInterpolateWithConstantsDividedDifferences(constant_t *dividedDifferences, constant_t *X, constant_t *Y, unsigned int n) {
  unsigned int i, j, k, l;
  constant_t *dd, *t;
  constant_t t1, t2, t3;

  /* Allocate a vector for the divided differences */
  dd = (constant_t *) safeCalloc(n,sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    dd[i] = NULL;
  }
  
  /* Allocate a temporary vector of size n */
  t = (constant_t *) safeCalloc(n,sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    t[i] = NULL;
  }
  
  /* Compute the divided differences and write back the diagonal
     results of the divided differences matrix.

     The temporary vector t only holds one column of the matrix at a
     time and it only starts at the diagonal.
  */
  for (i=0u;i<=n-1u;i++) {
    if (t[i] != NULL) {
      constantFree(t[i]);
      t[i] = NULL;
    }
    t[i] = constantFromCopy(Y[i]);
    if (i > 0u) {
      for (j=i-1u,k=i;k>0u;j--,k--) {
	t1 = constantSub(X[i], X[j]);
	if (constantIsZero(t1, 1)) {
	  /* Two entries in the X vector are equal, not as they should be. 

	     Deallocate the temporary vector and the vector holding the
	     divided differences 
	  */
	  for (l=0u;l<=n-1u;l++) {
	    if (t[l] != NULL) {
	      constantFree(t[l]);
	    }
	    if (dd[l] != NULL) {
	      constantFree(dd[l]);
	    }
	  }
	  safeFree(t);
	  safeFree(dd);

	  /* Free t1 */
	  constantFree(t1);
	  
	  /* Signal failure */
	  return 0;
	}
	/* Here, t1 is surely non-zero and we can hence divide by it */
	t2 = constantSub(t[j+1u], t[j]);
	t3 = constantDiv(t2, t1);
	if (t[j] != NULL) {
	  constantFree(t[j]);
	  t[j] = NULL;
	}
	t[j] = t3;
	constantFree(t1);
	constantFree(t2);
      }
    }
    if (dd[i] != NULL) {
      constantFree(dd[i]);
      dd[i] = NULL;
    }
    dd[i] = constantFromCopy(t[0]);
  }

  /* Check if we filled in all dd slots */
  for (i=0u;i<=n-1u;i++) {
    if (dd[i] == NULL) {
      /* Deallocate the temporary vector and the vector holding the
	 divided differences 
      */
      for (l=0u;l<=n-1u;l++) {
	if (t[l] != NULL) {
	  constantFree(t[l]);
	}
	if (dd[l] != NULL) {
	  constantFree(dd[l]);
	}
      }
      safeFree(t);
      safeFree(dd);
      
      /* Signal failure */
      return 0;
    }
  }

  /* Write back the result */
  for (i=0u;i<=n-1u;i++) {
    dividedDifferences[i] = constantFromCopy(dd[i]);
  }
  
  /* Deallocate the temporary vector and the vector holding the
     divided differences 
  */
  for (i=0u;i<=n-1u;i++) {
    if (t[i] != NULL) {
      constantFree(t[i]);
    }
    if (dd[i] != NULL) {
      constantFree(dd[i]);
    }
  }
  safeFree(t);
  safeFree(dd);

  /* Signal success */
  return 1;
}

static inline int __sparsePolynomialInterpolateWithConstantsRationalDividedDifferences(mpq_t *dividedDifferences, mpq_t *X, mpq_t *Y, unsigned int n) {
  unsigned int i, j, k, l;
  mpq_t *dd, *t;
  mpq_t t1, t2;

  /* Allocate a vector for the divided differences */
  dd = (mpq_t *) safeCalloc(n,sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(dd[i]);
  }
  
  /* Allocate a temporary vector of size n */
  t = (mpq_t *) safeCalloc(n,sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(t[i]);
  }

  /* Initialize two temporary variables */
  __sollyaRecycleMpqInit(t1);
  __sollyaRecycleMpqInit(t2);

  /* Compute the divided differences and write back the diagonal
     results of the divided differences matrix.

     The temporary vector t only holds one column of the matrix at a
     time and it only starts at the diagonal.
  */
  for (i=0u;i<=n-1u;i++) {
    mpq_set(t[i], Y[i]);
    if (i > 0u) {
      for (j=i-1u,k=i;k>0u;j--,k--) {
	mpq_sub(t1, X[i], X[j]);
	if (mpq_sgn(t1) == 0) {
	  /* Two entries in the X vector are equal, not as they should be. 

	     Deallocate the temporary vector and the vector holding the
	     divided differences 
	  */
	  for (l=0u;l<=n-1u;l++) {
	    __sollyaRecycleMpqClear(t[l]);
	    __sollyaRecycleMpqClear(dd[l]);
	  }
	  safeFree(t);
	  safeFree(dd);

	  /* Clear t1 and t2 */
	  __sollyaRecycleMpqClear(t1);
	  __sollyaRecycleMpqClear(t2);
	  
	  /* Signal failure */
	  return 0;
	}
	/* Here, t1 is surely non-zero and we can hence divide by it */
	mpq_sub(t2, t[j+1u], t[j]);
	mpq_div(t[j], t2, t1);
      }
    }
    mpq_set(dd[i], t[0]);
  }

  /* Clear t1 and t2 */
  __sollyaRecycleMpqClear(t1);
  __sollyaRecycleMpqClear(t2);
  
  /* Write back the result */
  for (i=0u;i<=n-1u;i++) {
    mpq_set(dividedDifferences[i], dd[i]);
  }
  
  /* Deallocate the temporary vector and the vector holding the
     divided differences 
  */
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqClear(t[i]);
    __sollyaRecycleMpqClear(dd[i]);
  }
  safeFree(t);
  safeFree(dd);

  /* Signal success */
  return 1;
}

/* At the start of the function, let 

   p(x) = sum C[i] * x^i

   The function changes C in such a way that in the end

   q(x) = sum C[i] * x^i

   where 

   q(x) = d + (x - X) * p(x)
        = (d + x * p(x)) - X * p(x)

*/
static inline void __sparsePolynomialInterpolateWithConstantsRationalHornerStep(mpq_t *C, mpq_t d, mpq_t X, unsigned int n, mpq_t *scratch) {
  unsigned int i;

  if (n == 0u) return;
  for (i=n-1u;i>0;i--) {
    mpq_set(scratch[i], C[i-1u]);
  }
  mpq_set(scratch[0u], d);
  for (i=0u;i<=n-1u;i++) {
    mpq_mul(C[i], X, C[i]);
    mpq_sub(C[i], scratch[i], C[i]);
  }
}

static inline int __sparsePolynomialInterpolateWithConstantsRationalInner(mpq_t *C, mpq_t *X, mpq_t *Y, unsigned int n) {
  unsigned int i;
  mpq_t *dd, *scratch;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (C == NULL) return 0;
  if (n == 0u) return 0;

  /* Allocate vector of rational numbers to hold diagonal of divided differences */
  dd = (mpq_t *) safeCalloc(n,sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(dd[i]);
  }
  
  /* Try to compute the divided differences */
  if (!__sparsePolynomialInterpolateWithConstantsRationalDividedDifferences(dd, X, Y, n)) {
    /* Something went wrong with the computation of the divided
       differences. In most cases this just means that there was a
       repeated (non-distinct) entry in the X vector. 

       We deallocate our stuff and signal failure.

    */
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpqClear(dd[i]);
    }
    safeFree(dd);
    return 0;
  }

  /* Allocate a scratch vector */
  scratch = (mpq_t *) safeCalloc(n,sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(scratch[i]);
  }
  
  /* We must form the polynomial 

     p(x) = dd[0] + (x-X[0]) * (dd[1] + (x-X[1]) * (dd[2] + ...))
     
     thru Horner evaluation.
  */
  for (i=n-1u;(i+1u)>0u;i--) {
    mpq_set_ui(C[i], 0u, 1u);
    mpq_set_ui(scratch[i], 0u, 1u);
  }
  for (i=n-1u;(i+1u)>0u;i--) {
    __sparsePolynomialInterpolateWithConstantsRationalHornerStep(C, dd[i], X[i], n, scratch);
  }

  /* Deallocate the divided differences and the scratch vector */
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqClear(dd[i]);
    __sollyaRecycleMpqClear(scratch[i]);
  }
  safeFree(dd);
  safeFree(scratch);

  /* Signal success */
  return 1;
}

static inline int sparsePolynomialInterpolateWithConstantsRational(sparse_polynomial_t *poly, constant_t *X, constant_t *Y, unsigned int n) {
  mpq_t *XX, *YY, *CC;
  unsigned int i, k;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
  }

  /* Check whether all abscissas X and ordinates Y can be evaluated to
     rational numbers.

     If no, return failure. The higher level function will handle the
     case at a higher cost.

  */
  for (i=0u;i<=n-1u;i++) {
    if (!constantIsRational(X[i],0)) return 0;
    if (!constantIsRational(Y[i],0)) return 0;
  }
  XX = (mpq_t *) safeCalloc(n, sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(XX[i]);
    if (!tryConstantToMpq(XX[i], X[i])) {
      for (k=0u;k<=i;k++) {
	__sollyaRecycleMpqClear(XX[k]);
      }
      safeFree(XX);
      return 0;
    }
  }
  YY = (mpq_t *) safeCalloc(n, sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(YY[i]);
    if (!tryConstantToMpq(YY[i], Y[i])) {
      for (k=0u;k<=i;k++) {
	__sollyaRecycleMpqClear(YY[k]);
      }
      safeFree(YY);
      for (k=0u;k<=n-1u;k++) {
	__sollyaRecycleMpqClear(XX[k]);
      }
      safeFree(XX);
      return 0;
    }
  }

  /* Allocate and initialize rational numbers for the polynomial's
     coefficients. 
  */
  CC = (mpq_t *) safeCalloc(n, sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(CC[i]);
  }

  /* Try to compute the interpolation polynomial on rational
     numbers 
  */
  if (__sparsePolynomialInterpolateWithConstantsRationalInner(CC, XX, YY, n)) {
    /* The rational interpolation worked. Build the polynomial. */

    if (poly != NULL) {
      *poly = sparsePolynomialFromMpqCoefficients(CC, n-1u);
    }

    /* Clear and deallocate the temporary vectors */
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpqClear(XX[i]);
      __sollyaRecycleMpqClear(YY[i]);
      __sollyaRecycleMpqClear(CC[i]);
    }
    safeFree(XX);
    safeFree(YY);
    safeFree(CC);
    
    /* Signal success */
    return 1;
  }

  /* Clear and deallocate the temporary vectors */
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqClear(XX[i]);
    __sollyaRecycleMpqClear(YY[i]);
    __sollyaRecycleMpqClear(CC[i]);
  }
  safeFree(XX);
  safeFree(YY);
  safeFree(CC);

  /* Signal failure */
  return 0;
}

static inline int sparsePolynomialInterpolateWithConstants(sparse_polynomial_t *poly, constant_t *X, constant_t *Y, unsigned int n) {
  unsigned int i;
  constant_t *dd;
  sparse_polynomial_t p, id, t1, t2, t3;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
  }

  /* Try to use a specialized function for rational abscissas and ordinates */
  if (sparsePolynomialInterpolateWithConstantsRational(&p, X, Y, n)) {
    if (poly != NULL) {
      *poly = p;
    } else {
      sparsePolynomialFree(p);
    }
    return 1;
  }
  
  /* Allocate vector of constants to hold diagonal of divided differences */
  dd = (constant_t *) safeCalloc(n,sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    dd[i] = NULL;
  }

  /* Try to compute the divided differences */
  if (!__sparsePolynomialInterpolateWithConstantsDividedDifferences(dd, X, Y, n)) {
    /* Something went wrong with the computation of the divided
       differences. In most cases this just means that there was a
       repeated (non-distinct) entry in the X vector. 

       We deallocate our stuff and signal failure.

    */
    for (i=0u;i<=n-1u;i++) {
      if (dd[i] != NULL) constantFree(dd[i]);
    }
    safeFree(dd);
    return 0;
  }

  /* We must form the polynomial 

     p(x) = dd[0] + (x-X[0]) * (dd[1] + (x-X[1]) * (dd[2] + ...))
     
     thru Horner evaluation.
  */
  id = sparsePolynomialFromIdentity();
  p = sparsePolynomialFromIntConstant(0);
  for (i=n-1u;(i+1u)>0u;i--) {
    t1 = sparsePolynomialSubConstant(id, X[i]);
    t2 = sparsePolynomialMul(t1, p);
    t3 = sparsePolynomialAddConstant(t2, dd[i]);
    sparsePolynomialFree(p);
    p = t3;
    sparsePolynomialFree(t1);
    sparsePolynomialFree(t2);
  }
  sparsePolynomialFree(id);

  /* Deallocate the divided differences */
  for (i=0u;i<=n-1u;i++) {
    if (dd[i] != NULL) constantFree(dd[i]);
  }
  safeFree(dd);
  
  /* Write back the result and signal success */
  if (poly != NULL) {
    *poly = p;
  } else {
    sparsePolynomialFree(p);
  }
  return 1;
}

static inline int sparsePolynomialInterpolateWithExpressions(sparse_polynomial_t *poly, node **X, node **Y, unsigned int n) {
  unsigned int i;
  constant_t *XX, *YY;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
    if (!isConstant(Y[i])) return 0;
  }

  /* Allocate two vectors of constants XX and YY and convert the
     expressions to constants.
  */
  XX = (constant_t *) safeCalloc(n,sizeof(constant_t));
  YY = (constant_t *) safeCalloc(n,sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    XX[i] = constantFromExpression(X[i]);
    YY[i] = constantFromExpression(Y[i]);
  }

  /* Interpolate with the vectors of constants */
  res = sparsePolynomialInterpolateWithConstants(&p, XX, YY, n);

  /* Deallocate the vectors of constants */
  for (i=0u;i<=n-1u;i++) {
    constantFree(XX[i]);
    constantFree(YY[i]);
  }
  safeFree(XX);
  safeFree(YY);

  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

static inline int sparsePolynomialInterpolateFunctionWithExpressions(sparse_polynomial_t *poly, node **X, node *f, unsigned int n) {
  unsigned int i;
  node **Y;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
  }

  /* Allocate a vector Y of size n to hold the f(X[i]) and form the
     f(X[i]) 
  */
  Y = (node **) safeCalloc(n,sizeof(node *));
  for (i=0u;i<=n-1u;i++) {
    Y[i] = substitute(f,X[i]);
  }

  /* Call the interpolation routine on vectors X/Y */
  res = sparsePolynomialInterpolateWithExpressions(&p, X, Y, n);

  /* Deallocate the temporary vector */
  for (i=0u;i<=n-1u;i++) {
    freeThing(Y[i]);
  }
  safeFree(Y);
  
  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

static inline int sparsePolynomialInterpolateWithPoints(sparse_polynomial_t *poly, mpfr_t *X, mpfr_t *Y, unsigned int n) {
  unsigned int i;
  constant_t *XX, *YY;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (mpfr_inf_p(X[i])) return 0;
    if (mpfr_nan_p(X[i])) return 0;
    if (mpfr_inf_p(Y[i])) return 0;
    if (mpfr_inf_p(Y[i])) return 0;
  }

  /* Allocate two vectors of constants XX and YY and convert the
     numbers to constants.
  */
  XX = (constant_t *) safeCalloc(n,sizeof(constant_t));
  YY = (constant_t *) safeCalloc(n,sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    XX[i] = constantFromMpfr(X[i]);
    YY[i] = constantFromMpfr(Y[i]);
  }

  /* Interpolate with the vectors of constants */
  res = sparsePolynomialInterpolateWithConstants(&p, XX, YY, n);

  /* Deallocate the vectors of constants */
  for (i=0u;i<=n-1u;i++) {
    constantFree(XX[i]);
    constantFree(YY[i]);
  }
  safeFree(XX);
  safeFree(YY);

  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

static inline int sparsePolynomialInterpolateFunctionWithPoints(sparse_polynomial_t *poly, mpfr_t *X, node *f, unsigned int n) {
  unsigned int i;
  node **XX, **YY;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (mpfr_inf_p(X[i])) return 0;
    if (mpfr_nan_p(X[i])) return 0;
  }

  /* Allocate two vectors XX and YY of size n to hold 
     
     * expressions XX[i] corresponding to the numbers X[i]
     * expressions YY[i] corresponding to f(XX[i]) 

     Form all these expressions.

  */
  XX = (node **) safeCalloc(n,sizeof(node *));
  YY = (node **) safeCalloc(n,sizeof(node *));
  for (i=0u;i<=n-1u;i++) {
    XX[i] = addMemRef(makeConstant(X[i]));
    YY[i] = substitute(f,XX[i]);
  }

  /* Call the interpolation routine on vectors XX/YY */
  res = sparsePolynomialInterpolateWithExpressions(&p, XX, YY, n);

  /* Deallocate the temporary vectors */
  for (i=0u;i<=n-1u;i++) {
    freeThing(XX[i]);
    freeThing(YY[i]);
  }
  safeFree(XX);
  safeFree(YY);
  
  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

/* A helper type used only in a couple of internal functions */
struct __sparse_polynomial_interpolate_mpfi_pair_struct_t {
  sollya_mpfi_t X;
  sollya_mpfi_t Y;
};
typedef struct __sparse_polynomial_interpolate_mpfi_pair_struct_t __sparse_polynomial_interpolate_mpfi_pair_t;

/* This function is used through its function pointer below. The
   function pointer is given to qsort in libc 
*/
int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkPairsSortCompare(const void *A, const void *B) {
  __sparse_polynomial_interpolate_mpfi_pair_t *a, *b;
  int res;

  /* Cast the weirdness into real types */
  a = (__sparse_polynomial_interpolate_mpfi_pair_t *) A;
  b = (__sparse_polynomial_interpolate_mpfi_pair_t *) B;

  /* Compare with respect to the infima of the abscissa entries 

     HACK ALERT: For performance reasons, we will access the internals
     of an mpfi_t !!!
  */
  res = mpfr_cmp(&(a->X->left), &(b->X->left));

  /* Return the comparison result */
  return res;
}

/* At the start of the function, let 

   p(x) = sum C[i] * x^i

   The function changes C in such a way that in the end

   q(x) = sum C[i] * x^i

   where 

   q(x) = d + (x - X) * p(x)
        = (d + x * p(x)) - X * p(x)

*/
static inline void __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkHornerStep(sollya_mpfi_t *C,
													     sollya_mpfi_t d,
													     sollya_mpfi_t X,
													     unsigned int n,
													     sollya_mpfi_t *scratch) {
  unsigned int i;

  if (n == 0u) return;
  for (i=n-1u;i>0;i--) {
    sollya_mpfi_set(scratch[i], C[i-1u]);
  }
  sollya_mpfi_set(scratch[0u], d);
  for (i=0u;i<=n-1u;i++) {
    sollya_mpfi_mul(C[i], X, C[i]);
    sollya_mpfi_sub(C[i], scratch[i], C[i]);
  }
}

/* Internal function, no checks needed. */
static inline int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkIntervals(sollya_mpfi_t *C,
													   __sparse_polynomial_interpolate_mpfi_pair_t *XY, 
													   unsigned int n,
													   mp_prec_t prec) {
  unsigned int i, j, k, l;
  sollya_mpfi_t *dd, *t;
  sollya_mpfi_t t1, t2;

  /* Allocate a vector for the divided differences */
  dd = (sollya_mpfi_t *) safeCalloc(n,sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(dd[i], prec);
  }
  
  /* Allocate a temporary vector of size n */
  t = (sollya_mpfi_t *) safeCalloc(n,sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(t[i], prec);
  }

  /* Initialize two temporary variables */
  __sollyaRecycleMpfiInit2(t1, prec);
  __sollyaRecycleMpfiInit2(t2, prec);

  /* Compute the divided differences and write back the diagonal
     results of the divided differences matrix.

     The temporary vector t only holds one column of the matrix at a
     time and it only starts at the diagonal.
  */
  for (i=0u;i<=n-1u;i++) {
    sollya_mpfi_set(t[i], XY[i].Y);
    if (i > 0u) {
      for (j=i-1u,k=i;k>0u;j--,k--) {
	sollya_mpfi_sub(t1, XY[i].X, XY[j].X);
	if (sollya_mpfi_has_zero(t1)) {
	  /* Two entries in the abscissa vector, subtracted one from
	     the other, yield to an interval containing zero, which is
	     not as it should be.

	     Deallocate the temporary vector and the vector holding the
	     divided differences 
	  */
	  for (l=0u;l<=n-1u;l++) {
	    __sollyaRecycleMpfiClear(t[l]);
	    __sollyaRecycleMpfiClear(dd[l]);
	  }
	  safeFree(t);
	  safeFree(dd);

	  /* Clear t1 and t2 */
	  __sollyaRecycleMpfiClear(t1);
	  __sollyaRecycleMpfiClear(t2);
	  
	  /* Signal failure */
	  return 0;
	}
	/* Here, t1 is surely non-zero and we can hence divide by it */
	sollya_mpfi_sub(t2, t[j+1u], t[j]);
	sollya_mpfi_div(t[j], t2, t1);
      }
    }
    sollya_mpfi_set(dd[i], t[0]);
  }

  /* Clear t1 and t2 */
  __sollyaRecycleMpfiClear(t1);
  __sollyaRecycleMpfiClear(t2);
    
  /* Here we have a vector dd of divided differences 

     We will reuse the temporary vector for other purpose now.
  
     We must form the polynomial 

     p(x) = dd[0] + (x-X[0]) * (dd[1] + (x-X[1]) * (dd[2] + ...))
     
     thru Horner evaluation.
  */
  for (i=n-1u;(i+1u)>0u;i--) {
    sollya_mpfi_set_si(C[i], 0);
    sollya_mpfi_set_si(t[i], 0);
  }
  for (i=n-1u;(i+1u)>0u;i--) {
    __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkHornerStep(C, dd[i], XY[i].X, n, t);
  }

  /* Clear and deallocate the temporary vectors. */
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiClear(t[i]);
    __sollyaRecycleMpfiClear(dd[i]);
  }
  safeFree(t);
  safeFree(dd);
  
  /* Signal success */
  return 1;
}

/* This function does not need to perform checks on stupid input and is allowed to destroy the XY array */
static inline int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkPairs(sparse_polynomial_t *poly,
												       mpfr_t Delta,
												       __sparse_polynomial_interpolate_mpfi_pair_t *XY, 
												       unsigned int n,
												       sollya_mpfi_t I, mp_prec_t prec) {
  unsigned int i;
  sollya_mpfi_t *C;
  mpfr_t *c;
  sollya_mpfi_t J;
  int okay;
  
  /* Start by sorting the abscissa/ordinate array by ascending
     abscissa infima 
  */
  qsort(XY, n, sizeof(__sparse_polynomial_interpolate_mpfi_pair_t),
	__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkPairsSortCompare);

  /* Now that the abscissa/ordinate array is sorted by ascending 
     abscissa infima, check whether subsequent abscissa intervals
     overlap: if they overlap, fail, as this would yield to
     differences x_i - x_j that contain zero.
     
  */
  if (n >= 2u) {
    for (i=0u;i<=(n-1u)-1u;i++) {
      /* HACK ALERT: For performance reasons, we will access the
	 internals of an mpfi_t !!! 
      */
      if (mpfr_cmp(&(XY[i].X->right), &(XY[i+1u].X->left)) >= 0) {
	/* The abscissa intervals i and i + 1 intersect. Fail. */
	return 0;
      }
    }
  }

  /* Here, we have an array XY of n interpolation points for which the
     abscissa intervals do not intersect.

     Allocate and initialize a vector of interval variables to 
     hold the interval coefficients of the polynomial.

  */
  C = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(C[i], prec);
  }

  /* Call a helper function to do the interpolation job on
     intervals 
  */
  if (!__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkIntervals(C, XY, n, prec)) {
    /* The interpolation did not work.

       Clear and deallocate the temporaries, then fail.

    */
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpfiClear(C[i]);
    }
    safeFree(C);

    /* Fail */
    return 0;
  }

  /* Here the interpolation on intervals worked. 

     Allocate and initialize a vector of MPFR variables to hold point
     coefficients.

  */
  c = (mpfr_t *) safeCalloc(n, sizeof(mpfr_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfrInit2(c[i], prec);
  }

  /* Determine the point coefficients, leave the interval "remainders"
     in the interval vector. 
  */
  for (i=0u;i<=n-1u;i++) {
    if (sollya_mpfi_has_zero(C[i])) {
      mpfr_set_si(c[i], 0, GMP_RNDN);
    } else {
      sollya_mpfi_mid(c[i], C[i]);
    }
    sollya_mpfi_sub_fr(C[i], C[i], c[i]);
  }

  /* Perform a Horner evaluation of the "remainder" polynomial over
     the whole interval I. 
  */
  __sollyaRecycleMpfiInit2(J, prec);
  sollya_mpfi_set_si(J, 0);
  for (i=n-1u;(i+1u)>0u;i--) {
    sollya_mpfi_mul(J, J, I);
    sollya_mpfi_add(J, J, C[i]);
  }

  /* Take absolute value of the bound */
  sollya_mpfi_abs(J, J);

  /* Check if the enclosure of the "remainder" polynomial contains NaN
     or Inf. Also check if one of the point coefficients is Inf or
     NaN. If this is the case, clear and deallocate everything and
     fail.
  */
  if (sollya_mpfi_has_nan(J) ||
      sollya_mpfi_has_infinity(J)) {
    okay = 0;
  } else {
    okay = 1;
    for (i=0u;i<=n-1u;i++) {
      if (mpfr_nan_p(c[i]) ||
	  mpfr_inf_p(c[i])) {
	okay = 0;
	break;
      }
    }
  }
  if (!okay) {
    /* Clear and deallocate everything and fail. */
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpfiClear(C[i]);
      __sollyaRecycleMpfrClear(c[i]);
    }
    safeFree(C);
    safeFree(c);
    __sollyaRecycleMpfiClear(J);

    /* Fail */
    return 0;
  }

  /* Here we have point coefficients that are real, finite numbers
     (not NaN nor Inf) and a bound J on the absolute value of the
     error of that polynomial over I. We know that the supremum of the
     bound is a finite real number.

     We construct a polynomial out of the point coefficients and 
     we write the bound into Delta.

  */
  if (poly != NULL) {
    *poly = sparsePolynomialFromMpfrCoefficients(c, n-1u);
  }
  sollya_mpfi_get_right(Delta, J);

  /* Clear and deallocate everything. */
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiClear(C[i]);
    __sollyaRecycleMpfrClear(c[i]);
  }
  safeFree(C);
  safeFree(c);
  __sollyaRecycleMpfiClear(J);
  
  /* Signal success */
  return 1;
}

static inline int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWork(sparse_polynomial_t *poly,
												  mpfr_t Delta,
												  sollya_mpfi_t *X, sollya_mpfi_t *Y, unsigned int n,
												  sollya_mpfi_t I, mp_prec_t prec) {
  unsigned int i;
  __sparse_polynomial_interpolate_mpfi_pair_t *XY;
  sparse_polynomial_t p;
  mpfr_t D;
  int res;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (sollya_mpfi_has_nan(X[i])) return 0;
    if (sollya_mpfi_has_infinity(X[i])) return 0;
    if (sollya_mpfi_has_nan(Y[i])) return 0;
    if (sollya_mpfi_has_infinity(Y[i])) return 0;
  }
  if (sollya_mpfi_has_nan(I)) return 0;
  if (sollya_mpfi_has_infinity(I)) return 0;

  /* Copy the abscissa/ordinate pairs into an array we can sort by
     abscissa infima and modify in case of overlap of abscissa
     intervals.
  */
  XY = (__sparse_polynomial_interpolate_mpfi_pair_t *) safeCalloc(n, sizeof(__sparse_polynomial_interpolate_mpfi_pair_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(XY[i].X, sollya_mpfi_get_prec(X[i]));
    __sollyaRecycleMpfiInit2(XY[i].Y, sollya_mpfi_get_prec(Y[i]));
    sollya_mpfi_set(XY[i].X, X[i]);
    sollya_mpfi_set(XY[i].Y, Y[i]);
  }

  /* Initialize a temporary MPFR variable */
  __sollyaRecycleMpfrInit2(D, mpfr_get_prec(Delta));
  
  /* Try a helper function on the abscissa/ordinate pairs */
  res = 0;
  if (__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWorkPairs(&p, D, XY, n, I, prec)) {
    /* The interpolation worked, write back the results */
    res = 1;
    mpfr_abs(Delta, D, GMP_RNDU);
    if (poly != NULL) {
      *poly = p;
    } else {
      sparsePolynomialFree(p);
    }
  }

  /* Clear and free the temporaries */
  __sollyaRecycleMpfrClear(D);
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiClear(XY[i].X);
    __sollyaRecycleMpfiClear(XY[i].Y);
  }
  safeFree(XY);
  
  /* Signal success or failure */
  return res;
}

static inline void __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoitEvaluate(sollya_mpfi_t Y, node *X) {
  mp_prec_t prec, p;

  if ((X->nodeType != MEMREF) || (X->cache->polynomialRepresentation == NULL)) {
    if (accessThruMemRef(X)->nodeType == CONSTANT) {
      prec = sollya_mpfi_get_prec(Y);
      p = mpfr_get_prec(*(accessThruMemRef(X)->value));
      if (p > prec) {
	sollya_mpfi_set_prec(Y, p);
      }
    }
  }
  evaluateConstantExpressionToSharpInterval(Y, X);
}

static inline int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoit(sparse_polynomial_t *poly,
												  mpfr_t Delta,
												  node **X, node **Y, unsigned int n,
												  sollya_mpfi_t I, mp_prec_t prec) {
  unsigned int i;
  sparse_polynomial_t p;
  sollya_mpfi_t *XX, *YY;
  mpfr_t D;

  /* Make compiler happy */
  p = NULL;

  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
    if (!isConstant(Y[i])) return 0;
  }
  if (sollya_mpfi_has_nan(I)) return 0;
  if (sollya_mpfi_has_infinity(I)) return 0;

  /* Allocate two vectors of MPFI variables XX and YY at precision prec */
  XX = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  YY = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(XX[i], prec);
    __sollyaRecycleMpfiInit2(YY[i], prec);
  }

  /* Evaluate the X[i] into the XX[i] and the Y[i] into the YY[i] */
  for (i=0u;i<=n-1u;i++) {
    __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoitEvaluate(XX[i], X[i]);
    __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoitEvaluate(YY[i], Y[i]);
  }

  /* Initialize an MPFR variable D */
  __sollyaRecycleMpfrInit2(D, mpfr_get_prec(Delta));

  /* Call a purely interval based function */
  if (__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsWork(&p, D, XX, YY, n, I, prec)) {
    /* The function worked 

       Copy abs(D) into Delta, copy the polynomial.

    */
    mpfr_abs(D, D, GMP_RNDN); /* exact */
    mpfr_set(Delta, D, GMP_RNDU);
    if (poly != NULL) {
      *poly = p;
    } else {
      sparsePolynomialFree(p);
    }

    /* Clear and free the temporaries */
    __sollyaRecycleMpfrClear(D);
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpfiClear(XX[i]);
      __sollyaRecycleMpfiClear(YY[i]);
    }
    safeFree(XX);
    safeFree(YY);

    /* Signal success */
    return 1;
  } 

  /* Here the interval based function did not work.

     Clear and free the temporaries.

  */
  __sollyaRecycleMpfrClear(D);
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiClear(XX[i]);
    __sollyaRecycleMpfiClear(YY[i]);
  }
  safeFree(XX);
  safeFree(YY);
  
  /* Signal failure */
  return 0;
}


static inline int __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervals(sparse_polynomial_t *poly,
											      node **X, node **Y, unsigned int n,
											      sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  sparse_polynomial_t p;
  mp_prec_t prec, pr;
  mpfr_t Delta;
  long int t;

  /* Make compiler happy */
  p = NULL;

  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
    if (!isConstant(Y[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (mpfr_zero_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;
  if (sollya_mpfi_has_infinity(I)) return 0;

  /* Initialize an MPFR variable */
  __sollyaRecycleMpfrInit2(Delta, mpfr_get_prec(delta));
  
  /* Set a certain start precision */
  prec = ((mp_prec_t) 24) + ((mp_prec_t) (2.0 * log2(((double) n) + 1.0)));

  /* Try an interval based technique */
  if (__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoit(&p, Delta, X, Y, n, I, prec)) {
    /* If Delta is NaN or infinity, we need to fail. */
    if (mpfr_nan_p(Delta) ||
	mpfr_inf_p(Delta)) {
      /* Free the polynomial p, clear Delta, signal failure */
      sparsePolynomialFree(p);
      __sollyaRecycleMpfrClear(Delta);
      return 0;
    }
    /* Here Delta is neither Nan nor infinity.

       If abs(Delta) <= abs(delta), we were lucky.

    */
    if (mpfr_cmp_abs(Delta, delta) <= 0) {
      /* Write back the polynomial */
      if (poly) {
	*poly = p;
      } else {
	sparsePolynomialFree(p);
      }

      /* Clear Delta */
      __sollyaRecycleMpfrClear(Delta);

      /* Signal success */
      return 1;
    }

    /* Here Delta is neither NaN nor Infinity but 

       abs(Delta) > abs(delta).

       We cannot use p, so free it.

    */
    sparsePolynomialFree(p);

    /* Compiler happiness */
    p = NULL;

    /* Try to deduce a new precision setting out 
       of Delta respective to delta. See the comments
       in the function

       __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinatesInterpolate

       for details.

    */
    mpfr_div(Delta, Delta, delta, GMP_RNDN);
    mpfr_abs(Delta, Delta, GMP_RNDN);
    if (mpfr_nan_p(Delta) ||
	mpfr_inf_p(Delta) ||
	mpfr_zero_p(Delta)) {
      /* We are unable to deduce a new precision to retry. 

	 Clear Delta, signal failure.

      */
      __sollyaRecycleMpfrClear(Delta);
      return 0;
    }

    /* Here Delta is the amount by which the next Delta needs to be
       less the next time.
    */
    mpfr_log2(Delta, Delta, GMP_RNDN);

    if (mpfr_nan_p(Delta) ||
	mpfr_inf_p(Delta)) {
      /* We are unable to deduce a new precision to retry. 

	 Clear Delta, signal failure.

      */
      __sollyaRecycleMpfrClear(Delta);
      return 0;
    }

    /* We need to add at least Delta bits to the working precision. 

       Start by making sure we add at least 1 bit.
       Then check that we do not add more than 2^15 - 1 bits.

    */
    if (mpfr_cmp_si(Delta, 1) <= 0) {
      mpfr_set_si(Delta, 1, GMP_RNDN);
    }
    if (mpfr_cmp_si(Delta, ((((1 << 14) - 1) << 1) + 1)) > 0) {
      mpfr_set_si(Delta, ((((1 << 14) - 1) << 1) + 1), GMP_RNDN);
    }
    t = mpfr_get_si(Delta, GMP_RNDN);
    if (t < 1) {
      t = (long int) 1;
    }
    if (t > (long int) ((((1 << 14) - 1) << 1) + 1)) {
      t = (long int) ((((1 << 14) - 1) << 1) + 1);
    }
    pr = prec + ((mp_prec_t) t) + ((mp_prec_t) 5);
    if (pr <= prec) {
      /* We could not come up with a new precision 

	 Clear Delta, signal failure.

     */
      __sollyaRecycleMpfrClear(Delta);
      return 0;
    }
    prec = pr;

    /* Retry the whole story with the new precision prec */
    if (__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervalsDoit(&p, Delta, X, Y, n, I, prec)) {
      /* If Delta is NaN or infinity, we need to fail. */
      if (mpfr_nan_p(Delta) ||
	  mpfr_inf_p(Delta)) {
	/* Free the polynomial p, clear Delta, signal failure */
	sparsePolynomialFree(p);
	__sollyaRecycleMpfrClear(Delta);
	return 0;
      }
      /* Here Delta is neither Nan nor infinity.
	 
	 If abs(Delta) <= abs(delta), we were lucky.

      */
      if (mpfr_cmp_abs(Delta, delta) <= 0) {
	/* Write back the polynomial */
	if (poly) {
	  *poly = p;
	} else {
	  sparsePolynomialFree(p);
	}
	
	/* Clear Delta */
	__sollyaRecycleMpfrClear(Delta);
	
	/* Signal success */
	return 1;
      }
      
      /* Here we were not lucky but this was already the 2nd time we tried. 

	 Free the polynomial p, clear Delta, signal failure 

      */
      sparsePolynomialFree(p);
      __sollyaRecycleMpfrClear(Delta);
      return 0;
    }
  }

  /* The interval based technique failed

     Clear the temporary MPFR variable.

  */
  __sollyaRecycleMpfrClear(Delta);
  
  /* Signal failure */
  return 0;
}

static inline int __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseIntervals(sparse_polynomial_t *poly,
												      node **X, node *f, unsigned int n,
												      sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  node **Y;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (mpfr_zero_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;
  if (sollya_mpfi_has_infinity(I)) return 0;

  /* Create a vector of ordinate expression nodes */
  Y = (node **) safeCalloc(n, sizeof(node *));
  for (i=0u;i<=n-1u;i++) {
    Y[i] = addMemRef(substitute(f, X[i]));
  }
  
  /* Try the abscissa/ordinate interpolation heuristic */
  res = __sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervals(&p, X, Y, n, I, delta);

  /* Free and deallocate the ordinate vector */
  for (i=0u;i<=n-1u;i++) {
    freeThing(Y[i]);
  }
  safeFree(Y);
  
  /* If the interpolation was successful write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      sparsePolynomialFree(p);
    }
  }

  /* Pass on the success/failure indication */
  return res;
}

static inline int sparsePolynomialInterpolateWithExpressionsPermitError(sparse_polynomial_t *poly, node **X, node **Y, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  int res;
  sparse_polynomial_t p, q;

  /* Make compiler happy */
  p = NULL;
  q = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
    if (!isConstant(Y[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;

  /* Handle the case when the bound delta is zero or the interval I
     contains infinity 
  */
  if (mpfr_zero_p(delta) || sollya_mpfi_has_infinity(I)) {
    return sparsePolynomialInterpolateWithExpressions(poly, X, Y, n);
  }

  /* Try a first approach based on Interval Arithmetic heuristics */
  if (__sparsePolynomialInterpolateWithExpressionsPermitErrorSpecialCaseIntervals(&q, X, Y, n, I, delta)) {
    /* The special code did the work. 

       Write back the result (if possible) and return success.

    */
    if (poly != NULL) {
      *poly = q;
    } else {
      sparsePolynomialFree(q);
    }
    return 1;
  }
  
  /* Handle the case when delta is non-zero and not NaN and I does
     not contain NaNs nor infinities.

     We compute an exact interpolation polynomial and round its
     coefficients in such a way that the absolute error due to this
     rounding, over I, stays bounded in magnitude by abs(delta).

  */
  res = sparsePolynomialInterpolateWithExpressions(&p, X, Y, n);

  /* If we got no result, we just return failure */
  if (!res) return 0;

  /* Here, p got computed and is valid 

     If poly is NULL, the caller is just interested whether the
     polynomial could be computed not in the polynomial itself.
     
     We just free the polynomial p and return success.

  */
  if (poly == NULL) {
    sparsePolynomialFree(p);
    return 1;
  }

  /* Here, poly is valid, p is valid 

     We round according to the interval and the error bound.

  */
  *poly = sparsePolynomialRoundWithErrorBound(p, I, delta);

  /* Free the intermediate polynomial p */
  sparsePolynomialFree(p);

  /* Signal success */
  return 1;
}

static inline void __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinatesEvaluate(mpfr_t y,
															      sollya_mpfi_t delta,
															      node *f,
															      constant_t x,
															      mp_prec_t prec,
															      sollya_mpfi_t scratch) {
  sollya_mpfi_set_prec(scratch, prec + 15);
  constantEvalMpfi(scratch, x);
  evaluateIntervalPlain(delta, f, scratch);
  sollya_mpfi_mid(y, delta);
  sollya_mpfi_sub_fr(delta, delta, y);
}

static inline int __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinatesInterpolate(constant_t *X,
																sollya_mpfi_t *Y,
																unsigned int n,
																sollya_mpfi_t I,
																mpfr_t delta,
																mp_prec_t prec,
																int *retry,
																sollya_mpfi_t *a,
																sollya_mpfi_t *t,
																sollya_mpfi_t s1,
																sollya_mpfi_t s2,
																int *precHintGiven,
																mp_prec_t *precHint) {
  unsigned int i, j;
  constant_t t1;
  mp_exp_t maxExpCurrent, maxExpSetpoint;
  mp_prec_t newPrec;

  /* Handle case n = 0 to make compiler happy */
  if (n == 0u) {
    *retry = 0;
    return 0;
  }
  
  /* Set precision for all interval variables */
  for (i=0;i<=n-1u;i++) {
    sollya_mpfi_set_prec(a[i], prec + 10);
    sollya_mpfi_set_prec(t[i], prec + 10);
  }
  sollya_mpfi_set_prec(s1, prec + 10);
  sollya_mpfi_set_prec(s2, prec + 10);

  /* Compute interval divided differences in a for

     * constant_t abscissas    and
     * interval   ordinates.

     Use t to hold one column of the divided differences
     matrix, put the top row in a.

     If one of the differences X[i] - X[j] ends
     up being zero, return failure without indicating 
     that a retry makes sense.

  */
  for (i=0;i<=n-1u;i++) {
    sollya_mpfi_set(t[i], Y[i]);
    if (i != 0u) {
      for (j=i-1u;(j+1u)>0u;j--) {
	t1 = constantSub(X[i], X[j]);
	if (constantIsZero(t1, 1)) {
	  constantFree(t1);
	  *retry = 0;
	  return 0;
	}
	constantEvalMpfi(s1, t1);
	constantFree(t1);
	sollya_mpfi_sub(s2, t[j+1u], t[j]);
	sollya_mpfi_div(t[j], s2, s1);
      }
    }
    sollya_mpfi_set(a[i], t[0]);
  }

  /* We must evaluate the polynomial

     p(x) = a[0] + (x-X[0]) * (a[1] + (x-X[1]) * (a[2] + ...))
     
     over the whole interval I thru Horner evaluation. This means
     we compute "the" interval:

     p(I) = a[0] + (I-X[0]) * (a[1] + (I-X[1]) * (a[2] + ...)).

     In this expression, everything is already an interval but the
     X[i]. We have to evaluate them to intervals first.

     The value of p(I) will be held in s1.

  */
  sollya_mpfi_set_si(s1, 0);
  for (i=n-1u;(i+1u)>0u;i--) {
    constantEvalMpfi(s2, X[i]);
    sollya_mpfi_sub(s2, I, s2);
    sollya_mpfi_mul(s2, s2, s1);
    sollya_mpfi_add(s1, a[i], s2);
  }

  /* Here, s1 contains an interval enclosing p(I).

     We start by checking for erroneous cases, like s1 containing an
     infinity or a NaN. In these cases, we bail out, setting "retry"
     to false and returning failure.

  */
  if (sollya_mpfi_has_infinity(s1) ||
      sollya_mpfi_has_nan(s1)) {
    *retry = 0;
    return 0;
  }

  /* Here s1 contains a finite enclosure of p(I), where p is
     a family of polynomials containing all polynomials interpolating
     at abscissas X[i] and ordinates taken out of Y[i]. 

     We check whether the interval s1 is contained in [-delta; delta].

     If it is, we return success. Otherwise, we return failure, indicating
     that a retry is worth getting done.

  */
  sollya_mpfi_interv_symmetrical_fr(s2, delta);
  if (sollya_mpfi_is_inside(s1, s2)) {
    return 1;
  }

  /* Here, we fail but it makes sense to retry. We may even try to
     compute a precision hint for the next try. 
  */
  *retry = 1;
  if ((!sollya_mpfi_has_nan(s1)) &&
      (!sollya_mpfi_has_infinity(s1)) &&
      (!sollya_mpfi_is_zero(s1)) &&
      (!mpfr_nan_p(delta)) &&
      (!mpfr_inf_p(delta)) &&
      (!mpfr_zero_p(delta))) {
    maxExpCurrent = sollya_mpfi_max_exp(s1);
    maxExpSetpoint = mpfr_get_exp(delta);
    if (maxExpCurrent > maxExpSetpoint) {
      if ((prec << 10) > ((mp_prec_t) 0)) {
	if (maxExpCurrent < (maxExpSetpoint + ((mp_exp_t) (prec << 10)))) {
	  newPrec = prec + ((mp_prec_t) (maxExpCurrent - maxExpSetpoint)) + ((mp_prec_t) 10);
	  if (newPrec > prec) {
	    *precHint = newPrec;
	    *precHintGiven = newPrec;
	  }
	}
      }   
    }
  }
  return 0;
}
																
static inline int __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinates(constant_t *Y,
														     constant_t *X,
														     node *f,
														     unsigned int n,
														     sollya_mpfi_t I,
														     mpfr_t delta) {
  unsigned int i;
  mpfr_t *YY;
  sollya_mpfi_t *YYDelta;
  int iter, okay, retry;
  mp_prec_t prec;
  sollya_mpfi_t s1, s2;
  sollya_mpfi_t *scratch1, *scratch2;
  int precHintGiven;
  mp_prec_t p, precHint;

  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (Y[i] != NULL) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;
  if (mpfr_zero_p(delta)) return 0;
  if (sollya_mpfi_has_infinity(I)) return 0;
  
  /* Set a starting precision */
  prec = 12;

  /* Initialize scratch variables */
  __sollyaRecycleMpfiInit2(s1, prec);
  __sollyaRecycleMpfiInit2(s2, prec);
  scratch1 = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  scratch2 = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiInit2(scratch1[i], prec);
    __sollyaRecycleMpfiInit2(scratch2[i], prec);
  }
  
  /* Initialize a vector of n MPFR numbers and n intervals and
     initialize them to prec bits of precision.
  */
  YY = (mpfr_t *) safeCalloc(n, sizeof(mpfr_t));
  YYDelta = (sollya_mpfi_t *) safeCalloc(n, sizeof(sollya_mpfi_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfrInit2(YY[i], prec);
    __sollyaRecycleMpfiInit2(YYDelta[i], prec);
  }

  /* Loop to determine an appropriate precision */
  iter = 0;
  okay = 0;
  do {
    /* Evaluate f(X[i]) as YY[i] + YYDelta[i] */
    for (i=0u;i<=n-1u;i++) {
      __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinatesEvaluate(YY[i],
														 YYDelta[i],
														 f,
														 X[i],
														 prec,
														 s1);
    }

    /* Check whether interpolation on abscissas X[i] and intervals YYDelta[i] over I depasses delta */
    retry = 1;
    precHintGiven = 0;
    precHint = (mp_prec_t) 0;
    if (__sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinatesInterpolate(X,
														      YYDelta,
														      n,
														      I,
														      delta,
														      prec,
														      &retry,
														      scratch1,
														      scratch2,
														      s1, s2,
														      &precHintGiven, &precHint)) {
      /* Success */
      okay = 1;
      break;
    }

    /* Here, we have a failure. 

       If retry has been set to zero, there is no hope we can find
       an appropriate precision. We just bail out.

    */
    if (!retry) break;

    /* Retry: 

       If we got a precision hint, we use the precision hint whenever
       possible. Otherwise, we just double the precision.

    */
    if (precHintGiven) {
      if (prec >= 16) {
	p = prec + (prec >> 2);
	if (p > precHint) {
	  prec = p;
	} else {
	  if (precHint < 1024) {
	    prec = precHint;
	  } else {
	    if ((precHint >> 10) > prec) {
	      prec <<= 10;
	    } else {
	      prec = precHint;
	    }
	  }
	}
      } else {
	prec <<= 1;
      }
    } else {
      prec <<= 1;
    }
    for (i=0u;i<=n-1u;i++) {
      mpfr_set_prec(YY[i], prec);
      sollya_mpfi_set_prec(YYDelta[i], prec);
    }
    iter++;
  } while (iter < SPARSE_POLYNOMIAL_INTERPOLATION_DOUBLE_PRECISION_ITERATIONS);

  /* If we have the impression we've found the right precision,
     we double-check not to have any NaNs or infinities.
  */
  if (okay) {
    for (i=0u;i<=n-1u;i++) {
      if (!(mpfr_number_p(YY[i]) || mpfr_zero_p(YY[i]))) {
	okay = 0;
	break;
      }
    }
  }
  
  /* If we could not find a correct precision, we just deallocate
     all intermediate variables and vectors and signal failure.
  */
  if (!okay) {
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpfrClear(YY[i]);
      __sollyaRecycleMpfiClear(YYDelta[i]);
    }
    safeFree(YY);
    safeFree(YYDelta);
    __sollyaRecycleMpfiClear(s1);
    __sollyaRecycleMpfiClear(s2);
    for (i=0u;i<=n-1u;i++) {
      __sollyaRecycleMpfiClear(scratch1[i]);
      __sollyaRecycleMpfiClear(scratch2[i]);
    }
    safeFree(scratch1);
    safeFree(scratch2);
    return 0;
  }

  /* Here, we have appropriate ordinates in the YY vector.

     We convert them to constants, deallocate the intermediates and
     return success.

  */
  for (i=0u;i<=n-1u;i++) {
    Y[i] = constantFromMpfr(YY[i]);
    __sollyaRecycleMpfrClear(YY[i]);
    __sollyaRecycleMpfiClear(YYDelta[i]);
  }
  safeFree(YY);
  safeFree(YYDelta);
  __sollyaRecycleMpfiClear(s1);
  __sollyaRecycleMpfiClear(s2);
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpfiClear(scratch1[i]);
    __sollyaRecycleMpfiClear(scratch2[i]);
  }
  safeFree(scratch1);
  safeFree(scratch2);
  return 1;
}

static inline int __sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRational(sparse_polynomial_t *poly,
												     node **X, node *f,
												     unsigned int n,
												     sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i, k;
  mpq_t *XX;
  constant_t *XXX, *YYY;
  mpfr_t deltaHalf;
  sparse_polynomial_t p;

  /* Make compiler happy */
  p = NULL;

  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;

  /* Handle input we can't do anything about in this special
     function.
  */
  if (mpfr_zero_p(delta) || sollya_mpfi_has_infinity(I)) return 0;

  /* Check whether all expressions X[i] can be simplified to rational
     numbers. Store these rational numbers in a vector XX of mpq_t.

     If one of the expressions cannot be evaluated to a rational
     number, deallocate the allocated rational numbers and return
     failure. The general function will handle this case at higher
     cost.

  */
  XX = (mpq_t *) safeCalloc(n, sizeof(mpq_t));
  for (i=0u;i<=n-1u;i++) {
    __sollyaRecycleMpqInit(XX[i]);
    if (!tryEvaluateConstantTermToMpq(XX[i], X[i])) {
      for (k=0u;k<=i;k++) {
	__sollyaRecycleMpqClear(XX[k]);
      }
      safeFree(XX);
      return 0;
    }
  }

  /* Here, we have a vector XX of rational numbers XX[i] corresponding
     exactly to the expressions X[i]. 

     We convert these rational numbers XX[i] into constants XXX[i].

  */
  XXX = (constant_t *) safeCalloc(n, sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    XXX[i] = constantFromMpq(XX[i]);
    __sollyaRecycleMpqClear(XX[i]);
  }
  safeFree(XX);

  /* Now we cut the maximum allowed error into two halves:

     * we keep one half for the final rounding of the coefficients
       of the interpolation polynomial,

     * we use the other half to approximate the function f 
       on the XXX in such a way that when evaluating f
       to dyadic numbers YYY in an approximate manner
       does not affect the interpolation polynomial
       with an absolute error greater than abs(delta/2), 
       when evaluating it over I.

  */
  __sollyaRecycleMpfrInit2(deltaHalf, mpfr_get_prec(delta));
  mpfr_div_2ui(deltaHalf, delta, 1u, GMP_RNDN); /* exact */

  /* Compute the YYY according to their specification above */
  YYY = (constant_t *) safeCalloc(n, sizeof(constant_t));
  for (i=0u;i<=n-1u;i++) {
    YYY[i] = NULL;
  }
  if (!__sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRationalComputeOrdinates(YYY, XXX, f, n, I, deltaHalf)) {
    /* Something went wrong. 

       Deallocate everything, signal failure and let the general
       function take over.

    */
    for (i=0u;i<=n-1u;i++) {
      constantFree(XXX[i]);
      constantFree(YYY[i]);
    }
    safeFree(XXX);
    safeFree(YYY);
    __sollyaRecycleMpfrClear(deltaHalf);
    return 0;
  }

  /* Here we have two vectors of rational constants XXX and YYY the
     interpolation polynomial of which is no farer away from the
     interpolation polynomial of f in X than abs(delta), in magnitude
     over I.

     We try to compute an interpolation polynomial for XXX/YYY.

  */
  if (!sparsePolynomialInterpolateWithConstants(&p, XXX, YYY, n)) {
    /* Something went wrong.

       Deallocate everything, signal failure and let the general
       function take over.

    */
    for (i=0u;i<=n-1u;i++) {
      constantFree(XXX[i]);
      constantFree(YYY[i]);
    }
    safeFree(XXX);
    safeFree(YYY);
    __sollyaRecycleMpfrClear(deltaHalf);
    return 0;
  }

  /* Here p is an interpolation polynomial for XXX/YYY. 

     Deallocate XXX and YYY, as they are no longer needed.

  */
  for (i=0u;i<=n-1u;i++) {
    constantFree(XXX[i]);
    constantFree(YYY[i]);
  }
  safeFree(XXX);
  safeFree(YYY);

  /* Here, p got computed and is valid 

     If poly is NULL, the caller is just interested whether the
     polynomial could be computed not in the polynomial itself.
     
     We just free the polynomial p and the intermediate variables and
     return success.

  */
  if (poly == NULL) {
    __sollyaRecycleMpfrClear(deltaHalf);
    sparsePolynomialFree(p);
    return 1;
  }

  /* Here, poly is valid, p is valid 

     We round according to the interval and the error bound deltaHalf.

  */
  *poly = sparsePolynomialRoundWithErrorBound(p, I, deltaHalf);

  /* Free the intermediate polynomial p */
  sparsePolynomialFree(p);

  /* Deallocate the intermediate variables */
  __sollyaRecycleMpfrClear(deltaHalf);

  /* Signal success */
  return 1;
}


static inline int sparsePolynomialInterpolateFunctionWithExpressionsPermitError(sparse_polynomial_t *poly, node **X, node *f, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  int res;
  sparse_polynomial_t p, q;

  /* Make compiler happy */
  p = NULL;
  q = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (X[i] == NULL) return 0;
    if (!isConstant(X[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;

  /* Handle the case when the bound delta is zero or the interval I
     contains infinity 
  */
  if (mpfr_zero_p(delta) || sollya_mpfi_has_infinity(I)) {
    return sparsePolynomialInterpolateFunctionWithExpressions(poly, X, f, n);
  }

  /* Try a first approach based on Interval Arithmetic heuristics */
  if (__sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseIntervals(&q, X, f, n, I, delta)) {
    /* The special code did the work. 

       Write back the result (if possible) and return success.

    */
    if (poly != NULL) {
      *poly = q;
    } else {
      sparsePolynomialFree(q);
    }
    return 1;
  }
  
  /* Try to handle the case when the values X are all rational numbers
     with some special code.
  */
  if (__sparsePolynomialInterpolateFunctionWithExpressionsPermitErrorSpecialCaseRational(&q, X, f, n, I, delta)) {
    /* The special code did the work. 

       Write back the result (if possible) and return success.

    */
    if (poly != NULL) {
      *poly = q;
    } else {
      sparsePolynomialFree(q);
    }
    return 1;
  }
  
  /* Handle the case when delta is non-zero and not NaN and I does
     not contain NaNs nor infinities.

     We compute an exact interpolation polynomial and round its
     coefficients in such a way that the absolute error due to this
     rounding, over I, stays bounded in magnitude by abs(delta).

  */
  res = sparsePolynomialInterpolateFunctionWithExpressions(&p, X, f, n);

  /* If we got no result, we just return failure */
  if (!res) return 0;

  /* Here, p got computed and is valid 

     If poly is NULL, the caller is just interested whether the
     polynomial could be computed not in the polynomial itself.
     
     We just free the polynomial p and return success.

  */
  if (poly == NULL) {
    sparsePolynomialFree(p);
    return 1;
  }

  /* Here, poly is valid, p is valid 

     We round according to the interval and the error bound.

  */
  *poly = sparsePolynomialRoundWithErrorBound(p, I, delta);

  /* Free the intermediate polynomial p */
  sparsePolynomialFree(p);

  /* Signal success */
  return 1;
}

static inline int sparsePolynomialInterpolateWithPointsPermitError(sparse_polynomial_t *poly, mpfr_t *X, mpfr_t *Y, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  node **XX, **YY;
  int res;
  sparse_polynomial_t p;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (Y == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (mpfr_inf_p(X[i])) return 0;
    if (mpfr_nan_p(X[i])) return 0;
    if (mpfr_inf_p(Y[i])) return 0;
    if (mpfr_inf_p(Y[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;

  /* Allocate and fill two vectors of expressions XX and YY
     corresponding to X and Y.
  */
  XX = (node **) safeCalloc(n,sizeof(node *));
  YY = (node **) safeCalloc(n,sizeof(node *));
  for (i=0u;i<=n-1u;i++) {
    XX[i] = addMemRef(makeConstant(X[i]));
    YY[i] = addMemRef(makeConstant(Y[i]));
  }

  /* Call the interpolation routine on vectors XX/YY */
  res = sparsePolynomialInterpolateWithExpressionsPermitError(&p, XX, YY, n, I, delta);

  /* Deallocate the temporary vectors */
  for (i=0u;i<=n-1u;i++) {
    freeThing(XX[i]);
    freeThing(YY[i]);
  }
  safeFree(XX);
  safeFree(YY);
  
  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

static inline int sparsePolynomialInterpolateFunctionWithPointsPermitError(sparse_polynomial_t *poly, mpfr_t *X, node *f, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  unsigned int i;
  node **XX;
  sparse_polynomial_t p;
  int res;

  /* Make compiler happy */
  p = NULL;
  
  /* Handle stupid input */
  if (X == NULL) return 0;
  if (f == NULL) return 0;
  if (n == 0u) return 0;
  for (i=0u;i<=n-1u;i++) {
    if (mpfr_inf_p(X[i])) return 0;
    if (mpfr_nan_p(X[i])) return 0;
  }
  if (mpfr_nan_p(delta)) return 0;
  if (sollya_mpfi_has_nan(I)) return 0;
  
  /* Allocate a vector XX of size n to hold expressions XX[i]
     corresponding to the numbers X[i]

     Form the expressions.

  */
  XX = (node **) safeCalloc(n,sizeof(node *));
  for (i=0u;i<=n-1u;i++) {
    XX[i] = addMemRef(makeConstant(X[i]));
  }

  /* Call the interpolation routine on vector XX and function f */
  res = sparsePolynomialInterpolateFunctionWithExpressionsPermitError(&p, XX, f, n, I, delta);

  /* Deallocate the temporary vector */
  for (i=0u;i<=n-1u;i++) {
    freeThing(XX[i]);
  }
  safeFree(XX);
  
  /* If the interpolation was successful, write back the result */
  if (res) {
    if (poly != NULL) {
      *poly = p;
    } else {
      if (p != NULL) {
	sparsePolynomialFree(p);
      }
    }
  }

  /* Return the success or failure indication */
  return res;
}

/* End of part for sparse polynomials */

/* Start of part for general (composed) polynomials */

static inline void __polynomialSparsify(polynomial_t);
int polynomialUsesExpressionConstant(polynomial_t p);

static inline polynomial_t __polynomialAllocate() {
  return (polynomial_t) safeMalloc(sizeof(struct __polynomial_struct_t));
}

static inline void __polynomialFreeMem(polynomial_t p) {
  safeFree(p);
}

void polynomialInitializeCaches() {
  sparsePolynomialInitializeCaches();
}

void polynomialFreeCaches() {
  sparsePolynomialFreeCaches();
}

static inline polynomial_t __polynomialBuildFromSparse(sparse_polynomial_t p) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle general case */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->type = SPARSE;
  res->outputType = ANY_FORM;
  res->value.sparse = p;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;

  /* Return the newly built polynomial */
  return res;
}

static inline sparse_polynomial_t __polynomialSparsifyComposition(polynomial_t p,
								  sparse_polynomial_t q) {
  sparse_polynomial_t g, h, r;

  /* Handle stupid input */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* Consider the structure of p */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialCompose(p->value.sparse, q);
    break;
  case ADDITION:
    g = __polynomialSparsifyComposition(p->value.pair.g, q);
    h = __polynomialSparsifyComposition(p->value.pair.h, q);
    r = sparsePolynomialAdd(g, h);
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return r;
    break;
  case SUBTRACTION:
    g = __polynomialSparsifyComposition(p->value.pair.g, q);
    h = __polynomialSparsifyComposition(p->value.pair.h, q);
    r = sparsePolynomialSub(g, h);
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return r;
    break;
  case MULTIPLICATION:
    g = __polynomialSparsifyComposition(p->value.pair.g, q);
    h = __polynomialSparsifyComposition(p->value.pair.h, q);
    r = sparsePolynomialMul(g, h);
    sparsePolynomialFree(g);
    sparsePolynomialFree(h);
    return r;
    break;
  case COMPOSITION:
    h = __polynomialSparsifyComposition(p->value.pair.h,
					q);
    r = __polynomialSparsifyComposition(p->value.pair.g, h);
    sparsePolynomialFree(h);
    return r;
    break;
  case NEGATE:
    g = __polynomialSparsifyComposition(p->value.g, q);
    r = sparsePolynomialNeg(g);
    sparsePolynomialFree(g);
    return r;
    break;
  case POWER:
    g = __polynomialSparsifyComposition(p->value.powering.g, q);
    if (!sparsePolynomialPowConstant(&r, g, p->value.powering.c)) {
      sollyaFprintf(stderr,"Error: __polynomialSparsifyComposition: could not compute power of sparse polynomial\n");
      exit(1);
    }
    sparsePolynomialFree(g);
    return r;
    break;
  default:
    break;
  }
  __polynomialSparsify(p);
  return sparsePolynomialCompose(p->value.sparse, q);
}

static inline void __polynomialSparsify(polynomial_t p) {
  sparse_polynomial_t sp;

  /* Handle stupid input */
  if (p == NULL) return;

  /* General case */
  switch (p->type) {
  case SPARSE:
    /* Nothing to do */
    return;
    break;
  case ADDITION:
    __polynomialSparsify(p->value.pair.g);
    __polynomialSparsify(p->value.pair.h);
    sp = sparsePolynomialAdd(p->value.pair.g->value.sparse,
			     p->value.pair.h->value.sparse);
    polynomialFree(p->value.pair.g);
    polynomialFree(p->value.pair.h);
    p->value.sparse = sp;
    break;
  case SUBTRACTION:
    __polynomialSparsify(p->value.pair.g);
    __polynomialSparsify(p->value.pair.h);
    sp = sparsePolynomialSub(p->value.pair.g->value.sparse,
			     p->value.pair.h->value.sparse);
    polynomialFree(p->value.pair.g);
    polynomialFree(p->value.pair.h);
    p->value.sparse = sp;
    break;
  case MULTIPLICATION:
    __polynomialSparsify(p->value.pair.g);
    __polynomialSparsify(p->value.pair.h);
    sp = sparsePolynomialMul(p->value.pair.g->value.sparse,
			     p->value.pair.h->value.sparse);
    polynomialFree(p->value.pair.g);
    polynomialFree(p->value.pair.h);
    p->value.sparse = sp;
    break;
  case COMPOSITION:
    __polynomialSparsify(p->value.pair.h);
    sp = __polynomialSparsifyComposition(p->value.pair.g,
					 p->value.pair.h->value.sparse);
    polynomialFree(p->value.pair.g);
    polynomialFree(p->value.pair.h);
    p->value.sparse = sp;
    break;
  case NEGATE:
    __polynomialSparsify(p->value.g);
    sp = sparsePolynomialNeg(p->value.g->value.sparse);
    polynomialFree(p->value.g);
    p->value.sparse = sp;
    break;
  case POWER:
    __polynomialSparsify(p->value.powering.g);
    if (!sparsePolynomialPowConstant(&sp,
				     p->value.powering.g->value.sparse,
				     p->value.powering.c)) {
      sollyaFprintf(stderr,"Error: __polynomialSparsify: could not compute power of sparse polynomial\n");
      exit(1);
    }
    polynomialFree(p->value.powering.g);
    constantFree(p->value.powering.c);
    p->value.sparse = sp;
    break;
  }
  if (p->usesExpressionConstant.cached) {
    if (p->usesExpressionConstant.res) {
      p->usesExpressionConstant.cached = 0;
    }
  }
  p->type = SPARSE;
}

static inline polynomial_t __polynomialExecuteCompositionCompose(polynomial_t p, polynomial_t q) {
  polynomial_t res, t;

  /* Make compiler happy */
  res = NULL;
  /* End of compiler happiness */

  /* Handle stupid input */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* General case */
  switch (p->type) {
  case SPARSE:
    __polynomialSparsify(q);
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->outputType = ANY_FORM;
    res->value.sparse = sparsePolynomialCompose(p->value.sparse,
						q->value.sparse);
    res->type = SPARSE;
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->outputType = ANY_FORM;
    res->type = p->type;
    res->value.pair.g = __polynomialExecuteCompositionCompose(p->value.pair.g, q);
    res->value.pair.h = __polynomialExecuteCompositionCompose(p->value.pair.h, q);
    break;
  case COMPOSITION:
    t = __polynomialExecuteCompositionCompose(p->value.pair.h, q);
    res = __polynomialExecuteCompositionCompose(p->value.pair.g, t);
    polynomialFree(t);
    break;
  case NEGATE:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->outputType = ANY_FORM;
    res->type = NEGATE;
    res->value.g = __polynomialExecuteCompositionCompose(p->value.g, q);
    break;
  case POWER:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->outputType = ANY_FORM;
    res->type = POWER;
    res->value.powering.g = __polynomialExecuteCompositionCompose(p->value.powering.g, q);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  }

  return res;
}

static inline void __polynomialExecuteComposition(polynomial_t p) {
  sparse_polynomial_t sp;
  constant_t c;
  polynomial_type_t t;
  polynomial_t g, h;

  /* Handle stupid input */
  if (p == NULL) return;

  /* General case */
  switch (p->type) {
  case SPARSE:
    /* Nothing to do */
    return;
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    __polynomialExecuteComposition(p->value.pair.g);
    __polynomialExecuteComposition(p->value.pair.h);
    break;
  case COMPOSITION:
    __polynomialExecuteComposition(p->value.pair.g);
    __polynomialExecuteComposition(p->value.pair.h);
    t = p->value.pair.g->type;
    switch (t) {
    case SPARSE:
      __polynomialSparsify(p->value.pair.h);
      sp = sparsePolynomialCompose(p->value.pair.g->value.sparse,
				   p->value.pair.h->value.sparse);
      polynomialFree(p->value.pair.g);
      polynomialFree(p->value.pair.h);
      p->value.sparse = sp;
      p->type = SPARSE;
      return;
      break;
    case ADDITION:
    case SUBTRACTION:
    case MULTIPLICATION:
      g = __polynomialExecuteCompositionCompose(p->value.pair.g->value.pair.g,
						p->value.pair.h);
      h = __polynomialExecuteCompositionCompose(p->value.pair.g->value.pair.h,
						p->value.pair.h);
      polynomialFree(p->value.pair.g);
      polynomialFree(p->value.pair.h);
      p->value.pair.g = g;
      p->value.pair.h = h;
      p->type = t;
      return;
      break;
    case COMPOSITION:
      /* Should never happen */
      __polynomialSparsify(p);
      return;
      break;
    case NEGATE:
      g = __polynomialExecuteCompositionCompose(p->value.pair.g->value.g,
						p->value.pair.h);
      polynomialFree(p->value.pair.g);
      polynomialFree(p->value.pair.h);
      p->value.g = g;
      p->type = NEGATE;
      return;
      break;
    case POWER:
      g = __polynomialExecuteCompositionCompose(p->value.pair.g->value.powering.g,
						p->value.pair.h);
      c = constantFromCopy(p->value.pair.g->value.powering.c);
      polynomialFree(p->value.pair.g);
      polynomialFree(p->value.pair.h);
      p->value.powering.g = g;
      p->value.powering.c = c;
      p->type = POWER;
      return;
      break;
    }
    break;
  case NEGATE:
    __polynomialExecuteComposition(p->value.g);
    break;
  case POWER:
    __polynomialExecuteComposition(p->value.powering.g);
    break;
  }
}

polynomial_t polynomialFromMpfrConstant(mpfr_t c) {
  return __polynomialBuildFromSparse(sparsePolynomialFromMpfrConstant(c));
}

polynomial_t polynomialFromMpzConstant(mpz_t c) {
  return __polynomialBuildFromSparse(sparsePolynomialFromMpzConstant(c));
}

polynomial_t polynomialFromMpqConstant(mpq_t c) {
  return __polynomialBuildFromSparse(sparsePolynomialFromMpqConstant(c));
}

polynomial_t polynomialFromIntConstant(int c) {
  return __polynomialBuildFromSparse(sparsePolynomialFromIntConstant(c));
}

polynomial_t polynomialFromIdentity() {
  return __polynomialBuildFromSparse(sparsePolynomialFromIdentity());
}

polynomial_t polynomialFromMpfrCoefficients(mpfr_t *coeffs, unsigned int deg) {
  return __polynomialBuildFromSparse(sparsePolynomialFromMpfrCoefficients(coeffs, deg));
}

static inline polynomial_t __polynomialFromConstant(constant_t c) {
  return __polynomialBuildFromSparse(sparsePolynomialFromConstant(c));
}

int polynomialFromConstantExpressionCoefficients(polynomial_t *r, node **coeffs, unsigned int deg) {
  sparse_polynomial_t sp;

  if (!sparsePolynomialFromConstantExpressionCoefficients(&sp, coeffs, deg)) return 0;
  *r = __polynomialBuildFromSparse(sp);
  return 1;
}

polynomial_t polynomialFromCopy(polynomial_t p) {
  if (p == NULL) return NULL;
  p->refCount++;
  return p;
}

void polynomialFree(polynomial_t p) {
  if (p == NULL) return;
  p->refCount--;
  if (p->refCount > 0u) return;
  switch (p->type) {
  case SPARSE:
    sparsePolynomialFree(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    polynomialFree(p->value.pair.g);
    polynomialFree(p->value.pair.h);
    break;
  case NEGATE:
    polynomialFree(p->value.g);
    break;
  case POWER:
    polynomialFree(p->value.powering.g);
    constantFree(p->value.powering.c);
    break;
  }
  __polynomialFreeMem(p);
}

static inline int __polynomialGetDegreeAsIntCheap(polynomial_t p) {
  int gd, hd, res;
  unsigned int u, gdu, tu, ttu;

  /* Handle stupid input */
  if (p == NULL) return -1;

  /* Try to handle the case without sparsifying the polynomial. */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialGetDegreeAsInt(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
    gd = __polynomialGetDegreeAsIntCheap(p->value.pair.g);
    hd = __polynomialGetDegreeAsIntCheap(p->value.pair.h);
    if ((gd == 0) && (hd == 0)) return 0;
    if ((gd >= 0) && (hd >= 0) && (gd != hd))
      return (hd > gd ? hd : gd);
    break;
  case MULTIPLICATION:
    gd = __polynomialGetDegreeAsIntCheap(p->value.pair.g);
    hd = __polynomialGetDegreeAsIntCheap(p->value.pair.h);
    if ((gd != 0) && (hd != 0)) {
      if ((gd < 0) || (hd < 0)) return -1;
      if (tryExactIntAddition(&res, gd, hd)) {
	return res;
      } else {
	return -1;
      }
    }
    break;
  case COMPOSITION:
    gd = __polynomialGetDegreeAsIntCheap(p->value.pair.g);
    hd = __polynomialGetDegreeAsIntCheap(p->value.pair.h);
    if ((gd == 0) || (hd == 0)) return 0;
    if ((gd < 0) || (hd < 0)) return -1;
    if (tryExactIntMultiplication(&res, gd, hd)) {
      return res;
    } else {
      return -1;
    }
    break;
  case NEGATE:
    return __polynomialGetDegreeAsIntCheap(p->value.g);
    break;
  case POWER:
    gd = __polynomialGetDegreeAsIntCheap(p->value.powering.g);
    if (gd == 0) return 0;
    if (tryConstantToUnsignedInt(&u, p->value.powering.c)) {
      if (u == 0u) {
	return 0;
      } else {
	if (gd < 0) return -1;
	gdu = gd;
	if (tryExactUnsignedIntMultiplication(&tu,gdu,u)) {
	  res = tu;
	  if (res < 0) return -1;
	  ttu = res;
	  if (ttu != tu) return -1;
	  return res;
	} else {
	  return -1;
	}
      }
    } else {
      return -1;
    }
    break;
  }

  /* The fallback would be too expensive */
  return -1;
}

static inline int __polynomialEqualCheap(polynomial_t p, polynomial_t q) {
  if (p == NULL) return 0;
  if (q == NULL) return 0;
  if (p == q) return 1;
  if (p->type != q->type) return 0;
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialEqual(p->value.sparse, q->value.sparse, 0);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    return (__polynomialEqualCheap(p->value.pair.g, q->value.pair.g) &&
	    __polynomialEqualCheap(p->value.pair.h, q->value.pair.h));
    break;
  case NEGATE:
    return __polynomialEqualCheap(p->value.g, q->value.g);
    break;
  case POWER:
    return (__polynomialEqualCheap(p->value.powering.g, q->value.powering.g) &&
	    constantIsEqual(p->value.powering.c, q->value.powering.c, 0));
    break;
  }
  return 0;
}

static inline int __polynomialStructurallyEqualCheap(polynomial_t p, polynomial_t q) {
  if (p == NULL) return 0;
  if (q == NULL) return 0;
  if (p == q) return 1;
  if (p->type != q->type) return 0;
  if (p->outputType != q->outputType) return 0;
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialEqual(p->value.sparse, q->value.sparse, 0);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    return (__polynomialStructurallyEqualCheap(p->value.pair.g, q->value.pair.g) &&
	    __polynomialStructurallyEqualCheap(p->value.pair.h, q->value.pair.h));
    break;
  case NEGATE:
    return __polynomialStructurallyEqualCheap(p->value.g, q->value.g);
    break;
  case POWER:
    return (__polynomialStructurallyEqualCheap(p->value.powering.g, q->value.powering.g) &&
	    constantIsEqual(p->value.powering.c, q->value.powering.c, 0));
    break;
  }
  return 0;
}

static inline int __polynomialIsConstantCheap(polynomial_t p) {
  if (p == NULL) return 0;
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialIsConstant(p->value.sparse, 0);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    return (__polynomialIsConstantCheap(p->value.pair.g) &&
	    __polynomialIsConstantCheap(p->value.pair.h));
    break;
  case COMPOSITION:
    return (__polynomialIsConstantCheap(p->value.pair.g) ||
	    __polynomialIsConstantCheap(p->value.pair.h));
    break;
  case NEGATE:
    return __polynomialIsConstantCheap(p->value.g);
    break;
  case POWER:
    return (__polynomialIsConstantCheap(p->value.powering.g) ||
	    constantIsZero(p->value.powering.c, 0));
    break;
  }
  return 0;
}

static inline void __polynomialUnifyEqual(polynomial_t p, polynomial_t q) {
  if (p == NULL) return;
  if (q == NULL) return;
  if (p == q) return;
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    if (p->value.sparse == q->value.sparse) return;
    if (sparsePolynomialGetReferenceCount(p->value.sparse) >
	sparsePolynomialGetReferenceCount(q->value.sparse)) {
      sparsePolynomialFree(q->value.sparse);
      q->value.sparse = sparsePolynomialFromCopy(p->value.sparse);
    } else {
      sparsePolynomialFree(p->value.sparse);
      p->value.sparse = sparsePolynomialFromCopy(q->value.sparse);
    }
    return;
  }
  if ((p->type == SPARSE) && (q->type != SPARSE)) {
    switch (q->type) {
    case ADDITION:
    case SUBTRACTION:
    case MULTIPLICATION:
    case COMPOSITION:
      polynomialFree(q->value.pair.g);
      polynomialFree(q->value.pair.h);
      break;
    case NEGATE:
      polynomialFree(q->value.g);
      break;
    case POWER:
      polynomialFree(q->value.powering.g);
      constantFree(q->value.powering.c);
      break;
    default:
      return;
    }
    q->type = SPARSE;
    q->value.sparse = sparsePolynomialFromCopy(p->value.sparse);
    return;
  }
  if ((q->type == SPARSE) && (p->type != SPARSE)) {
    switch (p->type) {
    case ADDITION:
    case SUBTRACTION:
    case MULTIPLICATION:
    case COMPOSITION:
      polynomialFree(p->value.pair.g);
      polynomialFree(p->value.pair.h);
      break;
    case NEGATE:
      polynomialFree(p->value.g);
      break;
    case POWER:
      polynomialFree(p->value.powering.g);
      constantFree(p->value.powering.c);
      break;
    default:
      return;
    }
    p->type = SPARSE;
    p->value.sparse = sparsePolynomialFromCopy(q->value.sparse);
    return;
  }
}

int polynomialEqual(polynomial_t p, polynomial_t q, int defVal) {
  int dp, dq, res;

  /* Handle stupid inputs */
  if (p == NULL) return defVal;
  if (q == NULL) return defVal;

  /* Pointer equality */
  if (p == q) return 1;

  /* If both polynomials are in sparse form, just use this */
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    res = sparsePolynomialEqual(p->value.sparse, q->value.sparse, 51);
    if (res == 51) return defVal;
    if (!res) return 0;
    __polynomialUnifyEqual(p,q);
    return 1;
  }

  /* Try to cheaply compute the degrees. If both degrees can be
     computed but are different, we know that the polynomials are
     different.
  */
  dp = __polynomialGetDegreeAsIntCheap(p);
  dq = __polynomialGetDegreeAsIntCheap(q);
  if ((dp >= 0) && (dq >= 0) && (dp != dq)) return 0;

  /* If p and q are written the same way and each sub-polynomial is
     the same, they are the same.
  */
  if (__polynomialEqualCheap(p, q)) {
    __polynomialUnifyEqual(p, q);
    return 1;
  }

  /* General case

     Can perhaps be optimized still a little bit.

  */
  __polynomialSparsify(p);
  __polynomialSparsify(q);
  res = sparsePolynomialEqual(p->value.sparse, q->value.sparse, 51);
  if (res == 51) return defVal;
  if (!res) return 0;
  __polynomialUnifyEqual(p, q);
  return 1;
}

unsigned int polynomialGetNumberOfNonZeroCoefficients(polynomial_t p) {

  /* Handle stupid inputs */
  if (p == NULL) return 0u;

  /* General case */
  __polynomialSparsify(p);
  return sparsePolynomialGetNumberOfNonZeroCoefficients(p->value.sparse);
}

int polynomialStructurallyEqual(polynomial_t p, polynomial_t q, int defVal) {
  constant_t c0, cd;
  mpz_t deg;
  unsigned int nzc;

  /* Handle stupid inputs */
  if (p == NULL) return defVal;
  if (q == NULL) return defVal;

  /* Pointer equality */
  if (p == q) return 1;

  /* Use hashes if we have them already computed */
  if (p->hash.hasHash && q->hash.hasHash) {
    /* Both polynomials have a hash.

       If the hashes are different, they are structurally different.

    */
    if (p->hash.hash != q->hash.hash) return 0;
  }

  /* Do a first check using a cheap function */
  if (__polynomialStructurallyEqualCheap(p, q)) return 1;

  /* If the polynomials are not mathematically equal, they cannot be
     structurally equal.
  */
  if (!polynomialEqual(p, q, 1)) return 0;

  /* Try with the other default value */
  if (!polynomialEqual(p, q, 0)) {
    __polynomialSparsify(p);
    __polynomialSparsify(q);
  }

  /* Retry */
  if (!polynomialEqual(p, q, 1)) return 0;

  /* Here the polynomials are mathematically equal or their
     mathematical equality cannot be decided.

     Start by assuring us that we know that they are mathematically
     equal.

  */
  if (polynomialEqual(p, q, 0)) {
    /* Here, the polynomials are mathematically equal.

       If they have the same output form and that output form
       is canonical or hornerized, they are structurally equal.

       If they have the same output form and that output form is
       ANY_FORM, we sparsify both polynomials and can then answer that
       they are structurally equal.

    */
    if (p->outputType == q->outputType) {
      if (p->outputType == ANY_FORM) {
	__polynomialSparsify(p);
	__polynomialSparsify(q);
      }
      return 1;
    }

    /* Here, the polynomials have different output forms.

       Start by checking if one of them is constant. As the other one
       is mathematically equal, it is constant, too.

       If the polynomials are constant, they are structurally the
       same.

    */
    if (polynomialIsConstant(p, 0)) return 1;

    /* Now check if the polynomials (testing one is sufficient as they
       are mathematically equal) have a non-zero constant coefficient,
       a leading coefficient equal to 1 and only two non-zero coefficients.

       In this particular case the hornerized form c_0 + x^d is
       structurally the same as the canonical form c_0 + x^d.
    */
    c0 = __polynomialGetIthCoefficientAsConstantIntIndex(p, 0);
    if (!constantIsZero(c0, 1)) {
      __sollyaRecycleMpzInit(deg);
      polynomialGetDegree(deg, p);
      cd = __polynomialGetIthCoefficientAsConstant(p, deg);
      __sollyaRecycleMpzClear(deg);
      if (constantIsOne(cd, 0)) {
	nzc = polynomialGetNumberOfNonZeroCoefficients(p);
	if (nzc == 2u) {
	  constantFree(cd);
	  constantFree(c0);
	  return 1;
	}
      }
      constantFree(cd);
    }
    constantFree(c0);

    /* Here, the polynomials have different output forms and are not
       constant.

       If none of these output forms is ANY_FORM, they are
       structurally different.
    */
    if ((p->outputType != ANY_FORM) &&
	(q->outputType != ANY_FORM)) {
      return 0;
    }

    /* Here at least one of the output forms is ANY_FORM and
       the other output form is not ANY_FORM.

       We sparsify both polynomials. Then they are structurally equal
       iff none of them is in CANONICAL_FORM as the output for
       sparsified ANY_FORMS is HORNER_FORM.

    */
    __polynomialSparsify(p);
    __polynomialSparsify(q);
    if ((p->outputType != CANONICAL_FORM) &&
	(q->outputType != CANONICAL_FORM)) {
      return 1;
    }

    /* Here the polynomials are structurally different */
    return 0;
  }

  /* Here, we don't know if the polynomials are
     mathematically the same and we already retried.

     We return the default answer.

  */
  return defVal;
}

int polynomialIsIdentity(polynomial_t p, int defVal) {
  int dp;

  /* Handle stupid inputs */
  if (p == NULL) return defVal;

  /* If the polynomial is sparse, just use the test function on sparse
     polynomials
  */
  if (p->type == SPARSE)
    return sparsePolynomialIsIdentity(p->value.sparse, defVal);

  /* If the polynomial is constant, it is not the identity function */
  if (__polynomialIsConstantCheap(p)) return 0;

  /* If the degree of the polynomial can be computed easily and it is
     not 1, the polynomial can't be the identity function.
  */
  dp = __polynomialGetDegreeAsIntCheap(p);
  if ((dp >= 0) && (dp != 1)) return 0;

  /* Some optimisations are still possible */

  /* Sparsify the polynomial and use the test function on sparse
     polynomials
  */
  __polynomialSparsify(p);
  return sparsePolynomialIsIdentity(p->value.sparse, defVal);
}

int polynomialIsConstant(polynomial_t p, int defVal) {
  int deg;
  constant_t c;

  /* Handle stupid inputs */
  if (p == NULL) return defVal;

  /* If the polynomial is sparse, just use the test function on sparse
     polynomials.
  */
  if (p->type == SPARSE)
    return sparsePolynomialIsConstant(p->value.sparse, defVal);

  /* Try a cheap function to determine if the polynomial is
     constant
  */
  if (__polynomialIsConstantCheap(p)) return 1;

  /* Here, we still do not know if the polynomial is constant.

     We compute its degree.

     If the degree is larger than the largest machine integer, the
     function will return a negative value instead of the degree.

     In this case, we sparsify the polynomial and call the test
     function on the sparsified polynomial.

     Otherwise,

     * if the degree is zero, we are sure the polynomial is
     constant,

     * otherwise, we get the coefficient corresponding to the
     degree and check if it is non-zero. If we cannot show it
     is non-zero, we return the default answer. Otherwise, we
     are sure that the polynomial is not constant.

  */
  deg = polynomialGetDegreeAsInt(p);
  if (deg < 0) {
    __polynomialSparsify(p);
    return sparsePolynomialIsConstant(p->value.sparse, defVal);
  }

  /* Here the degree of the polynomial holds on a machine integer.

     If it is zero, the polynomial is constant.
  */
  if (deg == 0) return 1;

  /* Get the coefficient of the degree of the polynomial */
  c = __polynomialGetIthCoefficientAsConstantIntIndex(p, deg);

  /* Check if coefficient of degree is zero. */
  if (constantIsZero(c, 1)) {
    /* Here the coefficient of the polynomial corresponding
       to its degree is said to be zero. This means it cannot
       be shown to be non-zero.

       We return the default value, as we do not know if the
       polynomial is constant or not.

    */
    constantFree(c);
    return defVal;
  }

  /* Free the constant corresponding to coefficient */
  constantFree(c);

  /* Here the coefficient of degree deg != 0 is shown to be
     non-zero. Hence, the polynomial is not constant.
  */
  return 0;
}

void polynomialDiv(polynomial_t *quot, polynomial_t *rest, polynomial_t a, polynomial_t b) {
  sparse_polynomial_t sq, sr;
  polynomial_t q, r, qg, qh, rg, rh, t, rcp;
  constant_t one, tc, c, recprC;

  /* Handle stupid inputs */
  if ((a == NULL) || (b == NULL)) {
    *quot = NULL;
    *rest = NULL;
    return;
  }

  /* If a and b are both sparse, just use the sparse division */
  if ((a->type == SPARSE) && (b->type == SPARSE)) {
    sparsePolynomialDiv(&sq, &sr, a->value.sparse, b->value.sparse);

    *quot = __polynomialBuildFromSparse(sq);
    *rest = __polynomialBuildFromSparse(sr);
    return;
  }

  /* If b is constant, do some special handling */
  if (polynomialGetDegreeAsInt(b) == 0) {
    /* Get the constant corresponding to b */
    c = __polynomialGetIthCoefficientAsConstantIntIndex(b, 0);
    /* If we are sure that b = c is non-zero, we are sure we can
       compute 1/c and replace the division by a multiplication
    */
    if (!constantIsZero(c, 1)) {
      /* Compute 1/c and represent it as a polynomial */
      one = constantFromInt(1);
      recprC = constantDiv(one, c);
      constantFree(one);
      rcp = __polynomialFromConstant(recprC);
      constantFree(recprC);
      *quot = polynomialMul(rcp, a);
      *rest = polynomialFromIntConstant(0);
      polynomialFree(rcp);
    } else {
      /* Here b = c = 0. We take q = 0 and r = a */
      *quot = polynomialFromIntConstant(0);
      *rest = polynomialFromCopy(a);
    }
    constantFree(c);
    return;
  }

  /* If a and b are equal, the answer is easy */
  if (__polynomialEqualCheap(a, b)) {
    *quot = polynomialFromIntConstant(1);
    *rest = polynomialFromIntConstant(0);
    return;
  }

  /* If a is a negation, do the division recursively and negate the
     quotient and rest.
  */
  if (a->type == NEGATE) {
    polynomialDiv(&q, &r, a->value.g, b);
    *quot = polynomialNeg(q);
    *rest = polynomialNeg(r);
    polynomialFree(q);
    polynomialFree(r);
    return;
  }

  /* If a is an addition, just do the division
     recursively.
  */
  if (a->type == ADDITION) {
    polynomialDiv(&qg, &rg, a->value.pair.g, b);
    polynomialDiv(&qh, &rh, a->value.pair.h, b);
    *quot = polynomialAdd(qg, qh);
    *rest = polynomialAdd(rg, rh);
    polynomialFree(qg);
    polynomialFree(rg);
    return;
  }

  /* If a is a subtraction, just do the division
     recursively.
  */
  if (a->type == SUBTRACTION) {
    polynomialDiv(&qg, &rg, a->value.pair.g, b);
    polynomialDiv(&qh, &rh, a->value.pair.h, b);
    *quot = polynomialSub(qg, qh);
    *rest = polynomialSub(rg, rh);
    polynomialFree(qg);
    polynomialFree(rg);
    return;
  }

  /* If a is of the form a = p^c with c >= 2 and b = p,
     the result is q = p^(c - 1) and r = 0.
  */
  if (a->type == POWER) {
    if ((!constantIsZero(a->value.powering.c,1)) &&
	(!constantIsOne(a->value.powering.c,1))) {
      if (polynomialEqual(a->value.powering.g, b, 0)) {
	one = constantFromInt(1);
	tc = constantSub(a->value.powering.c, one);
	constantFree(one);
	t = __polynomialFromConstant(tc);
	constantFree(tc);
	if (polynomialPow(quot, b, t)) {
	  *rest = polynomialFromIntConstant(0);
	  polynomialFree(t);
	  return;
	}
	polynomialFree(t);
      }
    }
  }

  /* General case

     Some cases can still be optimized.

  */
  __polynomialSparsify(a);
  __polynomialSparsify(b);
  sparsePolynomialDiv(&sq, &sr, a->value.sparse, b->value.sparse);

  *quot = __polynomialBuildFromSparse(sq);
  *rest = __polynomialBuildFromSparse(sr);
}

void polynomialDivExtended(polynomial_t *quot, polynomial_t *rest, polynomial_t a, polynomial_t b) {
  constant_t ca, cb, cq, cr;

  /* Handle stupid inputs */
  if ((a == NULL) || (b == NULL)) {
    *quot = NULL;
    *rest = NULL;
    return;
  }

  /* If both a and b are constants, let

     quot = floor(a/b)
     rest = a - quot * b

  */
  if ((polynomialGetDegreeAsInt(a) == 0) &&
      (polynomialGetDegreeAsInt(b) == 0)) {
    ca = __polynomialGetIthCoefficientAsConstantIntIndex(a, 0);
    cb = __polynomialGetIthCoefficientAsConstantIntIndex(b, 0);
    constantDivMod(&cq, &cr, ca, cb);
    *quot = __polynomialFromConstant(cq);
    *rest = __polynomialFromConstant(cr);
    constantFree(cr);
    constantFree(cq);
    constantFree(cb);
    constantFree(ca);
    return;
  }

  /* Here, at least one of a and b is a non-constant
     polynomial. Return the classical polynomial division result.
  */
  polynomialDiv(quot, rest, a, b);
}

polynomial_t polynomialHornerize(polynomial_t p) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If p is already hornerized, return a plain copy. */
  if (p->outputType == HORNER_FORM) {
    return polynomialFromCopy(p);
  }

  /* Here we have work to do. We perform a "deep" copy and set the
     output form to "hornerized".
  */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = p->type;
  res->outputType = HORNER_FORM;
  switch (p->type) {
  case SPARSE:
    res->value.sparse = sparsePolynomialFromCopy(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    res->value.pair.g = polynomialFromCopy(p->value.pair.g);
    res->value.pair.h = polynomialFromCopy(p->value.pair.h);
    break;
  case NEGATE:
    res->value.g = polynomialFromCopy(p->value.g);
    break;
  case POWER:
    res->value.powering.g = polynomialFromCopy(p->value.powering.g);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  }
  return res;
}

polynomial_t polynomialCanonicalize(polynomial_t p) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If p is already canonical, return a plain copy. */
  if (p->outputType == CANONICAL_FORM) {
    return polynomialFromCopy(p);
  }

  /* Here we have work to do. We perform a "deep" copy and set the
     output form to "canonical".
  */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = p->type;
  res->outputType = CANONICAL_FORM;
  switch (p->type) {
  case SPARSE:
    res->value.sparse = sparsePolynomialFromCopy(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    res->value.pair.g = polynomialFromCopy(p->value.pair.g);
    res->value.pair.h = polynomialFromCopy(p->value.pair.h);
    break;
  case NEGATE:
    res->value.g = polynomialFromCopy(p->value.g);
    break;
  case POWER:
    res->value.powering.g = polynomialFromCopy(p->value.powering.g);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  }
  return res;
}

void polynomialGetDegree(mpz_t deg, polynomial_t p) {
  mpz_t gd, hd;
  polynomial_t zero;

  /* Handle stupid input */
  if (p == NULL) {
    mpz_set_si(deg,-1);
    return;
  }

  /* Try to handle the case without sparsifying the polynomial. */
  switch (p->type) {
  case SPARSE:
    sparsePolynomialGetDegree(deg, p->value.sparse);
    return;
    break;
  case ADDITION:
  case SUBTRACTION:
    __sollyaRecycleMpzInit(gd);
    __sollyaRecycleMpzInit(hd);
    polynomialGetDegree(gd, p->value.pair.g);
    polynomialGetDegree(hd, p->value.pair.h);
    if ((mpz_sgn(gd) < 0) ||
	(mpz_sgn(hd) < 0)) {
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      mpz_set_si(deg,-1);
      return;
    }
    if (mpz_cmp(gd, hd) != 0) {
      if (mpz_cmp(gd, hd) > 0) {
	mpz_set(deg, gd);
      } else {
	mpz_set(deg, hd);
      }
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      return;
    }
    __sollyaRecycleMpzClear(gd);
    __sollyaRecycleMpzClear(hd);
    break;
  case MULTIPLICATION:
    __sollyaRecycleMpzInit(gd);
    __sollyaRecycleMpzInit(hd);
    polynomialGetDegree(gd, p->value.pair.g);
    polynomialGetDegree(hd, p->value.pair.h);
    if ((mpz_sgn(gd) < 0) ||
	(mpz_sgn(hd) < 0)) {
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      mpz_set_si(deg,-1);
      return;
    }
    if ((mpz_cmp_si(gd, 0) == 0) &&
	(mpz_sgn(hd) > 0)) {
      zero = polynomialFromIntConstant(0);
      if (!polynomialEqual(p->value.pair.g, zero, 1)) {
	mpz_set(deg, hd);
	__sollyaRecycleMpzClear(gd);
	__sollyaRecycleMpzClear(hd);
	polynomialFree(zero);
	return;
      }
      polynomialFree(zero);
    }
    if ((mpz_cmp_si(hd, 0) == 0) &&
	(mpz_sgn(gd) > 0)) {
      zero = polynomialFromIntConstant(0);
      if (!polynomialEqual(p->value.pair.h, zero, 1)) {
	mpz_set(deg, gd);
	__sollyaRecycleMpzClear(gd);
	__sollyaRecycleMpzClear(hd);
	polynomialFree(zero);
	return;
      }
      polynomialFree(zero);
    }
    if ((mpz_sgn(gd) > 0) &&
	(mpz_sgn(hd) > 0)) {
      mpz_add(deg, gd, hd);
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      return;
    }
    __sollyaRecycleMpzClear(gd);
    __sollyaRecycleMpzClear(hd);
    break;
  case COMPOSITION:
    __sollyaRecycleMpzInit(gd);
    __sollyaRecycleMpzInit(hd);
    polynomialGetDegree(gd, p->value.pair.g);
    polynomialGetDegree(hd, p->value.pair.h);
    if ((mpz_sgn(gd) < 0) ||
	(mpz_sgn(hd) < 0)) {
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      mpz_set_si(deg,-1);
      return;
    }
    mpz_mul(deg, hd, gd);
    __sollyaRecycleMpzClear(gd);
    __sollyaRecycleMpzClear(hd);
    return;
    break;
  case NEGATE:
    polynomialGetDegree(deg,p->value.g);
    return;
    break;
  case POWER:
    __sollyaRecycleMpzInit(gd);
    polynomialGetDegree(gd, p->value.powering.g);
    if (mpz_sgn(gd) < 0) {
      __sollyaRecycleMpzClear(gd);
      mpz_set_si(deg,-1);
      return;
    }
    if (mpz_sgn(gd) == 0) {
      __sollyaRecycleMpzClear(gd);
      mpz_set_si(deg,0);
      return;
    }
    __sollyaRecycleMpzInit(hd);
    if (!tryConstantToMpz(hd, p->value.powering.c)) {
      __sollyaRecycleMpzClear(gd);
      __sollyaRecycleMpzClear(hd);
      mpz_set_si(deg,-1);
      return;
    }
    if (mpz_sgn(hd) < 0) {
      __sollyaRecycleMpzClear(hd);
      __sollyaRecycleMpzClear(gd);
      mpz_set_si(deg,-1);
      return;
    }
    mpz_mul(deg, gd, hd);
    __sollyaRecycleMpzClear(gd);
    __sollyaRecycleMpzClear(hd);
    return;
    break;
  }

  /* Fall-back case */
  __polynomialSparsify(p);
  sparsePolynomialGetDegree(deg, p->value.sparse);
}

int polynomialGetDegreeAsInt(polynomial_t p) {
  int gd, hd, res;
  mpz_t degz;
  signed long t, tt;
  unsigned int u, gdu, tu, ttu;

  /* Handle stupid input */
  if (p == NULL) return -1;

  /* Try to handle the case without sparsifying the polynomial. */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialGetDegreeAsInt(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
    gd = polynomialGetDegreeAsInt(p->value.pair.g);
    hd = polynomialGetDegreeAsInt(p->value.pair.h);
    if ((gd == 0) && (hd == 0)) return 0;
    if ((gd >= 0) && (hd >= 0) && (gd != hd))
      return (hd > gd ? hd : gd);
    break;
  case MULTIPLICATION:
    gd = polynomialGetDegreeAsInt(p->value.pair.g);
    hd = polynomialGetDegreeAsInt(p->value.pair.h);
    if ((gd != 0) && (hd != 0)) {
      if ((gd < 0) || (hd < 0)) return -1;
      if (tryExactIntAddition(&res, gd, hd)) {
	return res;
      } else {
	return -1;
      }
    }
    break;
  case COMPOSITION:
    gd = polynomialGetDegreeAsInt(p->value.pair.g);
    hd = polynomialGetDegreeAsInt(p->value.pair.h);
    if ((gd == 0) || (hd == 0)) return 0;
    if ((gd < 0) || (hd < 0)) return -1;
    if (tryExactIntMultiplication(&res, gd, hd)) {
      return res;
    } else {
      return -1;
    }
    break;
  case NEGATE:
    return polynomialGetDegreeAsInt(p->value.g);
    break;
  case POWER:
    gd = polynomialGetDegreeAsInt(p->value.powering.g);
    if (gd == 0) return 0;
    if (tryConstantToUnsignedInt(&u, p->value.powering.c)) {
      if (u == 0u) {
	return 0;
      } else {
	if (gd < 0) return -1;
	gdu = gd;
	if (tryExactUnsignedIntMultiplication(&tu,gdu,u)) {
	  res = tu;
	  if (res < 0) return -1;
	  ttu = res;
	  if (ttu != tu) return -1;
	  return res;
	} else {
	  return -1;
	}
      }
    } else {
      return -1;
    }
    break;
  }

  /* Fall-back case */
  __sollyaRecycleMpzInit(degz);
  polynomialGetDegree(degz, p);
  if (!mpz_fits_slong_p(degz)) {
    res = -1;
  } else {
    t = mpz_get_si(degz);
    res = t;
    tt = res;
    if (t != tt) {
      res = -1;
    }
  }
  __sollyaRecycleMpzClear(degz);
  return res;
}

/* Try to compute the i-th coefficient c of p^k, fail if this
   computation is not easy to perform.
*/
static inline int __polynomialGetIthCoefficientAsConstantIntIndexPowerCheap(constant_t *c, polynomial_t p, constant_t k, int i) {
  constant_t ic, mk, t, j, a, b, m, n, r, bin, aPowj, bPowr, prod;

  /* Make compiler happy */
  bin = NULL;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (k == NULL) return 0;
  if (i < 0) return 0;

  /* Sparsify the polynomial p */
  __polynomialSparsify(p);

  /* Try to decompose p into p = x^m * (a + b * x^n) */
  if (!sparsePolynomialDecomposeTwoMonomials(&a, &m, &b, &n, p->value.sparse))
    return 0;

  /* Here we have to compute the coefficient of degree i of

     x^(m * k) * (a + b * x^n)^k

     This means we have to get the coefficient of degree

     t = i - m * k

     of the polynomial

     (a + b * x^n)^k.

     If t = i - m * k is less than 0, we can simply return 0.

  */
  ic = constantFromInt(i);
  mk = constantMul(m, k);
  t = constantSub(ic, mk);

  /* Check if t is less than 0 */
  if (!(constantIsPositive(t, 1) || constantIsZero(t, 1))) {
    /* Here t = i - m * k is less than 0.

       Assign 0 to c and return success.

    */
    *c = constantFromInt(0);
    constantFree(ic);
    constantFree(mk);
    constantFree(t);
    constantFree(a);
    constantFree(m);
    constantFree(b);
    constantFree(n);
    return 1;
  }

  /* Here t = i - m * k is greater than or equal to 0

     We have to compute the coefficient of degree t of the polynomial

     (a + b * x^n)^k.

     Since

     (a + b * x^n)^k = sum_j=0^k bin(k,j) * a^j * b^(k - j) * x^(n * j)

     the coefficient of degree t is

     * equal to bin(k,j) * a^j * b^(k - j) if t is divisible by n and j = t / n
     * equal to 0                          if t is not divisible by n.

     We start by computing j = t / n and then test if j is a
     non-negative integer (hence if t is divisible by n).

  */
  j = constantDiv(t, n);

  /* Check if j is integer */
  if (constantIsNonNegativeInteger(j, 0)) {
    /* The value j = t / n is a non-negative integer. Hence t is divisible by n.

       We have to return c = bin(k,j) * a^j * b^(k - j).

       We first start by computing r = k - j.

       We then check if r is positive or zero.
       In this case we compute and return

       c = bin(k,j) * a^j * b^(k - j).

       Otherwise we return c = 0.

    */
    r = constantSub(k, j);
    if (constantIsZero(r, 0) || constantIsPositive(r,0)) {
      /* Here r >= 0 and we have to return

	 c = bin(k,j) * a^j * b^r.

	 If we can compute the binomial coefficient bin(k,j)
	 we use it, otherwise we simply signal failure.
      */
      if (constantFromBinomial(&bin, k, j)) {
	/* We have

	   bin = bin(k, j).

	   Compute

	   c = bin * a^j * b^r.

	*/
	aPowj = constantPow(a, j);
	bPowr = constantPow(b, r);
	prod = constantMul(aPowj, bPowr);
	*c = constantMul(bin, prod);
	constantFree(bin);
	constantFree(aPowj);
	constantFree(bPowr);
	constantFree(prod);
      } else {
	/* Signal failure. */
	constantFree(ic);
	constantFree(mk);
	constantFree(t);
	constantFree(a);
	constantFree(m);
	constantFree(b);
	constantFree(n);
	constantFree(j);
	constantFree(r);
	return 0;
      }
    } else {
      /* Here, r < 0 and we have to return c = 0. */
      *c = constantFromInt(0);
    }
    constantFree(r);
  } else {
    /* The value j = t / n is no non-negative integer. Hence t is not
       divisible by n and the coefficient of degree t of (a + b * x^n)^k is zero.
    */
    *c = constantFromInt(0);
  }

  /* Free the intermediate values */
  constantFree(ic);
  constantFree(mk);
  constantFree(t);
  constantFree(a);
  constantFree(m);
  constantFree(b);
  constantFree(n);
  constantFree(j);

  /* Return success */
  return 1;
}

static inline constant_t __polynomialGetIthCoefficientAsConstantIntIndex(polynomial_t p, int i) {
  int deg;
  constant_t a, b, res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle case when i < 0 */
  if (i < 0) {
    return constantFromInt(0);
  }

  /* Handle case when i > degree */
  deg = polynomialGetDegreeAsInt(p);
  if ((deg >= 0) &&
      (i > deg)) {
    return constantFromInt(0);
  }

  /* General case */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialGetIthCoefficientAsConstantIntIndex(p->value.sparse, i);
    break;
  case ADDITION:
  case SUBTRACTION:
    a = __polynomialGetIthCoefficientAsConstantIntIndex(p->value.pair.g,i);
    b = __polynomialGetIthCoefficientAsConstantIntIndex(p->value.pair.h,i);
    if (p->type == ADDITION) {
      res = constantAdd(a, b);
    } else {
      res = constantSub(a, b);
    }
    constantFree(a);
    constantFree(b);
    return res;
    break;
  case NEGATE:
    a = __polynomialGetIthCoefficientAsConstantIntIndex(p->value.g,i);
    res = constantNeg(a);
    constantFree(a);
    return res;
    break;
  case POWER:
    if (__polynomialGetIthCoefficientAsConstantIntIndexPowerCheap(&res,
								  p->value.powering.g,
								  p->value.powering.c,
								  i)) {
      return res;
    }
    break;
  default:
    break;
  }

  /* Fall-back case */
  __polynomialSparsify(p);
  return sparsePolynomialGetIthCoefficientAsConstantIntIndex(p->value.sparse, i);
}

static inline constant_t __polynomialGetIthCoefficientAsConstant(polynomial_t p, mpz_t i) {
  mpz_t deg;
  signed long int t, ttt;
  int tt;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle case when i < 0 */
  if (mpz_sgn(i) < 0) {
    return constantFromInt(0);
  }

  /* Handle case when i > degree */
  __sollyaRecycleMpzInit(deg);
  polynomialGetDegree(deg, p);
  if ((mpz_sgn(deg) >= 0) &&
      (mpz_cmp(i,deg) > 0)) {
    __sollyaRecycleMpzClear(deg);
    return constantFromInt(0);
  }

  /* Check if index i holds on a machine int */
  if (mpz_fits_slong_p(i)) {
    t = mpz_get_si(i);
    tt = (int) t;
    ttt = (signed long int) tt;
    if (ttt == t) {
      /* Here i holds on a signed long int and is equal t, which is
	 equal to tt.
      */
      return __polynomialGetIthCoefficientAsConstantIntIndex(p, tt);
    }
  }

  /* General case */
  __polynomialSparsify(p);
  return sparsePolynomialGetIthCoefficientAsConstant(p->value.sparse, i);
}

node *polynomialGetIthCoefficient(polynomial_t p, mpz_t i) {
  constant_t c;
  node *res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Get coefficient as a constant */
  c = __polynomialGetIthCoefficientAsConstant(p, i);

  /* Convert to an expression */
  res = addMemRef(constantToExpression(c));
  constantFree(c);

  /* Return the result */
  return res;
}

node *polynomialGetIthCoefficientIntIndex(polynomial_t p, int i) {
  constant_t c;
  node *res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Get coefficient as a constant */
  c = __polynomialGetIthCoefficientAsConstantIntIndex(p, i);

  /* Convert to an expression */
  res = addMemRef(constantToExpression(c));
  constantFree(c);

  /* Return the result */
  return res;
}

int polynomialGetCoefficients(node ***coeffs, unsigned int *deg, polynomial_t p) {
  /* Handle stupid input */
  if (p == NULL) return 0;

  /* General case */
  __polynomialSparsify(p);
  return sparsePolynomialGetCoefficients(coeffs, deg, p->value.sparse);
}

static inline node *__polynomialGetExpressionAnyForm(polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Handle composition */
  if (p->type == COMPOSITION) {
    __polynomialExecuteComposition(p);
    return __polynomialGetExpressionAnyForm(p);
  }

  /* General case */
  switch (p->type) {
  case SPARSE:
    return addMemRef(sparsePolynomialGetExpression(p->value.sparse, 0)); /* This must be Horner form */
    break;
  case ADDITION:
    return addMemRef(makeAdd(__polynomialGetExpressionAnyForm(p->value.pair.g),
			     __polynomialGetExpressionAnyForm(p->value.pair.h)));
    break;
  case SUBTRACTION:
    return addMemRef(makeSub(__polynomialGetExpressionAnyForm(p->value.pair.g),
			     __polynomialGetExpressionAnyForm(p->value.pair.h)));
    break;
  case MULTIPLICATION:
    return addMemRef(makeMul(__polynomialGetExpressionAnyForm(p->value.pair.g),
			     __polynomialGetExpressionAnyForm(p->value.pair.h)));
    break;
  case NEGATE:
    return addMemRef(makeNeg(__polynomialGetExpressionAnyForm(p->value.g)));
    break;
  case POWER:
    return addMemRef(makePow(__polynomialGetExpressionAnyForm(p->value.powering.g),
			     constantToExpression(p->value.powering.c)));
    break;
  default:
    break;
  }

  /* Cannot be reached */
  return NULL;
}

node *polynomialGetExpressionExplicit(polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If we can output the polynomial in any form, we do so. */
  if ((p->outputType == ANY_FORM) ||
      __polynomialIsConstantCheap(p)) {
    return addMemRef(__polynomialGetExpressionAnyForm(p));
  }

  /* Here, we have to output a "hornerized" or "canonicalized"
     form.
  */
  __polynomialSparsify(p);
  return addMemRef(sparsePolynomialGetExpression(p->value.sparse,
						 (p->outputType == CANONICAL_FORM)));
}

node *polynomialGetExpression(polynomial_t p) {
  node *res;

  /* Handle stupid input */
  if (p == NULL) return polynomialGetExpressionExplicit(p);

  /* Try to build a lazy memrefed expression */
  res = addMemRefEvenOnNull(NULL);
  if (res != NULL) {
    if (res->nodeType == MEMREF) {
      res->cache->polynomialRepresentation = polynomialFromCopy(p);
      return res;
    } else {
      freeThing(res);
    }
  }

  /* Could not create a lazy memrefed expression */
  return polynomialGetExpressionExplicit(p);
}

void polynomialFPrintf(FILE *fd, polynomial_t p) {
  node *t;

  /* Handle stupid cases */
  if (p == NULL) {
    sollyaFprintf(fd, "(null)");
  }

  /* Handle the general case.

     Can be optimized.

  */
  t = polynomialGetExpressionExplicit(p);
  sollyaFprintf(fd, "%b", t);
  freeThing(t);
}

char *polynomialToString(polynomial_t p) {
  node *t;
  char *str;
  char staticStr[8];
  int size, r;

  /* Handle stupid cases */
  if (p == NULL) return NULL;

  /* Handle the general case.

     Can be optimized.

  */
  t = polynomialGetExpressionExplicit(p);
  size = sollya_snprintf(staticStr,8,"%b",t);
  if (size < 0) {
    freeThing(t);
    return NULL;
  }
  str = (char *) safeCalloc(size + 2, sizeof(char));
  r = sollya_snprintf(str,size,"%b",t);
  if (r < 0) {
    freeThing(t);
    safeFree(str);
    return NULL;
  }

  /* Return the string */
  return str;
}

void polynomialEvalMpfr(mpfr_t y, polynomial_t p, mpfr_t x) {
  mpfr_t scratch, Y;

  /* Handle stupid inputs */
  if (p == NULL) {
    mpfr_set_nan(y);
    return;
  }

  /* If the polynomial is in sparse form, use the evaluation routine
     for sparse forms.
  */
  if (p->type == SPARSE) {
    sparsePolynomialEvalMpfr(y, p->value.sparse, x);
    return;
  }

  /* If the polynomial is in negation form, call ourselves recursively
     and negate y.
  */
  if (p->type == NEGATE) {
    polynomialEvalMpfr(y, p->value.g, x);
    mpfr_neg(y, y, GMP_RNDN);
    return;
  }

  /* Check if we can use y as a scratch space for recursion */
  if (x == y) {
    /* We can't. Call ourselves recursively. */
    __sollyaRecycleMpfrInit2(Y, mpfr_get_prec(y));
    polynomialEvalMpfr(Y, p, x);
    mpfr_set(y, Y, GMP_RNDN);
    __sollyaRecycleMpfrClear(Y);
    return;
  }

  /* Here, we have an addition, subtraction, multiplication,
     composition or power form to handle and we may use y as a scratch
     space.

     We need another scratch variable.

  */
  __sollyaRecycleMpfrInit2(scratch, mpfr_get_prec(y));
  /* Handle composition apart */
  if (p->type == COMPOSITION) {
    polynomialEvalMpfr(scratch, p->value.pair.h, x);
    polynomialEvalMpfr(y, p->value.pair.g, scratch);
  } else {
    /* Everything else but composition */
    switch (p->type) {
    case ADDITION:
    case SUBTRACTION:
    case MULTIPLICATION:
      polynomialEvalMpfr(y, p->value.pair.g, x);
      polynomialEvalMpfr(scratch, p->value.pair.h, x);
      break;
    case POWER:
      polynomialEvalMpfr(y, p->value.powering.g, x);
      constantEvalMpfr(scratch, p->value.powering.c);
      break;
    default:
      break;
    }
    switch (p->type) {
    case ADDITION:
      mpfr_add(y, y, scratch, GMP_RNDN);
      break;
    case SUBTRACTION:
      mpfr_sub(y, y, scratch, GMP_RNDN);
      break;
    case MULTIPLICATION:
      mpfr_mul(y, y, scratch, GMP_RNDN);
      break;
    case POWER:
      mpfr_pow(y, y, scratch, GMP_RNDN);
      break;
    default:
      break;
    }
  }
  __sollyaRecycleMpfrClear(scratch);
}

static inline void __polynomialEvalMpfiImpl(sollya_mpfi_t y, polynomial_t p, sollya_mpfi_t x) {
  mp_prec_t prec;
  sollya_mpfi_t scr, Y;
  sollya_mpfi_t *scratch;
  sollya_mpfi_t *reusedVars;
  int usingReused;

  /* Handle stupid inputs */
  if (p == NULL) {
    sollya_mpfi_set_nan(y);
    return;
  }

  /* If the polynomial is in sparse form, use the evaluation routine
     for sparse forms.
  */
  if (p->type == SPARSE) {
    sparsePolynomialEvalMpfi(y, p->value.sparse, x);
    return;
  }
  
  /* If the polynomial is in negation form, call ourselves recursively
     and negate y.
  */
  if (p->type == NEGATE) {
    __polynomialEvalMpfiImpl(y, p->value.g, x);
    sollya_mpfi_neg(y, y);
    return;
  }

  /* Check if we can use y as a scratch space for recursion */
  if (x == y) {
    /* We can't. Call ourselves recursively. */
    __sollyaRecycleMpfiInit2(Y, sollya_mpfi_get_prec(y));
    __polynomialEvalMpfiImpl(Y, p, x);
    sollya_mpfi_set(y, Y);
    __sollyaRecycleMpfiClear(Y);
    return;
  }

  /* Here, we have an addition, subtraction, multiplication,
     composition or power form to handle and we may use y as a scratch
     space.

     We need another scratch variable. We can try to get it by two
     means.

  */
  prec = sollya_mpfi_get_prec(y);
  sollya_mpfi_set_prec(y, prec + 4);
  reusedVars = getReusedGlobalMPFIVars(1, prec + 4);
  if (reusedVars == NULL) {
    usingReused = 0;
    __sollyaRecycleMpfiInit2(scr, prec + 4);
    scratch = &scr;
  } else {
    usingReused = 1;
    scratch = reusedVars;
  }

  /* Handle composition apart */
  if (p->type == COMPOSITION) {
    __polynomialEvalMpfiImpl(*scratch, p->value.pair.h, x);
    __polynomialEvalMpfiImpl(y, p->value.pair.g, *scratch);
  } else {
    /* Everything else but composition */
    switch (p->type) {
    case ADDITION:
    case SUBTRACTION:
    case MULTIPLICATION:
      __polynomialEvalMpfiImpl(y, p->value.pair.g, x);
      __polynomialEvalMpfiImpl(*scratch, p->value.pair.h, x);
      break;
    case POWER:
      __polynomialEvalMpfiImpl(y, p->value.powering.g, x);
      constantEvalMpfi(*scratch, p->value.powering.c);
      break;
    default:
      break;
    }
    switch (p->type) {
    case ADDITION:
      sollya_mpfi_add(y, y, *scratch);
      break;
    case SUBTRACTION:
      sollya_mpfi_sub(y, y, *scratch);
      break;
    case MULTIPLICATION:
      sollya_mpfi_mul(y, y, *scratch);
      break;
    case POWER:
      sollya_mpfi_pow(y, y, *scratch);
      break;
    default:
      break;
    }
  }

  if (usingReused) {
    returnReusedGlobalMPIVars(1);
  } else {
    __sollyaRecycleMpfiClear(scr);
  }
  sollya_mpfi_prec_round(y, prec);
}

void polynomialEvalMpfi(sollya_mpfi_t y, polynomial_t p, sollya_mpfi_t x) {
  /* This is just a wrapper for debugging purposes */
  __polynomialEvalMpfiImpl(y, p, x);
}

static inline int __polynomialCheapCheckConstantInteger(polynomial_t p, int k) {
  int deg;
  sollya_mpfi_t X, Y;
  mpfr_t temp;
  constant_t c, d;
  int res;

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* General case

     If the polynomial is sparse, we can use the appropriate function
     for sparse polynomials.

  */
  if (p->type == SPARSE) {
    return sparsePolynomialIsConstantInteger(p->value.sparse, k, 0);
  }

  /* The polynomial is not sparse.

     Try to determine its degree.

     If we can't determine the degree, we just fail.

     If we can and it is zero, we do the following:

     (i)  We evaluate the polynomial at [1;1] with interval
     arithmetic. If k is not in the image interval,
     the polynomial can't be equal to that constant k.
     (ii) We get the coefficient of degree zero and
     compare it to k.
  */
  deg = __polynomialGetDegreeAsIntCheap(p);

  /* Fail if we couldn't get the degree. Any fall-back would seem to
     be too expensive.
  */
  if (deg < 0) return 0;

  /* If the degree is not zero, the polynomial can't be constant */
  if (deg != 0) return 0;

  /* Here the degree is zero.

     Start by evaluating the polynomial at [1;1].

  */
  __sollyaRecycleMpfiInit2(X, 12);
  __sollyaRecycleMpfiInit2(Y, 8 * sizeof(int) + 10);
  sollya_mpfi_set_si(X, 1);
  polynomialEvalMpfi(Y, p, X);
  if (!sollya_mpfi_has_nan(Y)) {
    __sollyaRecycleMpfrInit2(temp, 8 * sizeof(int) + 10);
    mpfr_set_si(temp, k, GMP_RNDN); /* exact, enough precision */
    if (!sollya_mpfi_fr_in_interval(temp, Y)) {
      /* k is not in the non-NaN image interval */
      __sollyaRecycleMpfrClear(temp);
      __sollyaRecycleMpfiClear(Y);
      __sollyaRecycleMpfiClear(X);
      return 0;
    }
    __sollyaRecycleMpfrClear(temp);
  }
  __sollyaRecycleMpfiClear(Y);
  __sollyaRecycleMpfiClear(X);

  /* Get the coefficient of degree zero and compare it to k */
  c = __polynomialGetIthCoefficientAsConstantIntIndex(p, 0);
  d = constantFromInt(k);
  res = constantIsEqual(c, d, 0);
  constantFree(d);
  constantFree(c);

  /* Return result */
  return res;
}

static inline int __polynomialCheapCheckConstantZero(polynomial_t p) {

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* General case */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialIsConstantZero(p->value.sparse, 0);
    break;
  case ADDITION:
  case SUBTRACTION:
    return __polynomialCheapCheckConstantInteger(p, 0);
    break;
  case MULTIPLICATION:
    return (__polynomialCheapCheckConstantZero(p->value.pair.g) ||
	    __polynomialCheapCheckConstantZero(p->value.pair.h));
    break;
  case COMPOSITION:
    return __polynomialCheapCheckConstantInteger(p, 0);
    break;
  case NEGATE:
    return __polynomialCheapCheckConstantZero(p->value.g);
    break;
  case POWER:
    return __polynomialCheapCheckConstantZero(p->value.powering.g);
    break;
  }
  return 0;
}

static inline int __polynomialCheapCheckConstantOne(polynomial_t p) {

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* General case */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialIsConstantOne(p->value.sparse, 0);
    break;
  case ADDITION:
  case SUBTRACTION:
    return __polynomialCheapCheckConstantInteger(p, 1);
    break;
  case MULTIPLICATION:
    if (__polynomialCheapCheckConstantOne(p->value.pair.g) &&
	__polynomialCheapCheckConstantOne(p->value.pair.h))
      return 1;
    return __polynomialCheapCheckConstantInteger(p, 1);
    break;
  case COMPOSITION:
    return __polynomialCheapCheckConstantInteger(p, 1);
    break;
  case NEGATE:
    return __polynomialCheapCheckConstantInteger(p, 1);
    break;
  case POWER:
    return __polynomialCheapCheckConstantOne(p->value.powering.g);
    break;
  }
  return 0;
}

polynomial_t polynomialAdd(polynomial_t p, polynomial_t q) {
  polynomial_t res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* If an easy check shows that p or q are constant zero, return a
     copy of the other
  */
  if (__polynomialCheapCheckConstantZero(p))
    return polynomialFromCopy(q);
  if (__polynomialCheapCheckConstantZero(q))
    return polynomialFromCopy(p);

  /* If both polynomials are sparse, perform the operation on sparse
     polynomials.
  */
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    return __polynomialBuildFromSparse(sparsePolynomialAdd(p->value.sparse,
							   q->value.sparse));
  }

  /* General case: construct the addition polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = ADDITION;
  res->outputType = ANY_FORM;
  res->value.pair.g = polynomialFromCopy(p);
  res->value.pair.h = polynomialFromCopy(q);

  /* Return the result polynomial */
  return res;
}

polynomial_t polynomialSub(polynomial_t p, polynomial_t q) {
  polynomial_t res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* If an easy check shows that p or q are constant zero, return a
     copy of the other or its negation
  */
  if (__polynomialCheapCheckConstantZero(p))
    return polynomialNeg(q);
  if (__polynomialCheapCheckConstantZero(q))
    return polynomialFromCopy(p);

  /* If both polynomials are sparse, perform the operation on sparse
     polynomials.
  */
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    return __polynomialBuildFromSparse(sparsePolynomialSub(p->value.sparse,
							   q->value.sparse));
  }

  /* General case: construct the addition polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = SUBTRACTION;
  res->outputType = ANY_FORM;
  res->value.pair.g = polynomialFromCopy(p);
  res->value.pair.h = polynomialFromCopy(q);

  /* Return the result polynomial */
  return res;
}

polynomial_t polynomialMul(polynomial_t p, polynomial_t q) {
  polynomial_t res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* If an easy check shows that p or q are constant zero, return a
     copy of them.
  */
  if (__polynomialCheapCheckConstantZero(p))
    return polynomialFromCopy(p);
  if (__polynomialCheapCheckConstantZero(q))
    return polynomialFromCopy(q);

  /* If an easy check shows that p or q are constant one, return a
     copy of the other one.
  */
  if (__polynomialCheapCheckConstantOne(p))
    return polynomialFromCopy(q);
  if (__polynomialCheapCheckConstantOne(q))
    return polynomialFromCopy(p);

  /* If both polynomials are sparse, and at least one of them has only
     one monomial (or less), perform the operation on sparse
     polynomials.
  */
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    if ((sparsePolynomialGetMonomialCount(p->value.sparse) <= 1u) ||
	(sparsePolynomialGetMonomialCount(q->value.sparse) <= 1u)) {
      return __polynomialBuildFromSparse(sparsePolynomialMul(p->value.sparse,
							     q->value.sparse));
    }
  }

  /* General case: construct the multiplication polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = MULTIPLICATION;
  res->outputType = ANY_FORM;
  res->value.pair.g = polynomialFromCopy(p);
  res->value.pair.h = polynomialFromCopy(q);

  /* Return the result polynomial */
  return res;
}

polynomial_t polynomialNeg(polynomial_t p) {
  polynomial_t res;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Handle certain cases in an ad-hoc way */
  switch (p->type) {
  case SPARSE:
    return __polynomialBuildFromSparse(sparsePolynomialNeg(p->value.sparse));
    break;
  case SUBTRACTION:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->type = SUBTRACTION;
    res->outputType = ANY_FORM;
    res->value.pair.g = polynomialFromCopy(p->value.pair.h);
    res->value.pair.h = polynomialFromCopy(p->value.pair.g);
    return res;
    break;
  case NEGATE:
    return polynomialFromCopy(p->value.g);
    break;
  default:
    break;
  }

  /* General case: construct the negation polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = NEGATE;
  res->outputType = ANY_FORM;
  res->value.g = polynomialFromCopy(p);

  /* Return the result polynomial */
  return res;
}

polynomial_t polynomialCompose(polynomial_t p, polynomial_t q) {
  polynomial_t res, g, h;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* If q easily proves constant, just construct the composition
     polynomial. This takes almost no space and no time and allows for
     later approximate evaluation of the constructed constant.
  */
  if (__polynomialIsConstantCheap(q)) {
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->type = COMPOSITION;
    res->outputType = ANY_FORM;
    res->value.pair.g = polynomialFromCopy(p);
    res->value.pair.h = polynomialFromCopy(q);
    return res;
  }

  /* If both polynomials are sparse, and polynomial q only has one
     monomial, perform the operation on sparse polynomials.
  */
  if ((p->type == SPARSE) && (q->type == SPARSE)) {
    if (sparsePolynomialGetMonomialCount(q->value.sparse) <= 1u) {
      return __polynomialBuildFromSparse(sparsePolynomialCompose(p->value.sparse,
								 q->value.sparse));
    }
  }

  /* If q is sparse and the identity polynomial, just return a copy of
     p.
  */
  if ((q->type == SPARSE) &&
      (sparsePolynomialIsIdentity(q->value.sparse, 0))) {
    return polynomialFromCopy(p);
  }

  /* If p is of the form p = r^c, we have p(q) = (r(q))^c */
  if (p->type == POWER) {
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->type = POWER;
    res->outputType = ANY_FORM;
    res->value.powering.g = polynomialCompose(p->value.powering.g, q);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    return res;
  }

  /* If q is sparse and only has one monomial but is not constant,
     perform the composition by recursion.
  */
  if ((q->type == SPARSE) &&
      (sparsePolynomialGetMonomialCount(q->value.sparse) <= 1u) &&
      (!sparsePolynomialIsConstant(q->value.sparse,0))) {
    switch (p->type) {
    case SPARSE:
      return __polynomialBuildFromSparse(sparsePolynomialCompose(p->value.sparse,
								 q->value.sparse));
      break;
    case ADDITION:
      g = polynomialCompose(p->value.pair.g, q);
      h = polynomialCompose(p->value.pair.h, q);
      res = polynomialAdd(g, h);
      polynomialFree(g);
      polynomialFree(h);
      break;
    case SUBTRACTION:
      g = polynomialCompose(p->value.pair.g, q);
      h = polynomialCompose(p->value.pair.h, q);
      res = polynomialSub(g, h);
      polynomialFree(g);
      polynomialFree(h);
      break;
    case MULTIPLICATION:
      g = polynomialCompose(p->value.pair.g, q);
      h = polynomialCompose(p->value.pair.h, q);
      res = polynomialMul(g, h);
      polynomialFree(g);
      polynomialFree(h);
      break;
    case COMPOSITION:
      h = polynomialCompose(p->value.pair.h, q);
      res = polynomialCompose(p->value.pair.g, h);
      polynomialFree(h);
      return res;
      break;
    case NEGATE:
      g = polynomialCompose(p->value.g, q);
      res = polynomialNeg(g);
      polynomialFree(g);
      break;
    case POWER:
      res = __polynomialAllocate();
      res->refCount = 1u;
      res->hash.hasHash = 0;
      res->usesExpressionConstant.cached = 0;
      res->type = POWER;
      res->outputType = ANY_FORM;
      res->value.powering.g = polynomialCompose(p->value.powering.g, q);
      res->value.powering.c = constantFromCopy(p->value.powering.c);
      return res;
      break;
    default:
      break;
    }
  }

  /* General case: construct the composed polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = COMPOSITION;
  res->outputType = ANY_FORM;
  res->value.pair.g = polynomialFromCopy(p);
  res->value.pair.h = polynomialFromCopy(q);

  /* Return the result polynomial */
  return res;
}

polynomial_t polynomialDeriv(polynomial_t p) {
  polynomial_t res;
  constant_t one;

  /* Handle stupid inputs */
  if (p == NULL) return NULL;

  /* Easy check if the polynomial actually is constant */
  if (__polynomialIsConstantCheap(p)) {
    return polynomialFromIntConstant(0);
  }

  /* Handle certain cases in an ad-hoc way */
  switch (p->type) {
  case SPARSE:
    return __polynomialBuildFromSparse(sparsePolynomialDeriv(p->value.sparse));
    break;
  case ADDITION:
  case SUBTRACTION:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->type = p->type;
    res->outputType = ANY_FORM;
    res->value.pair.g = polynomialDeriv(p->value.pair.g);
    res->value.pair.h = polynomialDeriv(p->value.pair.h);
    return res;
    break;
  case NEGATE:
    res = __polynomialAllocate();
    res->refCount = 1u;
    res->hash.hasHash = 0;
    res->usesExpressionConstant.cached = 0;
    res->type = NEGATE;
    res->outputType = ANY_FORM;
    res->value.g = polynomialDeriv(p->value.g);
    return res;
    break;
  case POWER:
    if ((!constantIsZero(p->value.powering.c, 1)) &&
	(!constantIsOne(p->value.powering.c, 1))) {
      one = constantFromInt(1);
      res = __polynomialAllocate();
      res->refCount = 1u;
      res->hash.hasHash = 0;
      res->usesExpressionConstant.cached = 0;
      res->type = MULTIPLICATION;
      res->outputType = ANY_FORM;
      res->value.pair.g = __polynomialFromConstant(p->value.powering.c);
      res->value.pair.h = __polynomialAllocate();
      res->value.pair.h->refCount = 1u;
      res->value.pair.h->hash.hasHash = 0;
      res->value.pair.h->usesExpressionConstant.cached = 0;
      res->value.pair.h->type = POWER;
      res->value.pair.h->outputType = ANY_FORM;
      res->value.pair.h->value.powering.g = polynomialFromCopy(p->value.powering.g);
      res->value.pair.h->value.powering.c = constantSub(p->value.powering.c, one);
      constantFree(one);
      return res;
    }
    break;
  default:
    break;
  }

  /* General case: sparsify the polynomial and return the derivative
     of the sparse polynomial.
  */
  __polynomialSparsify(p);
  return __polynomialBuildFromSparse(sparsePolynomialDeriv(p->value.sparse));
}

polynomial_t polynomialGcd(polynomial_t p, polynomial_t q) {

  /* Handle stupid inputs */
  if (p == NULL) return NULL;
  if (q == NULL) return NULL;

  /* General case: sparsify the polynomials and return the gcd
     of the sparse polynomials.
  */
  __polynomialSparsify(p);
  __polynomialSparsify(q);
  return __polynomialBuildFromSparse(sparsePolynomialGcd(p->value.sparse, q->value.sparse));
}

/* Computes g, r, a and b such that 

   (1) gcd(g, r) = gcd(p, q)

   and 

   (2) g = a * p + b * q

   while trying to ensure that 

   (3) r = 0.

   That latter property (3) cannot always be ensured. The first two
   properties (1) and (2) are always guaranteed to be true.

   In addition, if p and q are not constant polynomials, 
   the functions tries to ensure that g is unitary. This 
   property cannot always be ensured.

   If r ends up being 0, then g is the gcd of p and q and a and b are
   the co-factors (Bezout coefficients).

*/
void polynomialExtendedEuclid(polynomial_t *g, polynomial_t *r,
			      polynomial_t *a, polynomial_t *b,
			      polynomial_t p, polynomial_t q) {
  sparse_polynomial_t sg, sr, sa, sb;
  
  /* Handle stupid inputs */
  if ((p == NULL) ||
      (q == NULL)) {
    *g = NULL;
    *r = NULL;
    *a = NULL;
    *b = NULL;
    return;
  }

  /* General case: sparsify the polynomials and return the extended
     Euclid's results on the sparse polynomials.
  */
  __polynomialSparsify(p);
  __polynomialSparsify(q);

  sparsePolynomialExtendedEuclid(&sg, &sr, &sa, &sb,
				 p->value.sparse, q->value.sparse);
  *g = __polynomialBuildFromSparse(sg);
  *r = __polynomialBuildFromSparse(sr);
  *a = __polynomialBuildFromSparse(sa);
  *b = __polynomialBuildFromSparse(sb);
}

static inline int __polynomialFromExpressionInner(polynomial_t *r, node *p, node *t) {
  polynomial_t a, b, quot, rest, zero;
  sparse_polynomial_t sr;
  int res;
  constant_t c;
  node *pp;

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* Try to decompose expressions built on c, x, +, -, *, /, -, ^ */
  switch (p->nodeType) {
  case MEMREF:
    if (p->cache->polynomialRepresentation != NULL) {
      *r = polynomialFromCopy(p->cache->polynomialRepresentation);
      return 1;
    }
    res = __polynomialFromExpressionInner(r, getMemRefChild(p), p);
    if (res && (p->cache->polynomialRepresentation == NULL) && (!polynomialReferencesExpression(*r, p))) {
      p->cache->polynomialRepresentation = polynomialFromCopy(*r);
    }
    return res;
    break;
  case VARIABLE:
    *r = polynomialFromIdentity();
    return 1;
    break;
  case CONSTANT:
    *r = polynomialFromMpfrConstant(*(p->value));
    return 1;
    break;
  case NEG:
    if (!__polynomialFromExpressionInner(&a, p->child1, NULL))
      break;
    *r = polynomialNeg(a);
    polynomialFree(a);
    return 1;
    break;
  case ADD:
  case SUB:
  case MUL:
  case DIV:
  case POW:
    if (!__polynomialFromExpressionInner(&a, p->child1, NULL))
      break;
    if (!__polynomialFromExpressionInner(&b, p->child2, NULL)) {
      polynomialFree(a);
      break;
    }
    res = 0;
    switch (p->nodeType) {
    case ADD:
      *r = polynomialAdd(a, b);
      res = 1;
      break;
    case SUB:
      *r = polynomialSub(a, b);
      res = 1;
      break;
    case MUL:
      *r = polynomialMul(a, b);
      res = 1;
      break;
    case DIV:
      polynomialDiv(&quot, &rest, a, b);
      zero = polynomialFromIntConstant(0);
      if (polynomialEqual(rest, zero, 0)) {
	if (polynomialGetDegreeAsInt(b) == 0) {
	  c = __polynomialGetIthCoefficientAsConstantIntIndex(b, 0);
	  if (!constantIsZero(c, 1)) {
	    *r = quot;
	    res = 1;
	  } else {
	    polynomialFree(quot);
	    res = 0;
	  }
	  constantFree(c);
	} else {
	  *r = quot;
	  res = 1;
	}
      } else {
	polynomialFree(quot);
	res = 0;
      }
      polynomialFree(rest);
      polynomialFree(zero);
      break;
    case POW:
      res = polynomialPow(r, a, b);
      break;
    }
    polynomialFree(a);
    polynomialFree(b);
    if (res) return 1;
    break;
  }

  /* Here, the expression contains some other basic function. It can
     be a polynomial only if it is a constant expression.
  */
  if (t != NULL) {
    pp = t;
  } else {
    pp = p;
  }
  if (isConstant(pp)) {
    if (!__sparsePolynomialFromConstantExpression(&sr, pp)) return 0;
    *r = __polynomialBuildFromSparse(sr);
    return 1;
  }

  return 0;
}

int polynomialFromExpression(polynomial_t *r, node *p) {
  return __polynomialFromExpressionInner(r, p, NULL);
}

static inline int __polynomialFromExpressionOnlyRealCoeffsInner(polynomial_t *r, node *p, node *t) {
  polynomial_t a, b, quot, rest, zero;
  sparse_polynomial_t sr;
  int res;
  constant_t c;
  node *pp;

  /* Handle stupid inputs */
  if (p == NULL) return 0;

  /* Try to decompose expressions built on c, x, +, -, *, /, -, ^ */
  switch (p->nodeType) {
  case MEMREF:
    if (p->cache->polynomialRepresentation != NULL) {
      *r = polynomialFromCopy(p->cache->polynomialRepresentation);
      return 1;
    }
    res = __polynomialFromExpressionOnlyRealCoeffsInner(r, getMemRefChild(p), p);
    if (res && (p->cache->polynomialRepresentation == NULL) && (!polynomialReferencesExpression(*r, p))) {
      p->cache->polynomialRepresentation = polynomialFromCopy(*r);
    }
    return res;
    break;
  case VARIABLE:
    *r = polynomialFromIdentity();
    return 1;
    break;
  case CONSTANT:
    if (mpfr_number_p(*(p->value))) {
      *r = polynomialFromMpfrConstant(*(p->value));
      return 1;
    } else {
      return 0;
    }
    break;
  case NEG:
    if (!__polynomialFromExpressionOnlyRealCoeffsInner(&a, p->child1, NULL))
      break;
    *r = polynomialNeg(a);
    polynomialFree(a);
    return 1;
    break;
  case ADD:
  case SUB:
  case MUL:
  case DIV:
  case POW:
    if (!__polynomialFromExpressionOnlyRealCoeffsInner(&a, p->child1, NULL))
      break;
    if (!__polynomialFromExpressionOnlyRealCoeffsInner(&b, p->child2, NULL)) {
      polynomialFree(a);
      break;
    }
    res = 0;
    switch (p->nodeType) {
    case ADD:
      *r = polynomialAdd(a, b);
      res = 1;
      break;
    case SUB:
      *r = polynomialSub(a, b);
      res = 1;
      break;
    case MUL:
      *r = polynomialMul(a, b);
      res = 1;
      break;
    case DIV:
      polynomialDiv(&quot, &rest, a, b);
      zero = polynomialFromIntConstant(0);
      if (polynomialEqual(rest, zero, 0)) {
	if (polynomialGetDegreeAsInt(b) == 0) {
	  c = __polynomialGetIthCoefficientAsConstantIntIndex(b, 0);
	  if (!constantIsZero(c, 1)) {
	    *r = quot;
	    res = 1;
	  } else {
	    polynomialFree(quot);
	    res = 0;
	  }
	  constantFree(c);
	} else {
	  *r = quot;
	  res = 1;
	}
      } else {
	polynomialFree(quot);
	res = 0;
      }
      polynomialFree(rest);
      polynomialFree(zero);
      break;
    case POW:
      res = polynomialPow(r, a, b);
      break;
    }
    polynomialFree(a);
    polynomialFree(b);
    if (res) return 1;
    break;
  }

  /* Here, the expression contains some other basic function. It can
     be a polynomial only if it is a constant expression.
  */
  if (t != NULL) {
    pp = t;
  } else {
    pp = p;
  }
  if (isConstant(pp)) {
    if (!__sparsePolynomialFromConstantExpressionOnlyRealCoeffs(&sr, pp)) return 0;
    *r = __polynomialBuildFromSparse(sr);
    return 1;
  }

  return 0;
}

int polynomialFromExpressionOnlyRealCoeffs(polynomial_t *r, node *p) {
  return __polynomialFromExpressionOnlyRealCoeffsInner(r, p, NULL);
}

polynomial_t polynomialPowUnsignedInt(polynomial_t p, unsigned int n) {
  polynomial_t res;

  /* Handle stupid case */
  if (p == NULL) return NULL;

  /* If n is zero, return the polynomial 1. */
  if (n == 0u) return polynomialFromIntConstant(1);

  /* If n is one, return a copy of the polynomial p. */
  if (n == 1u) return polynomialFromCopy(p);

  /* If n is two, return p * p. */
  if (n == 2u) return polynomialMul(p, p);

  /* If p is sparse and only has one monomial (or less), immediately
     perform the operation
  */
  if (p->type == SPARSE) {
    if (sparsePolynomialGetMonomialCount(p->value.sparse) <= 1u) {
      return __polynomialBuildFromSparse(sparsePolynomialPowUnsignedInt(p->value.sparse,
									n));
    }
  }

  /* General case: construct the powering polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = POWER;
  res->outputType = ANY_FORM;
  res->value.powering.g = polynomialFromCopy(p);
  res->value.powering.c = constantFromUnsignedInt(n);

  /* Return the result polynomial */
  return res;
}

int polynomialPow(polynomial_t *r, polynomial_t p, polynomial_t q) {
  constant_t n, two14;
  polynomial_t res;
  sparse_polynomial_t sp;

  /* Handle stupid inputs */
  if (p == NULL) return 0;
  if (q == NULL) return 0;

  /* Get the degree of q

     If the integer-output degree accessor function
     indicates failure, this means that q has a degree
     so huge that it does not hold on an integer. In this case,
     p^q is not a polynomial.

     If the integer-output degress accessor function indicates success
     but a degree of at least one, p^q is not a polynomial.

     So the only way that q is a constant is that the integer-output
     degree accessor function returns 0.

  */
  if (polynomialGetDegreeAsInt(q) != 0) return 0;

  /* Here we know that the degree of q is 0, i.e. that it is a
     constant.

     We get its coefficient of degree 0 to get its constant value.

  */
  n = __polynomialGetIthCoefficientAsConstantIntIndex(q, 0);
  if (!constantIsNonNegativeInteger(n,0)) {
    constantFree(n);
    return 0;
  }

  /* Handle the cases n = 0, n = 1 */
  if (constantIsZero(n, 0)) {
    constantFree(n);
    *r = polynomialFromIntConstant(1);
    return 1;
  }
  if (constantIsOne(n, 0)) {
    constantFree(n);
    *r = polynomialFromCopy(p);
    return 1;
  }

  /* If p is sparse and only has one monomial (or less) and n is
     reasonably small (say n <= 2^14), immediately perform the
     operation.
  */
  if (p->type == SPARSE) {
    if (sparsePolynomialGetMonomialCount(p->value.sparse) <= 1u) {
      two14 = constantFromUnsignedInt(1u << 14);
      if (!constantIsGreater(n, two14, 1)) {
	if (sparsePolynomialPowConstant(&sp, p->value.sparse, n)) {
	  constantFree(n);
	  constantFree(two14);
	  *r = __polynomialBuildFromSparse(sp);
	  return 1;
	}
      }
      constantFree(two14);
    }
  }

  /* General case: construct the powering polynomial */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = POWER;
  res->outputType = ANY_FORM;
  res->value.powering.g = polynomialFromCopy(p);
  res->value.powering.c = n;

  /* Set the output and return success */
  *r = res;
  return 1;
}

int polynomialCoefficientsAreDyadic(polynomial_t p, int defVal) {

  /* Handle stupid input */
  if (p == NULL) return defVal;

  /* Try to handle the case without sparsifying the polynomial */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialCoefficientsAreDyadic(p->value.sparse, defVal);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    if (polynomialCoefficientsAreDyadic(p->value.pair.g, 0) &&
	polynomialCoefficientsAreDyadic(p->value.pair.h, 0)) return 1;
    break;
  case NEGATE:
    return polynomialCoefficientsAreDyadic(p->value.g, defVal);
    break;
  case POWER:
    if (constantIsZero(p->value.powering.c, 0)) return 1;
    if (polynomialCoefficientsAreDyadic(p->value.powering.g, 0)) return 1;
    break;
  }

  /* We couldn't decide the case. Sparsify the polynomial. */
  __polynomialSparsify(p);
  return sparsePolynomialCoefficientsAreDyadic(p->value.sparse, defVal);
}

int polynomialCoefficientsAreRational(polynomial_t p, int defVal) {

  /* Handle stupid input */
  if (p == NULL) return defVal;

  /* Try to handle the case without sparsifying the polynomial */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialCoefficientsAreRational(p->value.sparse, defVal);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    if (polynomialCoefficientsAreRational(p->value.pair.g, 0) &&
	polynomialCoefficientsAreRational(p->value.pair.h, 0)) return 1;
    break;
  case NEGATE:
    return polynomialCoefficientsAreRational(p->value.g, defVal);
    break;
  case POWER:
    if (constantIsZero(p->value.powering.c, 0)) return 1;
    if (polynomialCoefficientsAreRational(p->value.powering.g, 0)) return 1;
    break;
  }

  /* We couldn't decide the case. Sparsify the polynomial. */
  __polynomialSparsify(p);
  return sparsePolynomialCoefficientsAreRational(p->value.sparse, defVal);
}

int polynomialCoefficientsHoldOnPrecBits(polynomial_t p, mp_prec_t prec, int defVal) {

  /* Handle stupid input */
  if (p == NULL) return defVal;
  if (prec < 2) return defVal;

  /* Try to handle the case without sparsifying the polynomial */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialCoefficientsHoldOnPrecBits(p->value.sparse, prec, defVal);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    if (polynomialCoefficientsHoldOnPrecBits(p->value.pair.g, prec, 0) &&
	polynomialCoefficientsHoldOnPrecBits(p->value.pair.h, prec, 0)) return 1;
    break;
  case NEGATE:
    return polynomialCoefficientsHoldOnPrecBits(p->value.g, prec, defVal);
    break;
  case POWER:
    if (constantIsZero(p->value.powering.c, 0)) return 1;
    if (polynomialCoefficientsHoldOnPrecBits(p->value.powering.g, prec, 0)) return 1;
    break;
  }

  /* We couldn't decide the case. Sparsify the polynomial. */
  __polynomialSparsify(p);
  return sparsePolynomialCoefficientsHoldOnPrecBits(p->value.sparse, prec, defVal);
}

static inline polynomial_t __polynomialRoundDyadicAnyForm(polynomial_t p, mp_prec_t prec) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has dyadic coefficients, just return a
     copy of it.
  */
  if (polynomialCoefficientsAreDyadic(p, 0))
    return polynomialFromCopy(p);

  /* Handle composition */
  if (p->type == COMPOSITION) {
    __polynomialExecuteComposition(p);
    return __polynomialRoundDyadicAnyForm(p, prec);
  }

  /* Handle the problem recursively, keeping the expression structure
     of the polynomial
  */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = p->type;
  res->outputType = ANY_FORM;
  switch (p->type) {
  case SPARSE:
    res->value.sparse = sparsePolynomialRoundDyadic(p->value.sparse, prec);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    res->value.pair.g = __polynomialRoundDyadicAnyForm(p->value.pair.g, prec);
    res->value.pair.h = __polynomialRoundDyadicAnyForm(p->value.pair.h, prec);
    break;
  case NEGATE:
    res->value.g = __polynomialRoundDyadicAnyForm(p->value.g, prec);
    break;
  case POWER:
    res->value.powering.g = __polynomialRoundDyadicAnyForm(p->value.powering.g, prec);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  default:
    break;
  }
  return res;
}

polynomial_t polynomialRoundDyadic(polynomial_t p, mp_prec_t prec) {
  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the output type is "any form", use a special algorithm */
  if ((p->outputType == ANY_FORM) ||
      __polynomialIsConstantCheap(p)) {
    return __polynomialRoundDyadicAnyForm(p, prec);
  }

  /* If the polynomial only has dyadic coefficients, just return a
     copy of it.
  */
  if (polynomialCoefficientsAreDyadic(p, 0))
    return polynomialFromCopy(p);

  /* Here, at least one of the coefficients is not dyadic. Round it to
     dyadic, by sparsifying it because the output form is "hornerized" or "canonicalized".
  */
  __polynomialSparsify(p);
  return __polynomialBuildFromSparse(sparsePolynomialRoundDyadic(p->value.sparse, prec));
}

static inline polynomial_t __polynomialRoundRationalAnyForm(polynomial_t p, mp_prec_t prec) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has rational coefficients, just return a
     copy of it.
  */
  if (polynomialCoefficientsAreRational(p, 0))
    return polynomialFromCopy(p);

  /* Handle composition */
  if (p->type == COMPOSITION) {
    __polynomialExecuteComposition(p);
    return __polynomialRoundRationalAnyForm(p, prec);
  }

  /* Handle the problem recursively, keeping the expression structure
     of the polynomial
  */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = p->type;
  res->outputType = ANY_FORM;
  switch (p->type) {
  case SPARSE:
    res->value.sparse = sparsePolynomialRoundRational(p->value.sparse, prec);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    res->value.pair.g = __polynomialRoundRationalAnyForm(p->value.pair.g, prec);
    res->value.pair.h = __polynomialRoundRationalAnyForm(p->value.pair.h, prec);
    break;
  case NEGATE:
    res->value.g = __polynomialRoundRationalAnyForm(p->value.g, prec);
    break;
  case POWER:
    res->value.powering.g = __polynomialRoundRationalAnyForm(p->value.powering.g, prec);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  default:
    break;
  }
  return res;
}

polynomial_t polynomialRoundRational(polynomial_t p, mp_prec_t prec) {
  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the output type is "any form", use a special algorithm */
  if ((p->outputType == ANY_FORM) ||
      __polynomialIsConstantCheap(p)) {
    return __polynomialRoundRationalAnyForm(p, prec);
  }

  /* If the polynomial only has rational coefficients, just return a
     copy of it.
  */
  if (polynomialCoefficientsAreRational(p, 0))
    return polynomialFromCopy(p);

  /* Here, at least one of the coefficients is not rational. Round it to
     rational, by sparsifying it because the output form is "hornerized" or "canonicalized".
  */
  __polynomialSparsify(p);
  return __polynomialBuildFromSparse(sparsePolynomialRoundRational(p->value.sparse, prec));
}

static inline polynomial_t __polynomialRoundAnyForm(polynomial_t p, mp_prec_t prec) {
  polynomial_t res;

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the polynomial only has coefficients that hold on prec bits,
     just return a copy of it.
  */
  if (polynomialCoefficientsHoldOnPrecBits(p, prec, 0))
    return polynomialFromCopy(p);

  /* Handle composition */
  if (p->type == COMPOSITION) {
    __polynomialExecuteComposition(p);
    return __polynomialRoundAnyForm(p, prec);
  }

  /* Handle the problem recursively, keeping the expression structure
     of the polynomial
  */
  res = __polynomialAllocate();
  res->refCount = 1u;
  res->hash.hasHash = 0;
  res->usesExpressionConstant.cached = 0;
  res->type = p->type;
  res->outputType = ANY_FORM;
  switch (p->type) {
  case SPARSE:
    res->value.sparse = sparsePolynomialRound(p->value.sparse, prec);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
    res->value.pair.g = __polynomialRoundAnyForm(p->value.pair.g, prec);
    res->value.pair.h = __polynomialRoundAnyForm(p->value.pair.h, prec);
    break;
  case NEGATE:
    res->value.g = __polynomialRoundAnyForm(p->value.g, prec);
    break;
  case POWER:
    res->value.powering.g = __polynomialRoundAnyForm(p->value.powering.g, prec);
    res->value.powering.c = constantFromCopy(p->value.powering.c);
    break;
  default:
    break;
  }
  return res;
}

polynomial_t polynomialRound(polynomial_t p, mp_prec_t prec) {
  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* If the output type is "any form", use a special algorithm */
  if ((p->outputType == ANY_FORM) ||
      __polynomialIsConstantCheap(p)) {
    return __polynomialRoundAnyForm(p, prec);
  }

  /* If the polynomial only has coefficients that hold on prec bits,
     just return a copy of it.
  */
  if (polynomialCoefficientsHoldOnPrecBits(p, prec, 0))
    return polynomialFromCopy(p);

  /* Here, at least one of the coefficients is not rational. Round it to
     rational, by sparsifying it because the output form is "hornerized" or "canonicalized".
  */
  __polynomialSparsify(p);
  return __polynomialBuildFromSparse(sparsePolynomialRound(p->value.sparse, prec));
}

polynomial_t polynomialRoundWithErrorBound(polynomial_t p, sollya_mpfi_t I, mpfr_t delta) {
  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* General case: sparsify the polynomial and round */
  __polynomialSparsify(p);
  return __polynomialBuildFromSparse(sparsePolynomialRoundWithErrorBound(p->value.sparse, I, delta));
}

int polynomialIsHornerized(polynomial_t p) {
  if (p == NULL) return 0;
  return (p->outputType == HORNER_FORM);
}

int polynomialIsCanonicalized(polynomial_t p) {
  if (p == NULL) return 0;
  return (p->outputType == CANONICAL_FORM);
}

static inline sparse_polynomial_t __polynomialGetSparsePolynomial(polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return NULL;

  /* Sparsify the polynomial and return a copy */
  __polynomialSparsify(p);
  return sparsePolynomialFromCopy(p->value.sparse);
}

static inline int __polynomialUsesExpressionConstantInner(polynomial_t p) {

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Handle the problem recursively */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialUsesExpressionConstant(p->value.sparse);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    if (polynomialUsesExpressionConstant(p->value.pair.g)) return 1;
    if (polynomialUsesExpressionConstant(p->value.pair.h)) return 1;
    return 0;
    break;
  case NEGATE:
    return polynomialUsesExpressionConstant(p->value.g);
    break;
  case POWER:
    if (constantUsesExpressionConstant(p->value.powering.c)) return 1;
    if (polynomialUsesExpressionConstant(p->value.powering.g)) return 1;
    return 0;
    break;
  }
  return 0;
}

int polynomialUsesExpressionConstant(polynomial_t p) {
  int res;

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* Check if the result is in the cache */
  if (p->usesExpressionConstant.cached) {
    return p->usesExpressionConstant.res;
  }

  /* Call inner function */
  res = __polynomialUsesExpressionConstantInner(p);

  /* Put the result into the cache */
  p->usesExpressionConstant.res = res;
  p->usesExpressionConstant.cached = 1;

  /* Return the result */
  return res;
}

int polynomialReferencesExpression(polynomial_t p, node *expr) {

  /* Handle stupid input */
  if (p == NULL) return 0;

  /* If the polynomial is sparse, constant and uses the expression 
     in its cache, we need to invalidate that cache.
  */
  if ((p->type == SPARSE) && ((expr != NULL) && (expr->nodeType == MEMREF))) {
    __sparsePolynomialConstantInvalidateExpressionCache(p->value.sparse, expr);
  }
      
  /* If the polynomial does not use any expression, it cannot
     reference the given expression
  */
  if (!polynomialUsesExpressionConstant(p)) {
    return 0;
  }

  /* Handle the problem recursively */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialReferencesExpression(p->value.sparse, expr);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    if (polynomialReferencesExpression(p->value.pair.g, expr)) return 1;
    if (polynomialReferencesExpression(p->value.pair.h, expr)) return 1;
    return 0;
    break;
  case NEGATE:
    return polynomialReferencesExpression(p->value.g, expr);
    break;
  case POWER:
    if (constantReferencesExpression(p->value.powering.c, expr)) return 1;
    if (polynomialReferencesExpression(p->value.powering.g, expr)) return 1;
    return 0;
    break;
  }
  return 0;
}

size_t polynomialDeepReferenceCount(polynomial_t p, node *expr) {

  /* Handle stupid input */
  if (p == NULL) return ((size_t) 0);

  /* Handle the problem recursively */
  switch (p->type) {
  case SPARSE:
    return sparsePolynomialDeepReferenceCount(p->value.sparse, expr);
    break;
  case ADDITION:
  case SUBTRACTION:
  case MULTIPLICATION:
  case COMPOSITION:
    return polynomialDeepReferenceCount(p->value.pair.g, expr) + 
      polynomialDeepReferenceCount(p->value.pair.h, expr);
    break;
  case NEGATE:
    return polynomialDeepReferenceCount(p->value.g, expr);
    break;
  case POWER:
    return constantDeepReferenceCount(p->value.powering.c, expr) + 
      polynomialDeepReferenceCount(p->value.powering.g, expr);
    break;
  }
  return ((size_t) 0);
}

uint64_t polynomialHash(polynomial_t p) {
  uint64_t hash;

  /* Handle stupid input */
  if (p == NULL) return hashPointer(NULL);

  /* Check if the hash has already been computed */
  if (p->hash.hasHash) {
    return p->hash.hash;
  }

  /* Sparsify the polynomial and return a copy */
  __polynomialSparsify(p);
  hash = sparsePolynomialHash(p->value.sparse);

  /* Cache the result */
  p->hash.hash = hash;
  p->hash.hasHash = 1;

  /* Return the hash */
  return hash;
}

int polynomialInterpolateWithExpressions(polynomial_t *poly, node **X, node **Y, unsigned int n) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateWithExpressions(&sp, X, Y, n)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateFunctionWithExpressions(polynomial_t *poly, node **X, node *f, unsigned int n) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateFunctionWithExpressions(&sp, X, f, n)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateWithPoints(polynomial_t *poly, mpfr_t *X, mpfr_t *Y, unsigned int n) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateWithPoints(&sp, X, Y, n)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateFunctionWithPoints(polynomial_t *poly, mpfr_t *X, node *f, unsigned int n) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateFunctionWithPoints(&sp, X, f, n)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateWithExpressionsPermitError(polynomial_t *poly, node **X, node **Y, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateWithExpressionsPermitError(&sp, X, Y, n, I, delta)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateFunctionWithExpressionsPermitError(polynomial_t *poly, node **X, node *f, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateFunctionWithExpressionsPermitError(&sp, X, f, n, I, delta)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateWithPointsPermitError(polynomial_t *poly, mpfr_t *X, mpfr_t *Y, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateWithPointsPermitError(&sp, X, Y, n, I, delta)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}

int polynomialInterpolateFunctionWithPointsPermitError(polynomial_t *poly, mpfr_t *X, node *f, unsigned int n, sollya_mpfi_t I, mpfr_t delta) {
  sparse_polynomial_t sp;
  polynomial_t p;

  /* Everything gets delegated to the sparse polynomial interpolation
     routine 
  */
  sp = NULL;
  if (!sparsePolynomialInterpolateFunctionWithPointsPermitError(&sp, X, f, n, I, delta)) return 0;
  if (sp == NULL) return 0;

  /* Form the polynomial, write it back and return a success indication */
  p = __polynomialBuildFromSparse(sp);
  if (poly != NULL) {
    *poly = p;
  } else {
    polynomialFree(p);
  }
  return 1;
}
