/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * INCLUDES MODIFICATIONS BY RICHARD ZSCHECH AS WELL AS GOOGLE.
 */
package java.math;

/**
 * Static library that provides all operations related with division and modular
 * arithmetic to {@link BigInteger}. Some methods are provided in both mutable
 * and immutable way. There are several variants provided listed below:
 *
 * <ul type="circle"> <li><b>Division</b> <ul type="circle"> <li>
 * {@link BigInteger} division and remainder by {@link BigInteger}.</li> <li>
 * {@link BigInteger} division and remainder by {@code int}.</li> <li><i>gcd</i>
 * between {@link BigInteger} numbers.</li> </ul> </li> <li><b>Modular
 * arithmetic </b> <ul type="circle"> <li>Modular exponentiation between
 * {@link BigInteger} numbers.</li> <li>Modular inverse of a {@link BigInteger}
 * numbers.</li> </ul> </li> </ul>
 */
class Division {

  /**
   * Divides the array 'a' by the array 'b' and gets the quotient and the
   * remainder. Implements the Knuth's division algorithm. See D. Knuth, The Art
   * of Computer Programming, vol. 2. Steps D1-D8 correspond the steps in the
   * algorithm description.
   *
   * @param quot the quotient
   * @param quotLength the quotient's length
   * @param a the dividend
   * @param aLength the dividend's length
   * @param b the divisor
   * @param bLength the divisor's length
   * @return the remainder
   */
  static int[] divide(int quot[], int quotLength, int a[], int aLength,
      int b[], int bLength) {

    int normA[] = new int[aLength + 1]; // the normalized dividend
    // an extra byte is needed for correct shift
    int normB[] = new int[bLength + 1]; // the normalized divisor;
    int normBLength = bLength;
    /*
     * Step D1: normalize a and b and put the results to a1 and b1 the
     * normalized divisor's first digit must be >= 2^31
     */
    int divisorShift = Integer.numberOfLeadingZeros(b[bLength - 1]);
    if (divisorShift != 0) {
      BitLevel.shiftLeft(normB, b, 0, divisorShift);
      BitLevel.shiftLeft(normA, a, 0, divisorShift);
    } else {
      System.arraycopy(a, 0, normA, 0, aLength);
      System.arraycopy(b, 0, normB, 0, bLength);
    }
    int firstDivisorDigit = normB[normBLength - 1];
    // Step D2: set the quotient index
    int i = quotLength - 1;
    int j = aLength;

    while (i >= 0) {
      // Step D3: calculate a guess digit guessDigit
      int guessDigit = 0;
      if (normA[j] == firstDivisorDigit) {
        // set guessDigit to the largest unsigned int value
        guessDigit = -1;
      } else {
        long product = (((normA[j] & 0xffffffffL) << 32) + (normA[j - 1] & 0xffffffffL));
        long res = Division.divideLongByInt(product, firstDivisorDigit);
        guessDigit = (int) res; // the quotient of divideLongByInt
        int rem = (int) (res >> 32); // the remainder of
        // divideLongByInt
        // decrease guessDigit by 1 while leftHand > rightHand
        if (guessDigit != 0) {
          long leftHand = 0;
          long rightHand = 0;
          boolean rOverflowed = false;
          guessDigit++; // to have the proper value in the loop
          // below
          do {
            guessDigit--;
            if (rOverflowed) {
              break;
            }
            // leftHand always fits in an unsigned long
            leftHand = (guessDigit & 0xffffffffL)
                * (normB[normBLength - 2] & 0xffffffffL);
            /*
             * rightHand can overflow; in this case the loop condition will be
             * true in the next step of the loop
             */
            rightHand = ((long) rem << 32) + (normA[j - 2] & 0xffffffffL);
            long longR = (rem & 0xffffffffL)
                + (firstDivisorDigit & 0xffffffffL);
            /*
             * checks that longR does not fit in an unsigned int; this ensures
             * that rightHand will overflow unsigned long in the next step
             */
            if (Integer.numberOfLeadingZeros((int) (longR >>> 32)) < 32) {
              rOverflowed = true;
            } else {
              rem = (int) longR;
            }
          } while (((leftHand ^ 0x8000000000000000L) > (rightHand ^ 0x8000000000000000L)));
        }
      }
      // Step D4: multiply normB by guessDigit and subtract the production
      // from normA.
      if (guessDigit != 0) {
        int borrow = Division.multiplyAndSubtract(normA, j - normBLength,
            normB, normBLength, guessDigit);
        // Step D5: check the borrow
        if (borrow != 0) {
          // Step D6: compensating addition
          guessDigit--;
          long carry = 0;
          for (int k = 0; k < normBLength; k++) {
            carry += (normA[j - normBLength + k] & 0xffffffffL)
                + (normB[k] & 0xffffffffL);
            normA[j - normBLength + k] = (int) carry;
            carry >>>= 32;
          }
        }
      }
      if (quot != null) {
        quot[i] = guessDigit;
      }
      // Step D7
      j--;
      i--;
    }
    /*
     * Step D8: we got the remainder in normA. Denormalize it id needed
     */
    if (divisorShift != 0) {
      // reuse normB
      BitLevel.shiftRight(normB, normBLength, normA, 0, divisorShift);
      return normB;
    }
    System.arraycopy(normA, 0, normB, 0, bLength);
    return normA;
  }

