text: // @file nbtheory.h This code provides number theory utilities.
  // @author TPOC: contact@palisade-crypto.org
  //
  // @copyright Copyright (c) 2019, New Jersey Institute of Technology (NJIT)
  // All rights reserved.
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are met:
  // 1. Redistributions of source code must retain the above copyright notice,
  // this list of conditions and the following disclaimer.
  // 2. Redistributions in binary form must reproduce the above copyright notice,
  // this list of conditions and the following disclaimer in the documentation
  // and/or other materials provided with the distribution. THIS SOFTWARE IS
  // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  /*
   *   NBTHEORY is set set of functions that will be used to calculate
   *following:
   *    - If two numbers are coprime.
   *    - GCD of two numbers
   *    - If number i Prime
   *    - witnesss function to test if number is prime
   *    - Roots of unit for provided cyclotomic integer
   *    - Eulers Totient function phin(n)
   *    - Generator algorithm
   */
  
  #ifndef LBCRYPTO_MATH_NBTHEORY_H
  #define LBCRYPTO_MATH_NBTHEORY_H
  
  #include <memory>
  #include <random>
  #include <set>
  #include <string>
  #include <vector>
  #include "utils/inttypes.h"
  
  namespace {  // to define local (or C-style static) functions here
  
  #if defined(HAVE_INT128)
  inline int clz_u128(unsigned __int128 u) {
    uint64_t hi = u >> 64;
    uint64_t lo = u;
    int retval[3] = {__builtin_clzll(hi), __builtin_clzll(lo) + 64, 128};
  
    int idx = !hi + ((!lo) & (!hi));
    return retval[idx];
  }
  #endif
  };  // namespace
  
  /**
   * @namespace lbcrypto
   * The namespace of lbcrypto
   */
  namespace lbcrypto {
  
  /**
   * Finds roots of unity for given input.  Assumes the the input is a power of
   * two.
   *
   * @param m as number which is cyclotomic(in format of int).
   * @param &modulo which is used to find generator.
   *
   * @return a root of unity.
   */
  template <typename IntType>
  IntType RootOfUnity(usint m, const IntType &modulo);
  
  /**
   * Finds roots of unity for given input.  Assumes the the input cyclotomicorder
   * is a power of two.
   *
   * @param m as number which is cyclotomic(in format of int).
   * @param moduli vector of modulus
   *
   * @returns a vector of roots of unity corresponding to each modulus.
   */
  template <typename IntType>
  std::vector<IntType> RootsOfUnity(usint m, const std::vector<IntType> moduli);
  
  /**
   * Method to reverse bits of num and return an unsigned int, for all bits up to
   * an including the designated most significant bit.
   *
   * @param input an unsigned int
   * @param msb the most significant bit.  All larger bits are disregarded.
   *
   * @return an unsigned integer that represents the reversed bits.
   */
  
  // precomputed reverse of a byte
  
  inline static unsigned char reverse_byte(unsigned char x) {
    static const unsigned char table[] = {
        0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
        0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
        0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
        0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
        0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
        0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
        0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
        0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
        0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
        0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
        0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
        0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
        0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
        0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
        0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
        0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
        0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
        0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
        0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
        0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
        0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
        0x3f, 0xbf, 0x7f, 0xff,
    };
    return table[x];
  }
  
  static int shift_trick[] = {0, 7, 6, 5, 4, 3, 2, 1};
  
  /* Function to reverse bits of num */
  inline usint ReverseBits(usint num, usint msb) {
    usint msbb = (msb >> 3) + (msb & 0x7 ? 1 : 0);
  
    switch (msbb) {
      case 1:
        return (reverse_byte((num)&0xff) >> shift_trick[msb & 0x7]);
  
      case 2:
        return (reverse_byte((num)&0xff) << 8 |
                reverse_byte((num >> 8) & 0xff)) >>
               shift_trick[msb & 0x7];
  
      case 3:
        return (reverse_byte((num)&0xff) << 16 |
                reverse_byte((num >> 8) & 0xff) << 8 |
                reverse_byte((num >> 16) & 0xff)) >>
               shift_trick[msb & 0x7];
  
      case 4:
        return (reverse_byte((num)&0xff) << 24 |
                reverse_byte((num >> 8) & 0xff) << 16 |
                reverse_byte((num >> 16) & 0xff) << 8 |
                reverse_byte((num >> 24) & 0xff)) >>
               shift_trick[msb & 0x7];
      default:
        return -1;
        // PALISADE_THROW(math_error, "msbb value not handled:" +
        // std::to_string(msbb));
    }
  }
  
  /**
   * Get MSB of an integer.
   *
   * @param x the input to find MSB of.
   *
   * @return the index of the MSB bit location.
   */
  template <typename IntType>
  inline usint GetMSB(IntType x) {
    PALISADE_THROW(math_error,
                   "Unsupported integer type (GetMSB() supports 64- and 128-bit "
                   "integers only)");
  }
  
  template <>
  inline usint GetMSB(uint64_t x) {
    if (x == 0) return 0;
  
      // hardware instructions for finding MSB are used are used;
  #if defined(_MSC_VER)
    // a wrapper for VC++
    unsigned long msb;
    _BitScanReverse64(&msb, x);
    return msb + 1;
  #else
    // a wrapper for GCC
    return 64 -
           (sizeof(unsigned long) == 8 ? __builtin_clzl(x) : __builtin_clzll(x));
  #endif
  }
  
  template <>
  inline usint GetMSB(uint32_t x) {
    return GetMSB((uint64_t)x);
  }
  
  #if defined(HAVE_INT128)
  template <>
  inline usint GetMSB(unsigned __int128 x) {
    if (x == 0) return 0;
  
  #if defined(_MSC_VER)
    static_assert(false, "MSVC doesn't support 128-bit integers");
  #else
    return 128 - clz_u128(x);
  #endif
  }
  #endif
  
  /**
   * Get MSB of an unsigned 64 bit integer.
   *
   * @param x the input to find MSB of.
   *
   * @return the index of the MSB bit location.
   */
  inline usint GetMSB64(uint64_t x) { return GetMSB(x); }
  
  template <typename IntType>
  std::shared_ptr<std::vector<int64_t>> GetDigits(const IntType &u, uint64_t base,
                                                  uint32_t k) {
    auto u_vec = std::make_shared<std::vector<int64_t>>(k);
  
    size_t baseDigits = (uint32_t)(std::round(log2(base)));
  
    // if (!(base & (base - 1)))
    IntType uu = u;
    IntType uTemp;
    for (size_t i = 0; i < k;
         i++) {  // ****************4/1/2018 This loop is correct.
      uTemp = uu >> baseDigits;
      (*u_vec)[i] = (uu - (uTemp << baseDigits)).ConvertToInt();
      uu = uTemp;
    }
    return u_vec;
  }
  
  /**
   * Return greatest common divisor of two big binary integers.
   *
   * @param a one integer to find greatest common divisor of.
   * @param b another integer to find greatest common divisor of.
   *
   * @return the greatest common divisor.
   */
  template <typename IntType>
  IntType GreatestCommonDivisor(const IntType &a, const IntType &b);
  
  /**
   * Perform the MillerRabin primality test on an IntType.
   * This approach to primality testing is iterative and randomized.
   * It returns false if evidence of non-primality is found, and true if no
   * evidence is found after multiple rounds of testing. The const parameter
   * PRIMALITY_NO_OF_ITERATIONS determines how many rounds are used ( set in
   * nbtheory.h).
   *
   * @param p the candidate prime to test.
   * @param niter Number of iterations used for primality
   *              testing (default = 100.
   *
   * @return false if evidence of non-primality is found.  True is no evidence of
   * non-primality is found.
   */
  template <typename IntType>
  bool MillerRabinPrimalityTest(const IntType &p, const usint niter = 100);
  
  /**
   * Perform the PollardRho factorization of a IntType.
   * Returns IntType::ONE if no factorization is found.
   *
   * @param n the value to perform a factorization on.
   * @return a factor of n, and IntType::ONE if no other factor is found.
   */
  template <typename IntType>
  const IntType PollardRhoFactorization(const IntType &n);
  
  /**
   * Recursively factorizes to find the distinct primefactors of a number.
   * @param &n the value to factorize. [note the value of n is destroyed]
   * @param &primeFactors set of factors found [must begin cleared]
   Side effects: n is destroyed.
   */
  template <typename IntType>
  void PrimeFactorize(IntType n, std::set<IntType> &primeFactors);
  
  /**
   * Finds the first prime that satisfies q = 1 mod m
   *
   * @param nBits the number of bits needed to be in q.
   * @param m the the ring parameter.
   *
   * @return the first prime modulus.
   */
  template <typename IntType>
  IntType FirstPrime(uint64_t nBits, uint64_t m);
  
  /**
   * Finds the next prime that satisfies q = 1 mod m
   *
   * @param &q is the prime number to start from (the number itself is not
   * included)
   *
   * @return the next prime modulus.
   */
  template <typename IntType>
  IntType NextPrime(const IntType &q, uint64_t cyclotomicOrder);
  
  /**
   * Finds the previous prime that satisfies q = 1 mod m
   *
   * @param &q is the prime number to start from (the number itself is not
   * included)
   *
   * @return the previous prime modulus.
   */
  template <typename IntType>
  IntType PreviousPrime(const IntType &q, uint64_t cyclotomicOrder);
  
  /**
   * Multiplicative inverse for primitive unsigned integer data types
   *
   * @param a the number we need the inverse of.
   * @param b the modulus we are working with.
   *
   * @return the multiplicative inverse
   */
  usint ModInverse(usint a, usint b);
  
  /**
   * Returns the next power of 2 that is greater than the input number.
   *
   * @param &n is the input value for which next power of 2 needs to be computed.
   * @return Next power of 2 that is greater or equal to n.
   */
  template <typename IntType>
  IntType NextPowerOfTwo(const IntType &n);
  
  /**
   * Returns the totient value φ(n) of a number n.
   *
   * @param &n the input number.
   * @return φ(n) which is the number of integers m coprime to n such that 1 ≤ m ≤
   * n.
   */
  uint64_t GetTotient(const uint64_t n);
  
  /**
   * Returns the list of coprimes to number n in ascending order.
   *
   * @param &n the input number.
   * @return vector of mi's such that 1 ≤ mi ≤ n and gcd(mi,n)==1.
   */
  template <typename IntType>
  std::vector<IntType> GetTotientList(const IntType &n);
  
  /**
   * Returns the polynomial modulus.
   *
   * @param &dividend the input dividend polynomial with degree >= degree of
   * divisor.
   * @param &divisor the input divisor polynomial with degree <= degree of
   * dividend and divisor is a monic polynomial.
   * @param &modulus the working modulus.
   * @return resultant polynomial vector s.t. return = divident mod
   * (divisor,modulus).
   */
  template <typename IntVector>
  IntVector PolyMod(const IntVector &dividend, const IntVector &divisor,
                    const typename IntVector::Integer &modulus);
  
  /**
   * Returns the polynomial multiplication of the input operands.
   *
   * @param &a the input polynomial.
   * @param &b the input polynomial.
   * a and b must have the same modulus.
   * @return resultant polynomial s.t. return = a*b and coefficinet ci =
   * ci%modulus.
   */
  template <typename IntVector>
  IntVector PolynomialMultiplication(const IntVector &a, const IntVector &b);
  
  /**
   * Returns the m-th cyclotomic polynomial.
   * Added as a wrapper to GetCyclotomicPolynomialRecursive
   * @param &m the input cyclotomic order.
   * @param &modulus is the working modulus.
   * @return resultant m-th cyclotomic polynomial with coefficients in modulus.
   */
  template <typename IntVector>
  IntVector GetCyclotomicPolynomial(usint m,
                                    const typename IntVector::Integer &modulus);
  
  /**
   * Returns the m-th cyclotomic polynomial.
   *
   * @param &m the input cyclotomic order.
   * @return resultant m-th cyclotomic polynomial.
   */
  std::vector<int> GetCyclotomicPolynomialRecursive(usint m);
  
  /**
   * Returns the remainder after polynomial division of dividend with divisor =
   * x-a. Uses synthetic division algorithm.
   * @param &dividend is the input polynomial dividend in lower to higher
   * coefficient form.
   * @param &a is the integer in divisor[x-a].
   * @return remainder after division with x-a.
   */
  template <typename IntVector>
  typename IntVector::Integer SyntheticRemainder(
      const IntVector &dividend, const typename IntVector::Integer &a,
      const typename IntVector::Integer &modulus);
  
  /**
   * Returns the remainder vector after polynomial division of dividend with
   * divisor = x-aList[i]. Uses synthetic division algorithm.
   * @param &dividend is the input polynomial dividend in lower to higher
   * coefficient form.
   * @param &aList is the integer vector for divisor[x-aList[i]].
   * @return remainder vector after division with x-aList[i].
   */
  template <typename IntVector>
  IntVector SyntheticPolyRemainder(const IntVector &dividend,
                                   const IntVector &aList,
                                   const typename IntVector::Integer &modulus);
  
  /**
   * Returns the polynomial after raising it by exponent = power.
   * Returns input^power.Uses Frobenius mapping.
   * @param &input is operand polynomial which needs to be exponentiated.
   * @param &power is the exponent.
   * @return exponentiated polynomial.
   */
  template <typename IntVector>
  IntVector PolynomialPower(const IntVector &input, usint power);
  
  /**
   * Returns the quotient after polynomial division of dividend with divisor =
   * x-a. Uses synthetic division algorithm.
   * @param &dividend is the input polynomial dividend in lower to higher
   * coefficient form.
   * @param &a is the integer in divisor[x-a].
   * @return quotient after division with x-a.
   */
  template <typename IntVector>
  IntVector SyntheticPolynomialDivision(
      const IntVector &dividend, const typename IntVector::Integer &a,
      const typename IntVector::Integer &modulus);
  
  /**
   * Checkes if g is a generator for any cyclic group with modulus q (non-prime
   * moduli are supported); currently q up to 64 bits only are supported
   * @param &g is candidate generator
   * @param &q is the modulus ( 2, 4, p^k, or 2*p^k where p^k is a power of an odd
   * prime number )
   * @return true if g is a generator
   */
  template <typename IntType>
  bool IsGenerator(const IntType &g, const IntType &q);
  
  /**
   * Finds a generator for any cyclic group with modulus q (non-prime moduli are
   * supported); currently q up to 64 bits only are supported
   * @param &q is the modulus ( 2, 4, p^k, or 2*p^k where p^k is a power of an odd
   * prime number )
   * @return true if g is a generator
   */
  template <typename IntType>
  IntType FindGeneratorCyclic(const IntType &q);
  
  /**
   * Find an automorphism index for a power-of-two cyclotomic order
   * @param i is the plaintext array index
   * @param m is the cyclotomic order
   * @return the automorphism index
   */
  uint32_t FindAutomorphismIndex2n(int32_t i, uint32_t m);
  
  // For the CKKS scheme
  uint32_t FindAutomorphismIndex2nComplex(int32_t i, uint32_t m);
  
  /**
   * Find an automorhism index for cyclic groups
   * @param i is the plaintext array index
   * @param m is the cyclotomic order
   * @param g is the generator
   * @return the automorphism index
   */
  uint32_t FindAutomorphismIndexCyclic(int32_t i, uint32_t m, uint32_t g);
  
  }  // namespace lbcrypto
  
  #endif
  