/// <summary> /// Returns the length of a message after encryption with this parameter set /// <para>The length does not depend on the input size.</para> /// </summary> /// /// <returns>The length in bytes</returns> public int GetOutputLength() { // ceil(log q) int logq = 32 - IntUtils.NumberOfLeadingZeros(Q - 1); return((N * logq + 7) / 8); }
/// <summary> /// Like EncodeModQ(int[], int) but only returns the first <c>NumBytes</c> bytes of the encoding /// </summary> /// /// <param name="Data">The input array</param> /// <param name="Q">The modulus</param> /// <param name="NumBytes">The encoded array</param> /// /// <returns>Returns T</returns> public static byte[] EncodeModQTrunc(int[] Data, int Q, int NumBytes) { int bitsPerCoeff = 31 - IntUtils.NumberOfLeadingZeros(Q); byte[] data = new byte[NumBytes]; int bitIndex = 0; int byteIndex = 0; for (int i = 0; i < Data.Length; i++) { for (int j = 0; j < bitsPerCoeff; j++) { int currentBit = (Data[i] >> j) & 1; data[byteIndex] |= (byte)(currentBit << bitIndex); if (bitIndex == 7) { bitIndex = 0; byteIndex++; if (byteIndex >= NumBytes) { return(data); } } else { bitIndex++; } } } return(null); }
/// <summary> /// Encodes an int array whose elements are between 0 and <c>Q</c>, to a byte array leaving no gaps between bits. /// <para><c>Q</c> must be a power of 2.</para> /// </summary> /// /// <param name="A">The input array</param> /// <param name="Q">The modulus</param> /// /// <returns>The encoded array</returns> public static byte[] EncodeModQ(int[] A, int Q) { int bitsPerCoeff = 31 - IntUtils.NumberOfLeadingZeros(Q); int numBits = A.Length * bitsPerCoeff; int numBytes = (numBits + 7) / 8; byte[] data = new byte[numBytes]; int bitIndex = 0; int byteIndex = 0; for (int i = 0; i < A.Length; i++) { for (int j = 0; j < bitsPerCoeff; j++) { int currentBit = (A[i] >> j) & 1; data[byteIndex] |= (byte)(currentBit << bitIndex); if (bitIndex == 7) { bitIndex = 0; byteIndex++; } else { bitIndex++; } } } return(data); }
/// <summary> /// Decodes a <c>byte</c> array encoded with EncodeModQ(int[], int)} back to an <c>int</c> array. /// <para><c>N</c> is the number of coefficients. <c>Q</c> must be a power of <c>2</c>. /// Ignores any excess bytes.</para> /// </summary> /// /// <param name="Data">Data an encoded ternary polynomial</param> /// <param name="N">The number of coefficients</param> /// <param name="Q">The modulus</param> /// /// <returns>Returns an array containing <c>N</c> coefficients between <c>0</c> and <c>q-1</c></returns> public static int[] DecodeModQ(byte[] Data, int N, int Q) { int[] coeffs = new int[N]; int bitsPerCoeff = 31 - IntUtils.NumberOfLeadingZeros(Q); int mask = IntUtils.URShift(-1, (32 - bitsPerCoeff)); // for truncating values to bitsPerCoeff bits int byteIndex = 0; int bitIndex = 0; // next bit in data[byteIndex] int coeffBuf = 0; // contains (bitIndex) bits int coeffBits = 0; // length of coeffBuf int coeffIndex = 0; // index into coeffs while (coeffIndex < N) { // copy bitsPerCoeff or more into coeffBuf while (coeffBits < bitsPerCoeff) { coeffBuf += (Data[byteIndex] & 0xFF) << coeffBits; coeffBits += 8 - bitIndex; byteIndex++; bitIndex = 0; } // low bitsPerCoeff bits = next coefficient coeffs[coeffIndex] = coeffBuf & mask; coeffIndex++; coeffBuf = IntUtils.URShift(coeffBuf, bitsPerCoeff); coeffBits -= bitsPerCoeff; } return(coeffs); }
/// <summary> /// Performs val <= count, val should have enough place (and one digit more) /// </summary> /// /// <param name="Value">The source BigIntger</param> /// <param name="N">Shift distance</param> internal static void InplaceShiftLeft(BigInteger Value, int N) { int intCount = N >> 5; // count of integers Value._numberLength += intCount + (IntUtils.NumberOfLeadingZeros(Value._digits[Value._numberLength - 1]) - (N & 31) >= 0 ? 0 : 1); ShiftLeft(Value._digits, Value._digits, intCount, N & 31); Value.CutOffLeadingZeroes(); Value.UnCache(); }
/// <summary> /// Decodes data encoded with encodeModQ(int[], int) back to an <c>int</c> array. /// <para><c>N</c> is the number of coefficients. <c>q</c> must be a power of <c>2</c>. /// Ignores any excess bytes.</para> /// </summary> /// /// <param name="InputStream">An encoded ternary polynomial</param> /// <param name="N">The number of coefficients</param> /// <param name="Q">The modulus</param> /// /// <returns>The decoded polynomial</returns> public static int[] DecodeModQ(Stream InputStream, int N, int Q) { int qBits = 31 - IntUtils.NumberOfLeadingZeros(Q); int size = (N * qBits + 7) / 8; byte[] arr = ArrayEncoder.ReadFullLength(InputStream, size); return(DecodeModQ(arr, N, Q)); }
/// <summary> /// Decodes a polynomial encoded with ToBinary() /// </summary> /// /// <param name="InputStream">An input stream containing an encoded polynomial</param> /// <param name="N">Number of coefficients in the polynomial</param> /// /// <returns>The decoded polynomial</returns> public static SparseTernaryPolynomial FromBinary(MemoryStream InputStream, int N) { BinaryReader br = new BinaryReader(InputStream); // number of coefficients equal to 1 int numOnes = IntUtils.ReadShort(InputStream); // number of coefficients equal to -1 int numNegOnes = IntUtils.ReadShort(InputStream); int maxIndex = 1 << BITS_PER_INDEX; int bitsPerIndex = 32 - IntUtils.NumberOfLeadingZeros(maxIndex - 1); int data1Len = (numOnes * bitsPerIndex + 7) / 8; byte[] data1 = ArrayEncoder.ReadFullLength(InputStream, data1Len); int[] ones = ArrayEncoder.DecodeModQ(data1, numOnes, maxIndex); int data2Len = (numNegOnes * bitsPerIndex + 7) / 8; byte[] data2 = ArrayEncoder.ReadFullLength(InputStream, data2Len); int[] negOnes = ArrayEncoder.DecodeModQ(data2, numNegOnes, maxIndex); return(new SparseTernaryPolynomial(N, ones, negOnes)); }
/// <summary> /// Returns the length of the value's two's complement representation without /// leading zeros for positive numbers / without leading ones for negative values. /// <para>The two's complement representation of this will be at least BitLength() + 1 bits long. /// The value will fit into an int if <c>bitLength() < 32</c> or into a long if <c>bitLength() < 64</c>.</para> /// </summary> internal static int BitLength(BigInteger Value) { if (Value._sign == 0) { return(0); } int bLength = (Value._numberLength << 5); int highDigit = Value._digits[Value._numberLength - 1]; if (Value._sign < 0) { int i = Value.FirstNonzeroDigit; // We reduce the problem to the positive case. if (i == Value._numberLength - 1) { highDigit--; } } // Subtracting all sign bits bLength -= IntUtils.NumberOfLeadingZeros(highDigit); return(bLength); }
/// <remarks> /// <para>Schönhage-Strassen is used unless the numbers are in a range where /// <a href="http://en.wikipedia.org/wiki/Karatsuba_algorithm">Karatsuba</a> is more efficient.</para> /// The Schönhage-Strassen algorithm works as follows: /// Given numbers a and b, split both numbers into pieces of length 2^(n-1) bits. /// Take the low n+2 bits of each piece of a, zero-pad them to 3n+5 bits, and concatenate them to a new number u. /// Do the same for b to obtain v. /// Calculate all pieces of z' by multiplying u and v (using Schönhage-Strassen or another algorithm). The product will contain all pieces of a*b mod n+2. /// Pad the pieces of a and b from step 1 to 2^(n+1) bits. /// Perform a <a href="http://en.wikipedia.org/wiki/Discrete_Fourier_transform_%28general%29#Number-theoretic_transform"> /// Discrete Fourier Transform</a> (DFT) on the padded pieces. /// Calculate all pieces of z" by multiplying the i-th piece of a by the i-th piece of b. /// Perform an Inverse Discrete Fourier Transform (IDFT) on z". z" will contain all pieces of a*b mod Fn where Fn=2^2^n+1. /// Calculate all pieces of z such that each piece is congruent to z' modulo n+2 and congruent to z" modulo Fn. This is done using the /// <a href="http://en.wikipedia.org/wiki/Chinese_remainder_theorem">Chinese remainder theorem</a>. /// Calculate c by adding z_i * 2^(i*2^(n-1)) for all i, where z_i is the i-th piece of z. /// Return c reduced modulo 2^2^m+1. /// </remarks> private static int[] Multiply(int[] A, int ABitLen, int[] B, int BBitLen) { if (!ShouldUseSchonhageStrassen(Math.Max(ABitLen, BBitLen))) { return(MultKaratsuba(A, B)); } // set M to the number of binary digits in a or b, whichever is greater int M = Math.Max(ABitLen, BBitLen); // find the lowest m such that m>=log2(2M) int m = 32 - IntUtils.NumberOfLeadingZeros(2 * M - 1 - 1); int n = m / 2 + 1; // split a and b into pieces 1<<(n-1) bits long; assume n>=6 so pieces start and end at int boundaries bool even = m % 2 == 0; int numPieces = even ? 1 << n : 1 << (n + 1); int pieceSize = 1 << (n - 1 - 5); // in ints // build u and v from a and b, allocating 3n+5 bits in u and v per n+2 bits from a and b, resp. int numPiecesA = (A.Length + pieceSize) / pieceSize; int[] u = new int[(numPiecesA * (3 * n + 5) + 31) / 32]; int uBitLength = 0; for (int i = 0; i < numPiecesA && i * pieceSize < A.Length; i++) { AppendBits(u, uBitLength, A, i * pieceSize, n + 2); uBitLength += 3 * n + 5; } int numPiecesB = (B.Length + pieceSize) / pieceSize; int[] v = new int[(numPiecesB * (3 * n + 5) + 31) / 32]; int vBitLength = 0; for (int i = 0; i < numPiecesB && i * pieceSize < B.Length; i++) { AppendBits(v, vBitLength, B, i * pieceSize, n + 2); vBitLength += 3 * n + 5; } int[] gamma = Multiply(u, uBitLength, v, vBitLength); int[][] gammai = SplitBits(gamma, 3 * n + 5); int halfNumPcs = numPieces / 2; int[][] zi = new int[gammai.Length][]; for (int i = 0; i < gammai.Length; i++) { zi[i] = gammai[i]; } for (int i = 0; i < gammai.Length - halfNumPcs; i++) { SubModPow2(zi[i], gammai[i + halfNumPcs], n + 2); } for (int i = 0; i < gammai.Length - 2 * halfNumPcs; i++) { AddModPow2(zi[i], gammai[i + 2 * halfNumPcs], n + 2); } for (int i = 0; i < gammai.Length - 3 * halfNumPcs; i++) { SubModPow2(zi[i], gammai[i + 3 * halfNumPcs], n + 2); } // zr mod Fn int[][] ai = SplitInts(A, halfNumPcs, pieceSize, 1 << (n + 1 - 5)); int[][] bi = SplitInts(B, halfNumPcs, pieceSize, 1 << (n + 1 - 5)); Dft(ai, m, n); Dft(bi, m, n); ModFn(ai); ModFn(bi); int[][] c = new int[halfNumPcs][]; for (int i = 0; i < c.Length; i++) { c[i] = MultModFn(ai[i], bi[i]); } Idft(c, m, n); ModFn(c); int[] z = new int[1 << (m + 1 - 5)]; // calculate zr mod Fm from zr mod Fn and zr mod 2^(n+2), then add to z for (int i = 0; i < halfNumPcs; i++) { int[] eta = i >= zi.Length ? new int[(n + 2 + 31) / 32] : zi[i]; // zi = delta = (zi-c[i]) % 2^(n+2) SubModPow2(eta, c[i], n + 2); // z += zr<<shift = [ci + delta*(2^2^n+1)] << [i*2^(n-1)] int shift = i * (1 << (n - 1 - 5)); // assume n>=6 AddShifted(z, c[i], shift); AddShifted(z, eta, shift); AddShifted(z, eta, shift + (1 << (n - 5))); } // assume m>=5 ModFn(z); return(z); }
/// <summary> /// Multiplies the polynomial by another, taking the indices mod N. /// <para>Does not change this polynomial but returns the result as a new polynomial. /// Both polynomials must have the same number of coefficients. /// This method is designed for large polynomials and uses Schönhage-Strassen multiplication /// in combination with <a href="http://en.wikipedia.org/wiki/Kronecker_substitution">Kronecker substitution</a>. /// See <a href="http://math.stackexchange.com/questions/58946/karatsuba-vs-schonhage-strassen-for-multiplication-of-polynomials#58955">here</a> for details.</para> /// </summary> /// /// <param name="Factor">The polynomial to multiply by</param> /// /// <returns>The product polynomial</returns> public BigIntPolynomial MultBig(BigIntPolynomial Factor) { int N = Coeffs.Length; // determine #bits needed per coefficient int logMinDigits = 32 - IntUtils.NumberOfLeadingZeros(N - 1); int maxLengthA = 0; for (int i = 0; i < Coeffs.Length; i++) { BigInteger coeff = Coeffs[i]; maxLengthA = Math.Max(maxLengthA, coeff.BitLength); } int maxLengthB = 0; for (int i = 0; i < Factor.Coeffs.Length; i++) { BigInteger coeff = Factor.Coeffs[i]; maxLengthB = Math.Max(maxLengthB, coeff.BitLength); } int k = logMinDigits + maxLengthA + maxLengthB + 1; // in bits k = (k + 31) / 32; // in ints // encode each polynomial into an int[] int aDeg = Degree(); int bDeg = Factor.Degree(); if (aDeg < 0 || bDeg < 0) { return(new BigIntPolynomial(N)); // return zero } int[] aInt = ToIntArray(this, k); int[] bInt = ToIntArray(Factor, k); int[] cInt = SchonhageStrassen.Multiply(aInt, bInt); // decode poly coefficients from the product BigInteger _2k = BigInteger.One.ShiftLeft(k * 32); BigIntPolynomial cPoly = new BigIntPolynomial(N); for (int i = 0; i < 2 * N - 1; i++) { int[] coeffInt = cInt.CopyOfRange(i * k, (i + 1) * k); BigInteger coeff = SchonhageStrassen.ToBigInteger(coeffInt); if (coeffInt[k - 1] < 0) { // if coeff > 2^(k-1) coeff = coeff.Subtract(_2k); // add 2^k to cInt which is the same as subtracting coeff bool carry = false; int cIdx = (i + 1) * k; do { cInt[cIdx]++; carry = cInt[cIdx] == 0; cIdx++; } while (carry); } cPoly.Coeffs[i % N] = cPoly.Coeffs[i % N].Add(coeff); } int aSign = Coeffs[aDeg].Signum(); int bSign = Factor.Coeffs[bDeg].Signum(); if (aSign * bSign < 0) { for (int i = 0; i < N; i++) { cPoly.Coeffs[i] = cPoly.Coeffs[i].Negate(); } } return(cPoly); }
/// <summary> /// Divides the array 'a' by the array 'b' and gets the quotient and the remainder. /// <para>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.</para> /// </summary> /// /// <param name="Quotient">The quotient</param> /// <param name="QuotientLen">The quotient's length</param> /// <param name="X">The dividend</param> /// <param name="XLen">The dividend's length</param> /// <param name="Y">The divisor</param> /// <param name="YLength">The divisor's length</param> /// /// <returns>eturn the remainder</returns> internal static int[] Divide(int[] Quotient, int QuotientLen, int[] X, int XLen, int[] Y, int YLength) { int[] normA = new int[XLen + 1]; // the normalized dividend // an extra byte is needed for correct shift int[] normB = new int[YLength + 1]; // the normalized divisor; int normBLength = YLength; // 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 = IntUtils.NumberOfLeadingZeros(Y[YLength - 1]); if (divisorShift != 0) { BitLevel.ShiftLeft(normB, Y, 0, divisorShift); BitLevel.ShiftLeft(normA, X, 0, divisorShift); } else { Array.Copy(X, 0, normA, 0, XLen); Array.Copy(Y, 0, normB, 0, YLength); } int firstDivisorDigit = normB[normBLength - 1]; // Step D2: set the quotient index int i = QuotientLen - 1; int j = XLen; 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; bool 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 (IntUtils.NumberOfLeadingZeros((int)IntUtils.URShift(longR, 32)) < 32) { rOverflowed = true; } else { rem = (int)longR; } } while ((leftHand ^ long.MinValue) > (rightHand ^ long.MinValue)); } } // 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 = IntUtils.URShift(carry, 32); } } } if (Quotient != null) { Quotient[i] = guessDigit; } // Step D7 j--; i--; } // Step D8: we got the remainder in normA. Denormalize it as needed if (divisorShift != 0) { // reuse normB BitLevel.ShiftRight(normB, normBLength, normA, 0, divisorShift); return(normB); } Array.Copy(normA, 0, normB, 0, YLength); return(normA); }