package org.bouncycastle.pqc.math.linearalgebra; /** * This class implements polynomials over GF2nElements. * * @see GF2nElement */ public class GF2nPolynomial { private GF2nElement[] coeff; // keeps the coefficients of this polynomial private int size; // the size of this polynomial /** * Creates a new PolynomialGF2n of size <i>deg</i> and elem as * coefficients. * * @param deg - * the maximum degree + 1 * @param elem - * a GF2nElement */ public GF2nPolynomial(int deg, GF2nElement elem) { size = deg; coeff = new GF2nElement[size]; for (int i = 0; i < size; i++) { coeff[i] = (GF2nElement)elem.clone(); } } /** * Creates a new PolynomialGF2n of size <i>deg</i>. * * @param deg the maximum degree + 1 */ private GF2nPolynomial(int deg) { size = deg; coeff = new GF2nElement[size]; } /** * Creates a new PolynomialGF2n by cloning the given PolynomialGF2n <i>a</i>. * * @param a the PolynomialGF2n to clone */ public GF2nPolynomial(GF2nPolynomial a) { int i; coeff = new GF2nElement[a.size]; size = a.size; for (i = 0; i < size; i++) { coeff[i] = (GF2nElement)a.coeff[i].clone(); } } /** * Creates a new PolynomialGF2n from the given Bitstring <i>polynomial</i> * over the GF2nField <i>B1</i>. * * @param polynomial the Bitstring to use * @param B1 the field */ public GF2nPolynomial(GF2Polynomial polynomial, GF2nField B1) { size = B1.getDegree() + 1; coeff = new GF2nElement[size]; int i; if (B1 instanceof GF2nONBField) { for (i = 0; i < size; i++) { if (polynomial.testBit(i)) { coeff[i] = GF2nONBElement.ONE((GF2nONBField)B1); } else { coeff[i] = GF2nONBElement.ZERO((GF2nONBField)B1); } } } else if (B1 instanceof GF2nPolynomialField) { for (i = 0; i < size; i++) { if (polynomial.testBit(i)) { coeff[i] = GF2nPolynomialElement .ONE((GF2nPolynomialField)B1); } else { coeff[i] = GF2nPolynomialElement .ZERO((GF2nPolynomialField)B1); } } } else { throw new IllegalArgumentException( "PolynomialGF2n(Bitstring, GF2nField): B1 must be " + "an instance of GF2nONBField or GF2nPolynomialField!"); } } public final void assignZeroToElements() { int i; for (i = 0; i < size; i++) { coeff[i].assignZero(); } } /** * Returns the size (=maximum degree + 1) of this PolynomialGF2n. This is * not the degree, use getDegree instead. * * @return the size (=maximum degree + 1) of this PolynomialGF2n. */ public final int size() { return size; } /** * Returns the degree of this PolynomialGF2n. * * @return the degree of this PolynomialGF2n. */ public final int getDegree() { int i; for (i = size - 1; i >= 0; i--) { if (!coeff[i].isZero()) { return i; } } return -1; } /** * Enlarges the size of this PolynomialGF2n to <i>k</i> + 1. * * @param k the new maximum degree */ public final void enlarge(int k) { if (k <= size) { return; } int i; GF2nElement[] res = new GF2nElement[k]; System.arraycopy(coeff, 0, res, 0, size); GF2nField f = coeff[0].getField(); if (coeff[0] instanceof GF2nPolynomialElement) { for (i = size; i < k; i++) { res[i] = GF2nPolynomialElement.ZERO((GF2nPolynomialField)f); } } else if (coeff[0] instanceof GF2nONBElement) { for (i = size; i < k; i++) { res[i] = GF2nONBElement.ZERO((GF2nONBField)f); } } size = k; coeff = res; } public final void shrink() { int i = size - 1; while (coeff[i].isZero() && (i > 0)) { i--; } i++; if (i < size) { GF2nElement[] res = new GF2nElement[i]; System.arraycopy(coeff, 0, res, 0, i); coeff = res; size = i; } } /** * Sets the coefficient at <i>index</i> to <i>elem</i>. * * @param index the index * @param elem the GF2nElement to store as coefficient <i>index</i> */ public final void set(int index, GF2nElement elem) { if (!(elem instanceof GF2nPolynomialElement) && !(elem instanceof GF2nONBElement)) { throw new IllegalArgumentException( "PolynomialGF2n.set f must be an " + "instance of either GF2nPolynomialElement or GF2nONBElement!"); } coeff[index] = (GF2nElement)elem.clone(); } /** * Returns the coefficient at <i>index</i>. * * @param index the index * @return the GF2nElement stored as coefficient <i>index</i> */ public final GF2nElement at(int index) { return coeff[index]; } /** * Returns true if all coefficients equal zero. * * @return true if all coefficients equal zero. */ public final boolean isZero() { int i; for (i = 0; i < size; i++) { if (coeff[i] != null) { if (!coeff[i].isZero()) { return false; } } } return true; } public final boolean equals(Object other) { if (other == null || !(other instanceof GF2nPolynomial)) { return false; } GF2nPolynomial otherPol = (GF2nPolynomial)other; if (getDegree() != otherPol.getDegree()) { return false; } int i; for (i = 0; i < size; i++) { if (!coeff[i].equals(otherPol.coeff[i])) { return false; } } return true; } /** * @return the hash code of this polynomial */ public int hashCode() { return getDegree() + coeff.hashCode(); } /** * Adds the PolynomialGF2n <tt>b</tt> to <tt>this</tt> and returns the * result in a new <tt>PolynomialGF2n</tt>. * * @param b - * the <tt>PolynomialGF2n</tt> to add * @return <tt>this + b</tt> * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over * the same field. */ public final GF2nPolynomial add(GF2nPolynomial b) throws RuntimeException { GF2nPolynomial result; if (size() >= b.size()) { result = new GF2nPolynomial(size()); int i; for (i = 0; i < b.size(); i++) { result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]); } for (; i < size(); i++) { result.coeff[i] = coeff[i]; } } else { result = new GF2nPolynomial(b.size()); int i; for (i = 0; i < size(); i++) { result.coeff[i] = (GF2nElement)coeff[i].add(b.coeff[i]); } for (; i < b.size(); i++) { result.coeff[i] = b.coeff[i]; } } return result; } /** * Multiplies the scalar <i>s</i> to each coefficient of this * PolynomialGF2n and returns the result in a new PolynomialGF2n. * * @param s the scalar to multiply * @return <i>this</i> x <i>s</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>s</tt> are not defined over * the same field. */ public final GF2nPolynomial scalarMultiply(GF2nElement s) throws RuntimeException { GF2nPolynomial result = new GF2nPolynomial(size()); int i; for (i = 0; i < size(); i++) { result.coeff[i] = (GF2nElement)coeff[i].multiply(s); // result[i] // = // a[i]*s } return result; } /** * Multiplies <i>this</i> by <i>b</i> and returns the result in a new * PolynomialGF2n. * * @param b the PolynomialGF2n to multiply * @return <i>this</i> * <i>b</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over * the same field. */ public final GF2nPolynomial multiply(GF2nPolynomial b) throws RuntimeException { int i, j; int aDegree = size(); int bDegree = b.size(); if (aDegree != bDegree) { throw new IllegalArgumentException( "PolynomialGF2n.multiply: this and b must " + "have the same size!"); } GF2nPolynomial result = new GF2nPolynomial((aDegree << 1) - 1); for (i = 0; i < size(); i++) { for (j = 0; j < b.size(); j++) { if (result.coeff[i + j] == null) { result.coeff[i + j] = (GF2nElement)coeff[i] .multiply(b.coeff[j]); } else { result.coeff[i + j] = (GF2nElement)result.coeff[i + j] .add(coeff[i].multiply(b.coeff[j])); } } } return result; } /** * Multiplies <i>this</i> by <i>b</i>, reduces the result by <i>g</i> and * returns it in a new PolynomialGF2n. * * @param b the PolynomialGF2n to multiply * @param g the modul * @return <i>this</i> * <i>b</i> mod <i>g</i> * @throws DifferentFieldsException if <tt>this</tt>, <tt>b</tt> and <tt>g</tt> are * not all defined over the same field. */ public final GF2nPolynomial multiplyAndReduce(GF2nPolynomial b, GF2nPolynomial g) throws RuntimeException, ArithmeticException { return multiply(b).reduce(g); } /** * Reduces <i>this</i> by <i>g</i> and returns the result in a new * PolynomialGF2n. * * @param g - * the modulus * @return <i>this</i> % <i>g</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>g</tt> are not defined over * the same field. */ public final GF2nPolynomial reduce(GF2nPolynomial g) throws RuntimeException, ArithmeticException { return remainder(g); // return this % g } /** * Shifts left <i>this</i> by <i>amount</i> and stores the result in * <i>this</i> PolynomialGF2n. * * @param amount the amount to shift the coefficients */ public final void shiftThisLeft(int amount) { if (amount > 0) { int i; int oldSize = size; GF2nField f = coeff[0].getField(); enlarge(size + amount); for (i = oldSize - 1; i >= 0; i--) { coeff[i + amount] = coeff[i]; } if (coeff[0] instanceof GF2nPolynomialElement) { for (i = amount - 1; i >= 0; i--) { coeff[i] = GF2nPolynomialElement .ZERO((GF2nPolynomialField)f); } } else if (coeff[0] instanceof GF2nONBElement) { for (i = amount - 1; i >= 0; i--) { coeff[i] = GF2nONBElement.ZERO((GF2nONBField)f); } } } } public final GF2nPolynomial shiftLeft(int amount) { if (amount <= 0) { return new GF2nPolynomial(this); } GF2nPolynomial result = new GF2nPolynomial(size + amount, coeff[0]); result.assignZeroToElements(); for (int i = 0; i < size; i++) { result.coeff[i + amount] = coeff[i]; } return result; } /** * Divides <i>this</i> by <i>b</i> and stores the result in a new * PolynomialGF2n[2], quotient in result[0] and remainder in result[1]. * * @param b the divisor * @return the quotient and remainder of <i>this</i> / <i>b</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over * the same field. */ public final GF2nPolynomial[] divide(GF2nPolynomial b) throws RuntimeException, ArithmeticException { GF2nPolynomial[] result = new GF2nPolynomial[2]; GF2nPolynomial a = new GF2nPolynomial(this); a.shrink(); GF2nPolynomial shift; GF2nElement factor; int bDegree = b.getDegree(); GF2nElement inv = (GF2nElement)b.coeff[bDegree].invert(); if (a.getDegree() < bDegree) { result[0] = new GF2nPolynomial(this); result[0].assignZeroToElements(); result[0].shrink(); result[1] = new GF2nPolynomial(this); result[1].shrink(); return result; } result[0] = new GF2nPolynomial(this); result[0].assignZeroToElements(); int i = a.getDegree() - bDegree; while (i >= 0) { factor = (GF2nElement)a.coeff[a.getDegree()].multiply(inv); shift = b.scalarMultiply(factor); shift.shiftThisLeft(i); a = a.add(shift); a.shrink(); result[0].coeff[i] = (GF2nElement)factor.clone(); i = a.getDegree() - bDegree; } result[1] = a; result[0].shrink(); return result; } /** * Divides <i>this</i> by <i>b</i> and stores the remainder in a new * PolynomialGF2n. * * @param b the divisor * @return the remainder <i>this</i> % <i>b</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over * the same field. */ public final GF2nPolynomial remainder(GF2nPolynomial b) throws RuntimeException, ArithmeticException { GF2nPolynomial[] result = new GF2nPolynomial[2]; result = divide(b); return result[1]; } /** * Divides <i>this</i> by <i>b</i> and stores the quotient in a new * PolynomialGF2n. * * @param b the divisor * @return the quotient <i>this</i> / <i>b</i> * @throws DifferentFieldsException if <tt>this</tt> and <tt>b</tt> are not defined over * the same field. */ public final GF2nPolynomial quotient(GF2nPolynomial b) throws RuntimeException, ArithmeticException { GF2nPolynomial[] result = new GF2nPolynomial[2]; result = divide(b); return result[0]; } /** * Computes the greatest common divisor of <i>this</i> and <i>g</i> and * returns the result in a new PolynomialGF2n. * * @param g - * a GF2nPolynomial * @return gcd(<i>this</i>, <i>g</i>) * @throws DifferentFieldsException if the coefficients of <i>this</i> and <i>g</i> use * different fields * @throws ArithmeticException if coefficients are zero. */ public final GF2nPolynomial gcd(GF2nPolynomial g) throws RuntimeException, ArithmeticException { GF2nPolynomial a = new GF2nPolynomial(this); GF2nPolynomial b = new GF2nPolynomial(g); a.shrink(); b.shrink(); GF2nPolynomial c; GF2nPolynomial result; GF2nElement alpha; while (!b.isZero()) { c = a.remainder(b); a = b; b = c; } alpha = a.coeff[a.getDegree()]; result = a.scalarMultiply((GF2nElement)alpha.invert()); return result; } }