  /**
   * Computes the quotient and the remainder after a division by an {@code int}
   * number.
   *
   * @return an array of the form {@code [quotient, remainder]}.
   */
  static BigInteger[] divideAndRemainderByInteger(BigInteger val, int divisor,
      int divisorSign) {
    // res[0] is a quotient and res[1] is a remainder:
    int[] valDigits = val.digits;
    int valLen = val.numberLength;
    int valSign = val.sign;
    if (valLen == 1) {
      long a = (valDigits[0] & 0xffffffffL);
      long b = (divisor & 0xffffffffL);
      long quo = a / b;
      long rem = a % b;
      if (valSign != divisorSign) {
        quo = -quo;
      }
      if (valSign < 0) {
        rem = -rem;
      }
      return new BigInteger[] {BigInteger.valueOf(quo), BigInteger.valueOf(rem)};
    }
    int quotientLength = valLen;
    int quotientSign = ((valSign == divisorSign) ? 1 : -1);
    int quotientDigits[] = new int[quotientLength];
    int remainderDigits[];
    remainderDigits = new int[] {Division.divideArrayByInt(quotientDigits,
        valDigits, valLen, divisor)};
    BigInteger result0 = new BigInteger(quotientSign, quotientLength,
        quotientDigits);
    BigInteger result1 = new BigInteger(valSign, 1, remainderDigits);
    result0.cutOffLeadingZeroes();
    result1.cutOffLeadingZeroes();
    return new BigInteger[] {result0, result1};
  }

  /**
   * Divides an array by an integer value. Implements the Knuth's division
   * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
   *
   * @param dest the quotient
   * @param src the dividend
   * @param srcLength the length of the dividend
   * @param divisor the divisor
   * @return remainder
   */
  static int divideArrayByInt(int dest[], int src[], final int srcLength,
      final int divisor) {

    long rem = 0;
    long bLong = divisor & 0xffffffffL;

    for (int i = srcLength - 1; i >= 0; i--) {
      long temp = (rem << 32) | (src[i] & 0xffffffffL);
      long quot;
      if (temp >= 0) {
        quot = (temp / bLong);
        rem = (temp % bLong);
      } else {
        /*
         * make the dividend positive shifting it right by 1 bit then get the
         * quotient an remainder and correct them properly
         */
        long aPos = temp >>> 1;
        long bPos = divisor >>> 1;
        quot = aPos / bPos;
        rem = aPos % bPos;
        // double the remainder and add 1 if a is odd
        rem = (rem << 1) + (temp & 1);
        if ((divisor & 1) != 0) {
          // the divisor is odd
          if (quot <= rem) {
            rem -= quot;
          } else {
            if (quot - rem <= bLong) {
              rem += bLong - quot;
              quot -= 1;
            } else {
              rem += (bLong << 1) - quot;
              quot -= 2;
            }
          }
        }
      }
      dest[i] = (int) (quot & 0xffffffffL);
    }
    return (int) rem;
  }

