/**************************************************************************\ MODULE: zz_p SUMMARY: The class zz_p is used to represent integers mod p, where 1 <= p < NTL_SP_BOUND. Note that NTL_SP_BOUND is usually 2^30 on 32-bit machines and 2^50 on 64-bit machines. The modulus p may be any positive integer, not necessarily prime. Objects of the class zz_p are represented as a long in the range 0..p-1. An executing program maintains a "current modulus", which is set to p using zz_p::init(p). The current modulus *must* be initialized before any zz_p constructors are invoked. The modulus may be changed, and a mechanism is provided for saving and restoring a modulus (see classes zz_pBak and zz_pContext below). \**************************************************************************/ #include class zz_p { public: zz_p(); // initial value 0 zz_p(const zz_p& a); // copy constructor zz_p& operator=(const zz_p& a); // assignment zz_p& operator=(long a); // assignment static void init(long p); // set the modulus to p, where p > 1. This must be called before any // zz_p constructors are invoked. // The number p must have at most NTL_SP_NBITS bits. static long modulus(); // zz_p::modulus() yields read-only reference to the current // modulus }; long rep(zz_p a); // read-only access to representation of a /**************************************************************************\ Comparison \**************************************************************************/ long operator==(zz_p a, zz_p b); long operator!=(zz_p a, zz_p b); long IsZero(zz_p a); // test for 0 long IsOne(zz_p a); // test for 1 // PROMOTIONS: operators ==, != promote long to zz_p on (a, b). /**************************************************************************\ Addition \**************************************************************************/ // operator notation: zz_p operator+(zz_p a, zz_p b); zz_p operator-(zz_p a, zz_p b); zz_p operator-(zz_p a); // unary - zz_p& operator+=(zz_p& x, zz_p a); zz_p& operator+=(zz_p& x, long a); zz_p& operator-=(zz_p& x, zz_p a); zz_p& operator-=(zz_p& x, long a); zz_p& operator++(zz_p& x); // prefix void operator++(zz_p& x, int); // postfix zz_p& operator--(zz_p& x); // prefix void operator--(zz_p& x, int); // postfix // procedural versions: void add(zz_p& x, zz_p a, zz_p b); // x = a + b void sub(zz_p& x, zz_p a, zz_p b); // x = a - b void negate(zz_p& x, zz_p a); // x = -a // PROMOTIONS: binary +, -, and procedures add, sub promote // from long to zz_p on (a, b). /**************************************************************************\ Multiplication \**************************************************************************/ // operator notation: zz_p operator*(zz_p a, zz_p b); zz_p& operator*=(zz_p& x, zz_p a); zz_p& operator*=(zz_p& x, long a); // procedural versions: void mul(zz_p& x, zz_p a, zz_p b); // x = a * b void sqr(zz_p& x, zz_p a); // x = a^2 zz_p sqr(zz_p a); // PROMOTIONS: operator * and procedure mul promote from long to zz_p // on (a, b). /**************************************************************************\ Division \**************************************************************************/ operator notation: zz_p operator/(z_p a, zz_p b); zz_p& operator/=(zz_p& x, zz_p a); zz_p& operator/=(zz_p& x, long a); procedural versions: void div(zz_p& x, zz_p a, zz_p b); // x = a/b void inv(zz_p& x, zz_p a); zz_p inv(zz_p a); // x = 1/a // PROMOTIONS: operator / and procedure div promote from long to zz_p // on (a, b). /**************************************************************************\ Exponentiation \**************************************************************************/ void power(zz_p& x, zz_p a, long e); // x = a^e (e may be negative) zz_p power(zz_p a, long e); /**************************************************************************\ Random Elements \**************************************************************************/ void random(zz_p& x); zz_p random_zz_p(); // x = random element in zz_p. Uses RandomBnd from ZZ. /**************************************************************************\ Input/Output \**************************************************************************/ ostream& operator<<(ostream& s, zz_p a); istream& operator>>(istream& s, zz_p& x); // a ZZ is read and reduced mod p /**************************************************************************\ Modulus Switching Generally you do the following: zz_pBak bak; bak.save(); // save current modulus (if any) zz_p::init(p); // set modulus to desired value p // ... bak.restore(); // restore old modulus (if any) Note that between the save and restore, you may have several calls to zz_p::init, each of which simply clobbers the previous modulus. The zz_pBak interface is good for implementing simple stack-like modulus "context switching". For more general context switching, see zz_pContext below. .......................................................................... When the current modulus is changed, there may be extant zz_p objects. If the old modulus was saved and then later restored, these objects can be used again as if the modulus had never changed. Note, however, that if a zz_p object is created under one modulus and then used in any way (except destroyed) under another, program behavior is not predictable. This condition is not explicitly checked for, but an error is likely to be raised. One should also not presume that things will work properly if the modulus is changed, but its value happens to be the same--- one should restore the same "context", from either a zz_pBak or a zz_pContext object. \**************************************************************************/ class zz_pBak { public: // To describe this logic, think of a zz_pBak object // of having two components: a modulus q (possibly "null") and // an "auto-restore bit" b. // There is also a global current modulus p (initially "null"). zz_pBak(); // q = "null", b = 0 ~zz_pBak(); // if (b) p = q void save(); // q = p, b = 1 void restore(); // p = q, b = 0 private: zz_pBak(const zz_pBak&); // copy disabled void operator=(const zz_pBak&); // assignment disabled }; // more general context switching: class zz_pContext { // A zz_pContext object has a modulus q (possibly "null"), // but has no auto-restore bit like a zz_pBak object. // However, these objects can be initialized and copied with // complete generality. // As above, p is the current global modulus (initially "null") public: zz_pContext(); // q = "null" zz_pContext(long new_q); // q = new_q void save(); // q = p void restore() const; // p = q zz_pContext(const zz_pContext&); // copy zz_pContext& operator=(const zz_pContext&); // assignment ~zz_pContext(); // destructor }; /**************************************************************************\ Miscellany \**************************************************************************/ void clear(zz_p& x); // x = 0 void set(zz_p& x); // x = 1 static double zz_p::ModulusInverse(); // zz_p::ModulusInverse() returns 1.0/(double(zz_p::modulus())) static zz_p zz_p::zero(); // zz_p::zero() yields a read-only reference to zero void swap(zz_p& x, zz_p& y); // swap x and y static void zz_p::init(long p, long maxroot); // Same as ordinary zz_p::init(p), but somewhat more efficient. If you are // going to perform arithmetic modulo a degree n polynomial, in which // case set maxroot to NextPowerOfTwo(n)+1. This is useful, for // example, if you are going to factor a polynomial of degree n modulo // p, and you know n in advance. // If maxroot is set too low, the program will abort with an // appropriate error message. static void zz_p::FFTInit(long i); // sets modulus to the i-th FFT prime (counting from 0). FFT primes // are NTL_SP_NBITS-bit primes p, where p-1 is divisible by a high power // of two. Thus, polynomial arithmetic mod p can be implemented // particularly efficiently using the FFT. As i increases, the power // of 2 that divides p-1 gets smaller, thus placing a more severe // restriction on the degrees of the polynomials to be multiplied. zz_pContext::zz_pContext(long p, long maxroot); // constructor for a zz_pContext with same semantics // as zz_p::init(p, maxroot) above. zz_pContext::zz_pContext(INIT_FFT_TYPE, long i); // constructor for a zz_pContext with same semantics // as zz_p::FFTInit(i) above; invoke as zz_pContext(INIT_FFT, i).