/* misc.h
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions.
 *
 * Miscellaneous utilities for the interpolation code.
 * Header file
 */

#ifndef MISC_H
#define MISC_H

#include <cmath>
#include <climits>
#include <cassert>
#include <complex>
#include <NTL/tools.h>
#include <NTL/ZZ.h>

// Some constants
extern long double PI_LD;
extern double PI_D;
extern float PI_F;

// Get Pi in the given floating point type
template <class T> T getPi();

/* Seed NTL's random generator from an unsigned long long */
void ullseed (unsigned long long seed);

/* Initialize NTL's random seed from /dev/urandom and
 * (optionally) print its value to stdout.
 */
void randseed (bool printit = true); 

/* Computes a lower bound on lambda so that at least half the primes
 * between lambda and 2*lambda are good.
 * See Lemma 3.1 from Giesbrecht & Roche (2011).
 */
long goodp_bound (const NTL::ZZ& D, long T);

/* Computes a "best guess" bound on a small number to start searching
 *  for good primes.
 */
long guessp_bound (const NTL::ZZ& D, long T);

/* For any floating point type T, determines the least e such
 * that 1.0+e != 1.0 is true. Corresponds to 
 * 2^(-number of bits in the mantissa).
 */
template <class T>
T manbits() {
  T a = 1.0;
  T b = a / 2.0;
  while ((a + b) != a) {
    b /= 2.0;
  }
  return (b*2.0);
}

/* Compute an approximation for the exact root of unity
 * exp(2*Pi*num/denom).
 */
template <class T>
std::complex<T> ru (const NTL::ZZ& num, const NTL::ZZ& denom) {
  NTL::ZZ n2(num);
  n2 %= denom;
  T theta = NTL::to_double(n2) / NTL::to_double(denom);
  theta *= getPi<T>() * 2.0;
  return std::polar (((T)1.0), theta);
}

/* Class to generate random complex numbers.
 * Numbers are generated polar-ly, with radius in a specified range
 * and angle (by default) randomly chosen.
 */
template <class T>
class RandomGen {
  private:
  T lower;
  T upper;
  bool onlyreal;

  public:
  
  /* Will generate random numbers with radius between l and u
   * and angle randomly uniformly distributed from 0 to 2*Pi.
   * If r=true, then only real numbers are generated.
   * In either case, 0 <= l <= u must be true.
   */
  RandomGen (T l=0, T u=1, bool r=false) :lower(l), upper(u), onlyreal(r) {
    assert (lower <= upper);
    assert (lower >= 0);
  }

  // Generate a random unsigned long long
  unsigned long long randULL () {
    unsigned long long a;
    NTL::BytesFromZZ (reinterpret_cast<unsigned char*>(&a), 
      NTL::RandomBits_ZZ(64), sizeof(unsigned long long));
    return a;
  }
  
  // Generate a random real in [0,1)
  T randReal () {
    return static_cast<T>(randULL()) / static_cast<T>(ULLONG_MAX);
  }

  std::complex<T> operator() (void) {
    T r = randReal() * (upper-lower) + lower;
    if (onlyreal) {
      if (NTL::RandomBits_long(1)) r *= -1.0;
      return std::complex<T>(r,0);
    }
    else {
      T theta = randReal() * 2.0 * getPi<T>();
      return std::polar(r,theta);
    }
  }
};

#endif // MISC_H