  /**
   * Divides an unsigned long a by an unsigned int b. It is supposed that the
   * most significant bit of b is set to 1, i.e. b < 0
   *
   * @param a the dividend
   * @param b the divisor
   * @return the long value containing the unsigned integer remainder in the
   *         left half and the unsigned integer quotient in the right half
   */
  static long divideLongByInt(long a, int b) {
    long quot;
    long rem;
    long bLong = b & 0xffffffffL;

    if (a >= 0) {
      quot = (a / bLong);
      rem = (a % bLong);
    } else {
      /*
       * Make the dividend positive shifting it right by 1 bit then get the
       * quotient an remainder and correct them properly
       */
      long aPos = a >>> 1;
      long bPos = b >>> 1;
      quot = aPos / bPos;
      rem = aPos % bPos;
      // double the remainder and add 1 if a is odd
      rem = (rem << 1) + (a & 1);
      if ((b & 1) != 0) { // the divisor is odd
        if (quot <= rem) {
          rem -= quot;
        } else {
          if (quot - rem <= bLong) {
            rem += bLong - quot;
            quot -= 1;
          } else {
            rem += (bLong << 1) - quot;
            quot -= 2;
          }
        }
      }
    }
    return (rem << 32) | (quot & 0xffffffffL);
  }

  /**
   * Performs modular exponentiation using the Montgomery Reduction. It requires
   * that all parameters be positive and the modulus be even. Based <i>The
   * square and multiply algorithm and the Montgomery Reduction C. K. Koc -
   * Montgomery Reduction with Even Modulus</i>. The square and multiply
   * algorithm and the Montgomery Reduction.
   *
   * @ar.org.fitc.ref "C. K. Koc - Montgomery Reduction with Even Modulus"
   * @see BigInteger#modPow(BigInteger, BigInteger)
   */
  static BigInteger evenModPow(BigInteger base, BigInteger exponent,
      BigInteger modulus) {
    // PRE: (base > 0), (exponent > 0), (modulus > 0) and (modulus even)
    // STEP 1: Obtain the factorization 'modulus'= q * 2^j.
    int j = modulus.getLowestSetBit();
    BigInteger q = modulus.shiftRight(j);

    // STEP 2: Compute x1 := base^exponent (mod q).
    BigInteger x1 = oddModPow(base, exponent, q);

    // STEP 3: Compute x2 := base^exponent (mod 2^j).
    BigInteger x2 = pow2ModPow(base, exponent, j);

    // STEP 4: Compute q^(-1) (mod 2^j) and y := (x2-x1) * q^(-1) (mod 2^j)
    BigInteger qInv = modPow2Inverse(q, j);
    BigInteger y = (x2.subtract(x1)).multiply(qInv);
    inplaceModPow2(y, j);
    if (y.sign < 0) {
      y = y.add(BigInteger.getPowerOfTwo(j));
    }
    // STEP 5: Compute and return: x1 + q * y
    return x1.add(q.multiply(y));
  }

  /**
   * Performs the final reduction of the Montgomery algorithm.
   *
   * @see #monPro(BigInteger, BigInteger, BigInteger, long)
   * @see #monSquare(BigInteger, BigInteger, long)
   */
  static BigInteger finalSubtraction(int res[], BigInteger modulus) {

    // skipping leading zeros
    int modulusLen = modulus.numberLength;
    boolean doSub = res[modulusLen] != 0;
    if (!doSub) {
      int modulusDigits[] = modulus.digits;
      doSub = true;
      for (int i = modulusLen - 1; i >= 0; i--) {
        if (res[i] != modulusDigits[i]) {
          doSub = (res[i] != 0)
              && ((res[i] & 0xFFFFFFFFL) > (modulusDigits[i] & 0xFFFFFFFFL));
          break;
        }
      }
    }

    BigInteger result = new BigInteger(1, modulusLen + 1, res);

    // if (res >= modulusDigits) compute (res - modulusDigits)
    if (doSub) {
      Elementary.inplaceSubtract(result, modulus);
    }

    result.cutOffLeadingZeroes();
    return result;
  }

