package org.bouncycastle.pqc.math.ntru.polynomial;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.pqc.math.ntru.util.ArrayEncoder;
import org.bouncycastle.pqc.math.ntru.util.Util;
import org.bouncycastle.util.Arrays;
A TernaryPolynomial
with a "low" number of nonzero coefficients.
/**
* A <code>TernaryPolynomial</code> with a "low" number of nonzero coefficients.
*/
public class SparseTernaryPolynomial
implements TernaryPolynomial
{
Number of bits to use for each coefficient. Determines the upper bound for N
.
/**
* Number of bits to use for each coefficient. Determines the upper bound for <code>N</code>.
*/
private static final int BITS_PER_INDEX = 11;
private int N;
private int[] ones;
private int[] negOnes;
Constructs a new polynomial.
Params: - N – total number of coefficients including zeros
- ones – indices of coefficients equal to 1
- negOnes – indices of coefficients equal to -1
/**
* Constructs a new polynomial.
*
* @param N total number of coefficients including zeros
* @param ones indices of coefficients equal to 1
* @param negOnes indices of coefficients equal to -1
*/
SparseTernaryPolynomial(int N, int[] ones, int[] negOnes)
{
this.N = N;
this.ones = ones;
this.negOnes = negOnes;
}
Constructs a DenseTernaryPolynomial
from a IntegerPolynomial
. The two polynomials are
independent of each other.
Params: - intPoly – the original polynomial
/**
* Constructs a <code>DenseTernaryPolynomial</code> from a <code>IntegerPolynomial</code>. The two polynomials are
* independent of each other.
*
* @param intPoly the original polynomial
*/
public SparseTernaryPolynomial(IntegerPolynomial intPoly)
{
this(intPoly.coeffs);
}
Constructs a new SparseTernaryPolynomial
with a given set of coefficients.
Params: - coeffs – the coefficients
/**
* Constructs a new <code>SparseTernaryPolynomial</code> with a given set of coefficients.
*
* @param coeffs the coefficients
*/
public SparseTernaryPolynomial(int[] coeffs)
{
N = coeffs.length;
ones = new int[N];
negOnes = new int[N];
int onesIdx = 0;
int negOnesIdx = 0;
for (int i = 0; i < N; i++)
{
int c = coeffs[i];
switch (c)
{
case 1:
ones[onesIdx++] = i;
break;
case -1:
negOnes[negOnesIdx++] = i;
break;
case 0:
break;
default:
throw new IllegalArgumentException("Illegal value: " + c + ", must be one of {-1, 0, 1}");
}
}
ones = Arrays.copyOf(ones, onesIdx);
negOnes = Arrays.copyOf(negOnes, negOnesIdx);
}
Decodes a byte array encoded with toBinary()
to a ploynomial. Params: - is – an input stream containing an encoded polynomial
- N – number of coefficients including zeros
- numOnes – number of coefficients equal to 1
- numNegOnes – number of coefficients equal to -1
Throws: Returns: the decoded polynomial
/**
* Decodes a byte array encoded with {@link #toBinary()} to a ploynomial.
*
* @param is an input stream containing an encoded polynomial
* @param N number of coefficients including zeros
* @param numOnes number of coefficients equal to 1
* @param numNegOnes number of coefficients equal to -1
* @return the decoded polynomial
* @throws IOException
*/
public static SparseTernaryPolynomial fromBinary(InputStream is, int N, int numOnes, int numNegOnes)
throws IOException
{
int maxIndex = 1 << BITS_PER_INDEX;
int bitsPerIndex = 32 - Integer.numberOfLeadingZeros(maxIndex - 1);
int data1Len = (numOnes * bitsPerIndex + 7) / 8;
byte[] data1 = Util.readFullLength(is, data1Len);
int[] ones = ArrayEncoder.decodeModQ(data1, numOnes, maxIndex);
int data2Len = (numNegOnes * bitsPerIndex + 7) / 8;
byte[] data2 = Util.readFullLength(is, data2Len);
int[] negOnes = ArrayEncoder.decodeModQ(data2, numNegOnes, maxIndex);
return new SparseTernaryPolynomial(N, ones, negOnes);
}
Generates a random polynomial with numOnes
coefficients equal to 1,
numNegOnes
coefficients equal to -1, and the rest equal to 0.
Params: - N – number of coefficients
- numOnes – number of 1's
- numNegOnes – number of -1's
/**
* Generates a random polynomial with <code>numOnes</code> coefficients equal to 1,
* <code>numNegOnes</code> coefficients equal to -1, and the rest equal to 0.
*
* @param N number of coefficients
* @param numOnes number of 1's
* @param numNegOnes number of -1's
*/
public static SparseTernaryPolynomial generateRandom(int N, int numOnes, int numNegOnes, SecureRandom random)
{
int[] coeffs = Util.generateRandomTernary(N, numOnes, numNegOnes, random);
return new SparseTernaryPolynomial(coeffs);
}
public IntegerPolynomial mult(IntegerPolynomial poly2)
{
int[] b = poly2.coeffs;
if (b.length != N)
{
throw new IllegalArgumentException("Number of coefficients must be the same");
}
int[] c = new int[N];
for (int idx = 0; idx != ones.length; idx++)
{
int i = ones[idx];
int j = N - 1 - i;
for (int k = N - 1; k >= 0; k--)
{
c[k] += b[j];
j--;
if (j < 0)
{
j = N - 1;
}
}
}
for (int idx = 0; idx != negOnes.length; idx++)
{
int i = negOnes[idx];
int j = N - 1 - i;
for (int k = N - 1; k >= 0; k--)
{
c[k] -= b[j];
j--;
if (j < 0)
{
j = N - 1;
}
}
}
return new IntegerPolynomial(c);
}
public IntegerPolynomial mult(IntegerPolynomial poly2, int modulus)
{
IntegerPolynomial c = mult(poly2);
c.mod(modulus);
return c;
}
public BigIntPolynomial mult(BigIntPolynomial poly2)
{
BigInteger[] b = poly2.coeffs;
if (b.length != N)
{
throw new IllegalArgumentException("Number of coefficients must be the same");
}
BigInteger[] c = new BigInteger[N];
for (int i = 0; i < N; i++)
{
c[i] = BigInteger.ZERO;
}
for (int idx = 0; idx != ones.length; idx++)
{
int i = ones[idx];
int j = N - 1 - i;
for (int k = N - 1; k >= 0; k--)
{
c[k] = c[k].add(b[j]);
j--;
if (j < 0)
{
j = N - 1;
}
}
}
for (int idx = 0; idx != negOnes.length; idx++)
{
int i = negOnes[idx];
int j = N - 1 - i;
for (int k = N - 1; k >= 0; k--)
{
c[k] = c[k].subtract(b[j]);
j--;
if (j < 0)
{
j = N - 1;
}
}
}
return new BigIntPolynomial(c);
}
public int[] getOnes()
{
return ones;
}
public int[] getNegOnes()
{
return negOnes;
}
Encodes the polynomial to a byte array writing BITS_PER_INDEX
bits for each coefficient.
Returns: the encoded polynomial
/**
* Encodes the polynomial to a byte array writing <code>BITS_PER_INDEX</code> bits for each coefficient.
*
* @return the encoded polynomial
*/
public byte[] toBinary()
{
int maxIndex = 1 << BITS_PER_INDEX;
byte[] bin1 = ArrayEncoder.encodeModQ(ones, maxIndex);
byte[] bin2 = ArrayEncoder.encodeModQ(negOnes, maxIndex);
byte[] bin = Arrays.copyOf(bin1, bin1.length + bin2.length);
System.arraycopy(bin2, 0, bin, bin1.length, bin2.length);
return bin;
}
public IntegerPolynomial toIntegerPolynomial()
{
int[] coeffs = new int[N];
for (int idx = 0; idx != ones.length; idx++)
{
int i = ones[idx];
coeffs[i] = 1;
}
for (int idx = 0; idx != negOnes.length; idx++)
{
int i = negOnes[idx];
coeffs[i] = -1;
}
return new IntegerPolynomial(coeffs);
}
public int size()
{
return N;
}
public void clear()
{
for (int i = 0; i < ones.length; i++)
{
ones[i] = 0;
}
for (int i = 0; i < negOnes.length; i++)
{
negOnes[i] = 0;
}
}
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + N;
result = prime * result + Arrays.hashCode(negOnes);
result = prime * result + Arrays.hashCode(ones);
return result;
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
SparseTernaryPolynomial other = (SparseTernaryPolynomial)obj;
if (N != other.N)
{
return false;
}
if (!Arrays.areEqual(negOnes, other.negOnes))
{
return false;
}
if (!Arrays.areEqual(ones, other.ones))
{
return false;
}
return true;
}
}