  /**
   * @param m a positive modulus Return the greatest common divisor of op1 and
   *          op2,
   *
   * @param op1 must be greater than zero
   * @param op2 must be greater than zero
   * @see BigInteger#gcd(BigInteger)
   * @return {@code GCD(op1, op2)}
   */
  static BigInteger gcdBinary(BigInteger op1, BigInteger op2) {
    // PRE: (op1 > 0) and (op2 > 0)

    /*
     * Divide both number the maximal possible times by 2 without rounding
     * gcd(2*a, 2*b) = 2 * gcd(a,b)
     */
    int lsb1 = op1.getLowestSetBit();
    int lsb2 = op2.getLowestSetBit();
    int pow2Count = Math.min(lsb1, lsb2);

    BitLevel.inplaceShiftRight(op1, lsb1);
    BitLevel.inplaceShiftRight(op2, lsb2);

    BigInteger swap;
    // I want op2 > op1
    if (op1.compareTo(op2) == BigInteger.GREATER) {
      swap = op1;
      op1 = op2;
      op2 = swap;
    }

    do { // INV: op2 >= op1 && both are odd unless op1 = 0

      // Optimization for small operands
      // (op2.bitLength() < 64) implies by INV (op1.bitLength() < 64)
      if ((op2.numberLength == 1)
          || ((op2.numberLength == 2) && (op2.digits[1] > 0))) {
        op2 = BigInteger.valueOf(Division.gcdBinary(op1.longValue(),
            op2.longValue()));
        break;
      }

      // Implements one step of the Euclidean algorithm
      // To reduce one operand if it's much smaller than the other one
      if (op2.numberLength > op1.numberLength * 1.2) {
        op2 = op2.remainder(op1);
        if (op2.signum() != 0) {
          BitLevel.inplaceShiftRight(op2, op2.getLowestSetBit());
        }
      } else {

        // Use Knuth's algorithm of successive subtract and shifting
        do {
          Elementary.inplaceSubtract(op2, op1); // both are odd
          BitLevel.inplaceShiftRight(op2, op2.getLowestSetBit()); // op2 is even
        } while (op2.compareTo(op1) >= BigInteger.EQUALS);
      }
      // now op1 >= op2
      swap = op2;
      op2 = op1;
      op1 = swap;
    } while (op1.sign != 0);
    return op2.shiftLeft(pow2Count);
  }

  /**
   * Performs the same as {@link #gcdBinary(BigInteger, BigInteger)}, but with
   * numbers of 63 bits, represented in positives values of {@code long} type.
   *
   * @param op1 a positive number
   * @param op2 a positive number
   * @see #gcdBinary(BigInteger, BigInteger)
   * @return <code>GCD(op1, op2)</code>
   */
  static long gcdBinary(long op1, long op2) {
    // PRE: (op1 > 0) and (op2 > 0)
    int lsb1 = Long.numberOfTrailingZeros(op1);
    int lsb2 = Long.numberOfTrailingZeros(op2);
    int pow2Count = Math.min(lsb1, lsb2);

    if (lsb1 != 0) {
      op1 >>>= lsb1;
    }
    if (lsb2 != 0) {
      op2 >>>= lsb2;
    }
    do {
      if (op1 >= op2) {
        op1 -= op2;
        op1 >>>= Long.numberOfTrailingZeros(op1);
      } else {
        op2 -= op1;
        op2 >>>= Long.numberOfTrailingZeros(op2);
      }
    } while (op1 != 0);
    return (op2 << pow2Count);
  }

  /**
   * Performs {@code x = x mod (2<sup>n</sup>)}.
   *
   * @param x a positive number, it will store the result.
   * @param n a positive exponent of {@code 2}.
   */
  static void inplaceModPow2(BigInteger x, int n) {
    // PRE: (x > 0) and (n >= 0)
    int fd = n >> 5;
    int leadingZeros;

    if ((x.numberLength < fd) || (x.bitLength() <= n)) {
      return;
    }
    leadingZeros = 32 - (n & 31);
    x.numberLength = fd + 1;
    x.digits[fd] &= (leadingZeros < 32) ? (-1 >>> leadingZeros) : 0;
    x.cutOffLeadingZeroes();
  }

  /**
   *
   * Based on "New Algorithm for Classical Modular Inverse" Róbert Lórencz. LNCS
   * 2523 (2002)
   *
   * @return a^(-1) mod m
   */
  static BigInteger modInverseLorencz(BigInteger a, BigInteger modulo) {
    // PRE: a is coprime with modulo, a < modulo

    int max = Math.max(a.numberLength, modulo.numberLength);
    int uDigits[] = new int[max + 1]; // enough place to make all the inplace
                                      // operation
    int vDigits[] = new int[max + 1];
    System.arraycopy(modulo.digits, 0, uDigits, 0, modulo.numberLength);
    System.arraycopy(a.digits, 0, vDigits, 0, a.numberLength);
    BigInteger u = new BigInteger(modulo.sign, modulo.numberLength, uDigits);
    BigInteger v = new BigInteger(a.sign, a.numberLength, vDigits);

    BigInteger r = new BigInteger(0, 1, new int[max + 1]); // BigInteger.ZERO;
    BigInteger s = new BigInteger(1, 1, new int[max + 1]);
    s.digits[0] = 1;
    // r == 0 && s == 1, but with enough place

    int coefU = 0, coefV = 0;
    int n = modulo.bitLength();
    int k;
    while (!isPowerOfTwo(u, coefU) && !isPowerOfTwo(v, coefV)) {

      // modification of original algorithm: I calculate how many times the
      // algorithm will enter in the same branch of if
      k = howManyIterations(u, n);

      if (k != 0) {
        BitLevel.inplaceShiftLeft(u, k);
        if (coefU >= coefV) {
          BitLevel.inplaceShiftLeft(r, k);
        } else {
          BitLevel.inplaceShiftRight(s, Math.min(coefV - coefU, k));
          if (k - (coefV - coefU) > 0) {
            BitLevel.inplaceShiftLeft(r, k - coefV + coefU);
          }
        }
        coefU += k;
      }

      k = howManyIterations(v, n);
      if (k != 0) {
        BitLevel.inplaceShiftLeft(v, k);
        if (coefV >= coefU) {
          BitLevel.inplaceShiftLeft(s, k);
        } else {
          BitLevel.inplaceShiftRight(r, Math.min(coefU - coefV, k));
          if (k - (coefU - coefV) > 0) {
            BitLevel.inplaceShiftLeft(s, k - coefU + coefV);
          }
        }
        coefV += k;
      }

      if (u.signum() == v.signum()) {
        if (coefU <= coefV) {
          Elementary.completeInPlaceSubtract(u, v);
          Elementary.completeInPlaceSubtract(r, s);
        } else {
          Elementary.completeInPlaceSubtract(v, u);
          Elementary.completeInPlaceSubtract(s, r);
        }
      } else {
        if (coefU <= coefV) {
          Elementary.completeInPlaceAdd(u, v);
          Elementary.completeInPlaceAdd(r, s);
        } else {
          Elementary.completeInPlaceAdd(v, u);
          Elementary.completeInPlaceAdd(s, r);
        }
      }
      if (v.signum() == 0 || u.signum() == 0) {
        // math.19: BigInteger not invertible
        throw new ArithmeticException("BigInteger not invertible.");
      }
    }

    if (isPowerOfTwo(v, coefV)) {
      r = s;
      if (v.signum() != u.signum()) {
        u = u.negate();
      }
    }
    if (u.testBit(n)) {
      if (r.signum() < 0) {
        r = r.negate();
      } else {
        r = modulo.subtract(r);
      }
    }
    if (r.signum() < 0) {
      r = r.add(modulo);
    }

    return r;
  }

  /**
   * Calculates a.modInverse(p) Based on: Savas, E; Koc, C "The Montgomery
   * Modular Inverse - Revised".
   */
  static BigInteger modInverseMontgomery(BigInteger a, BigInteger p) {
    if (a.sign == 0) {
      // ZERO hasn't inverse
      // math.19: BigInteger not invertible
      throw new ArithmeticException("BigInteger not invertible.");
    }

    if (!p.testBit(0)) {
      // montgomery inverse require even modulo
      return modInverseLorencz(a, p);
    }

    int m = p.numberLength * 32;
    // PRE: a \in [1, p - 1]
    BigInteger u, v, r, s;
    u = p.copy(); // make copy to use inplace method
    v = a.copy();
    int max = Math.max(v.numberLength, u.numberLength);
    r = new BigInteger(1, 1, new int[max + 1]);
    s = new BigInteger(1, 1, new int[max + 1]);
    s.digits[0] = 1;
    // s == 1 && v == 0

    int k = 0;

    int lsbu = u.getLowestSetBit();
    int lsbv = v.getLowestSetBit();
    int toShift;

    if (lsbu > lsbv) {
      BitLevel.inplaceShiftRight(u, lsbu);
      BitLevel.inplaceShiftRight(v, lsbv);
      BitLevel.inplaceShiftLeft(r, lsbv);
      k += lsbu - lsbv;
    } else {
      BitLevel.inplaceShiftRight(u, lsbu);
      BitLevel.inplaceShiftRight(v, lsbv);
      BitLevel.inplaceShiftLeft(s, lsbu);
      k += lsbv - lsbu;
    }

    r.sign = 1;
    while (v.signum() > 0) {
      // INV v >= 0, u >= 0, v odd, u odd (except last iteration when v is even
      // (0))

      while (u.compareTo(v) > BigInteger.EQUALS) {
        Elementary.inplaceSubtract(u, v);
        toShift = u.getLowestSetBit();
        BitLevel.inplaceShiftRight(u, toShift);
        Elementary.inplaceAdd(r, s);
        BitLevel.inplaceShiftLeft(s, toShift);
        k += toShift;
      }

      while (u.compareTo(v) <= BigInteger.EQUALS) {
        Elementary.inplaceSubtract(v, u);
        if (v.signum() == 0) {
          break;
        }
        toShift = v.getLowestSetBit();
        BitLevel.inplaceShiftRight(v, toShift);
        Elementary.inplaceAdd(s, r);
        BitLevel.inplaceShiftLeft(r, toShift);
        k += toShift;
      }
    }
    if (!u.isOne()) {
      // in u is stored the gcd
      // math.19: BigInteger not invertible.
      throw new ArithmeticException("BigInteger not invertible.");
    }
    if (r.compareTo(p) >= BigInteger.EQUALS) {
      Elementary.inplaceSubtract(r, p);
    }

    r = p.subtract(r);

    // Have pair: ((BigInteger)r, (Integer)k) where r == a^(-1) * 2^k mod
    // (module)
    int n1 = calcN(p);
    if (k > m) {
      r = monPro(r, BigInteger.ONE, p, n1);
      k = k - m;
    }

    r = monPro(r, BigInteger.getPowerOfTwo(m - k), p, n1);
    return r;
  }

  /**
   * @param x an odd positive number.
   * @param n the exponent by which 2 is raised.
   * @return {@code x<sup>-1</sup> (mod 2<sup>n</sup>)}.
   */
  static BigInteger modPow2Inverse(BigInteger x, int n) {
    // PRE: (x > 0), (x is odd), and (n > 0)
    BigInteger y = new BigInteger(1, new int[1 << n]);
    y.numberLength = 1;
    y.digits[0] = 1;
    y.sign = 1;

    for (int i = 1; i < n; i++) {
      if (BitLevel.testBit(x.multiply(y), i)) {
        // Adding 2^i to y (setting the i-th bit)
        y.digits[i >> 5] |= (1 << (i & 31));
      }
    }
    return y;
  }

  /**
   * Implements the Montgomery Product of two integers represented by {@code
   * int} arrays. The arrays are supposed in <i>little endian</i> notation.
   *
   * @param a The first factor of the product.
   * @param b The second factor of the product.
   * @param modulus The modulus of the operations. Z<sub>modulus</sub>.
   * @param n2 The digit modulus'[0].
   * @ar.org.fitc.ref "C. K. Koc - Analyzing and Comparing Montgomery
   *                  Multiplication Algorithms"
   * @see #modPowOdd(BigInteger, BigInteger, BigInteger)
   */
  static BigInteger monPro(BigInteger a, BigInteger b, BigInteger modulus,
      int n2) {
    int modulusLen = modulus.numberLength;
    int res[] = new int[(modulusLen << 1) + 1];
    Multiplication.multArraysPAP(a.digits,
        Math.min(modulusLen, a.numberLength), b.digits, Math.min(modulusLen,
            b.numberLength), res);
    monReduction(res, modulus, n2);
    return finalSubtraction(res, modulus);
  }

  /**
   * Multiplies an array by int and subtracts it from a subarray of another
   * array.
   *
   * @param a the array to subtract from
   * @param start the start element of the subarray of a
   * @param b the array to be multiplied and subtracted
   * @param bLen the length of b
   * @param c the multiplier of b
   * @return the carry element of subtraction
   */
  static int multiplyAndSubtract(int a[], int start, int b[], int bLen, int c) {
    long carry0 = 0;
    long carry1 = 0;

    for (int i = 0; i < bLen; i++) {
      carry0 = Multiplication.unsignedMultAddAdd(b[i], c, (int) carry0, 0);
      carry1 = (a[start + i] & 0xffffffffL) - (carry0 & 0xffffffffL) + carry1;
      a[start + i] = (int) carry1;
      carry1 >>= 32; // -1 or 0
      carry0 >>>= 32;
    }

    carry1 = (a[start + bLen] & 0xffffffffL) - carry0 + carry1;
    a[start + bLen] = (int) carry1;
    return (int) (carry1 >> 32); // -1 or 0
  }

  /**
   * Performs modular exponentiation using the Montgomery Reduction. It requires
   * that all parameters be positive and the modulus be odd. >
   *
   * @see BigInteger#modPow(BigInteger, BigInteger)
   * @see #monPro(BigInteger, BigInteger, BigInteger, int)
   * @see #slidingWindow(BigInteger, BigInteger, BigInteger, BigInteger, int)
   * @see #squareAndMultiply(BigInteger, BigInteger, BigInteger, BigInteger,
   *      int)
   */
  static BigInteger oddModPow(BigInteger base, BigInteger exponent,
      BigInteger modulus) {
    // PRE: (base > 0), (exponent > 0), (modulus > 0) and (odd modulus)
    int k = (modulus.numberLength << 5); // r = 2^k
    // n-residue of base [base * r (mod modulus)]
    BigInteger a2 = base.shiftLeft(k).mod(modulus);
    // n-residue of base [1 * r (mod modulus)]
    BigInteger x2 = BigInteger.getPowerOfTwo(k).mod(modulus);
    BigInteger res;
    // Compute (modulus[0]^(-1)) (mod 2^32) for odd modulus

    int n2 = calcN(modulus);
    if (modulus.numberLength == 1) {
      res = squareAndMultiply(x2, a2, exponent, modulus, n2);
    } else {
      res = slidingWindow(x2, a2, exponent, modulus, n2);
    }

    return monPro(res, BigInteger.ONE, modulus, n2);
  }

  /**
   * It requires that all parameters be positive.
   *
   * @return {@code base<sup>exponent</sup> mod (2<sup>j</sup>)}.
   * @see BigInteger#modPow(BigInteger, BigInteger)
   */
  static BigInteger pow2ModPow(BigInteger base, BigInteger exponent, int j) {
    // PRE: (base > 0), (exponent > 0) and (j > 0)
    BigInteger res = BigInteger.ONE;
    BigInteger e = exponent.copy();
    BigInteger baseMod2toN = base.copy();
    BigInteger res2;
    /*
     * If 'base' is odd then it's coprime with 2^j and phi(2^j) = 2^(j-1); so we
     * can reduce reduce the exponent (mod 2^(j-1)).
     */
    if (base.testBit(0)) {
      inplaceModPow2(e, j - 1);
    }
    inplaceModPow2(baseMod2toN, j);

    for (int i = e.bitLength() - 1; i >= 0; i--) {
      res2 = res.copy();
      inplaceModPow2(res2, j);
      res = res.multiply(res2);
      if (BitLevel.testBit(e, i)) {
        res = res.multiply(baseMod2toN);
        inplaceModPow2(res, j);
      }
    }
    inplaceModPow2(res, j);
    return res;
  }

  /**
   * Divides a <code>BigInteger</code> by a signed <code>int</code> and returns
   * the remainder.
   *
   * @param dividend the BigInteger to be divided. Must be non-negative.
   * @param divisor a signed int
   * @return divide % divisor
   */
  static int remainder(BigInteger dividend, int divisor) {
    return remainderArrayByInt(dividend.digits, dividend.numberLength, divisor);
  }

  /**
   * Divides an array by an integer value. Implements the Knuth's division
   * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2.
   *
   * @param src the dividend
   * @param srcLength the length of the dividend
   * @param divisor the divisor
   * @return remainder
   */
  static int remainderArrayByInt(int src[], final int srcLength,
      final int divisor) {

    long result = 0;

    for (int i = srcLength - 1; i >= 0; i--) {
      long temp = (result << 32) + (src[i] & 0xffffffffL);
      long res = divideLongByInt(temp, divisor);
      result = (int) (res >> 32);
    }
    return (int) result;
  }

  /*
   * Implements the Montgomery modular exponentiation based in <i>The sliding
   * windows algorithm and the MongomeryReduction</i>.
   *
   * @ar.org.fitc.ref
   * "A. Menezes,P. van Oorschot, S. Vanstone - Handbook of Applied Cryptography"
   * ;
   *
   * @see #oddModPow(BigInteger, BigInteger, BigInteger)
   */
  static BigInteger slidingWindow(BigInteger x2, BigInteger a2,
      BigInteger exponent, BigInteger modulus, int n2) {
    // fill odd low pows of a2
    BigInteger pows[] = new BigInteger[8];
    BigInteger res = x2;
    int lowexp;
    BigInteger x3;
    int acc3;
    pows[0] = a2;

    x3 = monPro(a2, a2, modulus, n2);
    for (int i = 1; i <= 7; i++) {
      pows[i] = monPro(pows[i - 1], x3, modulus, n2);
    }

    for (int i = exponent.bitLength() - 1; i >= 0; i--) {
      if (BitLevel.testBit(exponent, i)) {
        lowexp = 1;
        acc3 = i;

        for (int j = Math.max(i - 3, 0); j <= i - 1; j++) {
          if (BitLevel.testBit(exponent, j)) {
            if (j < acc3) {
              acc3 = j;
              lowexp = (lowexp << (i - j)) ^ 1;
            } else {
              lowexp = lowexp ^ (1 << (j - acc3));
            }
          }
        }

        for (int j = acc3; j <= i; j++) {
          res = monPro(res, res, modulus, n2);
        }
        res = monPro(pows[(lowexp - 1) >> 1], res, modulus, n2);
        i = acc3;
      } else {
        res = monPro(res, res, modulus, n2);
      }
    }
    return res;
  }

  static BigInteger squareAndMultiply(BigInteger x2, BigInteger a2,
      BigInteger exponent, BigInteger modulus, int n2) {
    BigInteger res = x2;
    for (int i = exponent.bitLength() - 1; i >= 0; i--) {
      res = monPro(res, res, modulus, n2);
      if (BitLevel.testBit(exponent, i)) {
        res = monPro(res, a2, modulus, n2);
      }
    }
    return res;
  }

  /**
   * Calculate the first digit of the inverse.
   */
  private static int calcN(BigInteger a) {
    long m0 = a.digits[0] & 0xFFFFFFFFL;
    long n2 = 1L; // this is a'[0]
    long powerOfTwo = 2L;
    do {
      if (((m0 * n2) & powerOfTwo) != 0) {
        n2 |= powerOfTwo;
      }
      powerOfTwo <<= 1;
    } while (powerOfTwo < 0x100000000L);
    n2 = -n2;
    return (int) (n2 & 0xFFFFFFFFL);
  }

  /**
   * Calculate how many iteration of Lorencz's algorithm would perform the same
   * operation.
   *
   * @param bi
   * @param n
   * @return
   */
  private static int howManyIterations(BigInteger bi, int n) {
    int i = n - 1;
    if (bi.sign > 0) {
      while (!bi.testBit(i)) {
        i--;
      }
      return n - 1 - i;
    } else {
      while (bi.testBit(i)) {
        i--;
      }
      return n - 1 - Math.max(i, bi.getLowestSetBit());
    }
  }

  /**
   * Returns {@code bi == abs(2^exp)}.
   */
  private static boolean isPowerOfTwo(BigInteger bi, int exp) {
    boolean result = false;
    result = (exp >> 5 == bi.numberLength - 1)
        && (bi.digits[bi.numberLength - 1] == 1 << (exp & 31));
    if (result) {
      for (int i = 0; result && i < bi.numberLength - 1; i++) {
        result = bi.digits[i] == 0;
      }
    }
    return result;
  }

  private static void monReduction(int[] res, BigInteger modulus, int n2) {

    /* res + m*modulus_digits */
    int[] modulusDigits = modulus.digits;
    int modulusLen = modulus.numberLength;
    long outerCarry = 0;

    for (int i = 0; i < modulusLen; i++) {
      long innnerCarry = 0;
      int m = (int) Multiplication.unsignedMultAddAdd(res[i], n2, 0, 0);
      for (int j = 0; j < modulusLen; j++) {
        innnerCarry = Multiplication.unsignedMultAddAdd(m, modulusDigits[j],
            res[i + j], (int) innnerCarry);
        res[i + j] = (int) innnerCarry;
        innnerCarry >>>= 32;
      }

      outerCarry += (res[i + modulusLen] & 0xFFFFFFFFL) + innnerCarry;
      res[i + modulusLen] = (int) outerCarry;
      outerCarry >>>= 32;
    }

    res[modulusLen << 1] = (int) outerCarry;

    /* res / r */
    for (int j = 0; j < modulusLen + 1; j++) {
      res[j] = res[j + modulusLen];
    }
  }
}
