blob: e972f6f5f0d099eb49776ebc993dd3f6ed2b4200 [file] [log] [blame]
package org.bouncycastle.pqc.math.linearalgebra;
import java.security.SecureRandom;
import org.bouncycastle.util.Arrays;
/**
* This class implements the abstract class <tt>Vector</tt> for the case of
* vectors over the finite field GF(2). <br>
* For the vector representation the array of type int[] is used, thus one
* element of the array holds 32 elements of the vector.
*
* @see Vector
*/
public class GF2Vector
extends Vector
{
/**
* holds the elements of this vector
*/
private int[] v;
/**
* Construct the zero vector of the given length.
*
* @param length the length of the vector
*/
public GF2Vector(int length)
{
if (length < 0)
{
throw new ArithmeticException("Negative length.");
}
this.length = length;
v = new int[(length + 31) >> 5];
}
/**
* Construct a random GF2Vector of the given length.
*
* @param length the length of the vector
* @param sr the source of randomness
*/
public GF2Vector(int length, SecureRandom sr)
{
this.length = length;
int size = (length + 31) >> 5;
v = new int[size];
// generate random elements
for (int i = size - 1; i >= 0; i--)
{
v[i] = sr.nextInt();
}
// erase unused bits
int r = length & 0x1f;
if (r != 0)
{
// erase unused bits
v[size - 1] &= (1 << r) - 1;
}
}
/**
* Construct a random GF2Vector of the given length with the specified
* number of non-zero coefficients.
*
* @param length the length of the vector
* @param t the number of non-zero coefficients
* @param sr the source of randomness
*/
public GF2Vector(int length, int t, SecureRandom sr)
{
if (t > length)
{
throw new ArithmeticException(
"The hamming weight is greater than the length of vector.");
}
this.length = length;
int size = (length + 31) >> 5;
v = new int[size];
int[] help = new int[length];
for (int i = 0; i < length; i++)
{
help[i] = i;
}
int m = length;
for (int i = 0; i < t; i++)
{
int j = RandUtils.nextInt(sr, m);
setBit(help[j]);
m--;
help[j] = help[m];
}
}
/**
* Construct a GF2Vector of the given length and with elements from the
* given array. The array is copied and unused bits are masked out.
*
* @param length the length of the vector
* @param v the element array
*/
public GF2Vector(int length, int[] v)
{
if (length < 0)
{
throw new ArithmeticException("negative length");
}
this.length = length;
int size = (length + 31) >> 5;
if (v.length != size)
{
throw new ArithmeticException("length mismatch");
}
this.v = IntUtils.clone(v);
int r = length & 0x1f;
if (r != 0)
{
// erase unused bits
this.v[size - 1] &= (1 << r) - 1;
}
}
/**
* Copy constructor.
*
* @param other another {@link GF2Vector}
*/
public GF2Vector(GF2Vector other)
{
this.length = other.length;
this.v = IntUtils.clone(other.v);
}
/**
* Construct a new {@link GF2Vector} of the given length and with the given
* element array. The array is not changed and only a reference to the array
* is stored. No length checking is performed either.
*
* @param v the element array
* @param length the length of the vector
*/
protected GF2Vector(int[] v, int length)
{
this.v = v;
this.length = length;
}
/**
* Construct a new GF2Vector with the given length out of the encoded
* vector.
*
* @param length the length of the vector
* @param encVec the encoded vector
* @return the decoded vector
*/
public static GF2Vector OS2VP(int length, byte[] encVec)
{
if (length < 0)
{
throw new ArithmeticException("negative length");
}
int byteLen = (length + 7) >> 3;
if (encVec.length > byteLen)
{
throw new ArithmeticException("length mismatch");
}
return new GF2Vector(length, LittleEndianConversions.toIntArray(encVec));
}
/**
* Encode this vector as byte array.
*
* @return the encoded vector
*/
public byte[] getEncoded()
{
int byteLen = (length + 7) >> 3;
return LittleEndianConversions.toByteArray(v, byteLen);
}
/**
* @return the int array representation of this vector
*/
public int[] getVecArray()
{
return v;
}
/**
* Return the Hamming weight of this vector, i.e., compute the number of
* units of this vector.
*
* @return the Hamming weight of this vector
*/
public int getHammingWeight()
{
int weight = 0;
for (int i = 0; i < v.length; i++)
{
int e = v[i];
for (int j = 0; j < 32; j++)
{
int b = e & 1;
if (b != 0)
{
weight++;
}
e >>>= 1;
}
}
return weight;
}
/**
* @return whether this is the zero vector (i.e., all elements are zero)
*/
public boolean isZero()
{
for (int i = v.length - 1; i >= 0; i--)
{
if (v[i] != 0)
{
return false;
}
}
return true;
}
/**
* Return the value of the bit of this vector at the specified index.
*
* @param index the index
* @return the value of the bit (0 or 1)
*/
public int getBit(int index)
{
if (index >= length)
{
throw new IndexOutOfBoundsException();
}
int q = index >> 5;
int r = index & 0x1f;
return (v[q] & (1 << r)) >>> r;
}
/**
* Set the coefficient at the given index to 1. If the index is out of
* bounds, do nothing.
*
* @param index the index of the coefficient to set
*/
public void setBit(int index)
{
if (index >= length)
{
throw new IndexOutOfBoundsException();
}
v[index >> 5] |= 1 << (index & 0x1f);
}
/**
* Adds another GF2Vector to this vector.
*
* @param other another GF2Vector
* @return <tt>this + other</tt>
* @throws ArithmeticException if the other vector is not a GF2Vector or has another
* length.
*/
public Vector add(Vector other)
{
if (!(other instanceof GF2Vector))
{
throw new ArithmeticException("vector is not defined over GF(2)");
}
GF2Vector otherVec = (GF2Vector)other;
if (length != otherVec.length)
{
throw new ArithmeticException("length mismatch");
}
int[] vec = IntUtils.clone(((GF2Vector)other).v);
for (int i = vec.length - 1; i >= 0; i--)
{
vec[i] ^= v[i];
}
return new GF2Vector(length, vec);
}
/**
* Multiply this vector with a permutation.
*
* @param p the permutation
* @return <tt>this*p = p*this</tt>
*/
public Vector multiply(Permutation p)
{
int[] pVec = p.getVector();
if (length != pVec.length)
{
throw new ArithmeticException("length mismatch");
}
GF2Vector result = new GF2Vector(length);
for (int i = 0; i < pVec.length; i++)
{
int e = v[pVec[i] >> 5] & (1 << (pVec[i] & 0x1f));
if (e != 0)
{
result.v[i >> 5] |= 1 << (i & 0x1f);
}
}
return result;
}
/**
* Return a new vector consisting of the elements of this vector with the
* indices given by the set <tt>setJ</tt>.
*
* @param setJ the set of indices of elements to extract
* @return the new {@link GF2Vector}
* <tt>[this_setJ[0], this_setJ[1], ..., this_setJ[#setJ-1]]</tt>
*/
public GF2Vector extractVector(int[] setJ)
{
int k = setJ.length;
if (setJ[k - 1] > length)
{
throw new ArithmeticException("invalid index set");
}
GF2Vector result = new GF2Vector(k);
for (int i = 0; i < k; i++)
{
int e = v[setJ[i] >> 5] & (1 << (setJ[i] & 0x1f));
if (e != 0)
{
result.v[i >> 5] |= 1 << (i & 0x1f);
}
}
return result;
}
/**
* Return a new vector consisting of the first <tt>k</tt> elements of this
* vector.
*
* @param k the number of elements to extract
* @return a new {@link GF2Vector} consisting of the first <tt>k</tt>
* elements of this vector
*/
public GF2Vector extractLeftVector(int k)
{
if (k > length)
{
throw new ArithmeticException("invalid length");
}
if (k == length)
{
return new GF2Vector(this);
}
GF2Vector result = new GF2Vector(k);
int q = k >> 5;
int r = k & 0x1f;
System.arraycopy(v, 0, result.v, 0, q);
if (r != 0)
{
result.v[q] = v[q] & ((1 << r) - 1);
}
return result;
}
/**
* Return a new vector consisting of the last <tt>k</tt> elements of this
* vector.
*
* @param k the number of elements to extract
* @return a new {@link GF2Vector} consisting of the last <tt>k</tt>
* elements of this vector
*/
public GF2Vector extractRightVector(int k)
{
if (k > length)
{
throw new ArithmeticException("invalid length");
}
if (k == length)
{
return new GF2Vector(this);
}
GF2Vector result = new GF2Vector(k);
int q = (length - k) >> 5;
int r = (length - k) & 0x1f;
int length = (k + 31) >> 5;
int ind = q;
// if words have to be shifted
if (r != 0)
{
// process all but last word
for (int i = 0; i < length - 1; i++)
{
result.v[i] = (v[ind++] >>> r) | (v[ind] << (32 - r));
}
// process last word
result.v[length - 1] = v[ind++] >>> r;
if (ind < v.length)
{
result.v[length - 1] |= v[ind] << (32 - r);
}
}
else
{
// no shift necessary
System.arraycopy(v, q, result.v, 0, length);
}
return result;
}
/**
* Rewrite this vector as a vector over <tt>GF(2<sup>m</sup>)</tt> with
* <tt>t</tt> elements.
*
* @param field the finite field <tt>GF(2<sup>m</sup>)</tt>
* @return the converted vector over <tt>GF(2<sup>m</sup>)</tt>
*/
public GF2mVector toExtensionFieldVector(GF2mField field)
{
int m = field.getDegree();
if ((length % m) != 0)
{
throw new ArithmeticException("conversion is impossible");
}
int t = length / m;
int[] result = new int[t];
int count = 0;
for (int i = t - 1; i >= 0; i--)
{
for (int j = field.getDegree() - 1; j >= 0; j--)
{
int q = count >>> 5;
int r = count & 0x1f;
int e = (v[q] >>> r) & 1;
if (e == 1)
{
result[i] ^= 1 << j;
}
count++;
}
}
return new GF2mVector(field, result);
}
/**
* Check if the given object is equal to this vector.
*
* @param other vector
* @return the result of the comparison
*/
public boolean equals(Object other)
{
if (!(other instanceof GF2Vector))
{
return false;
}
GF2Vector otherVec = (GF2Vector)other;
return (length == otherVec.length) && IntUtils.equals(v, otherVec.v);
}
/**
* @return the hash code of this vector
*/
public int hashCode()
{
int hash = length;
hash = hash * 31 + Arrays.hashCode(v);
return hash;
}
/**
* @return a human readable form of this vector
*/
public String toString()
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < length; i++)
{
if ((i != 0) && ((i & 0x1f) == 0))
{
buf.append(' ');
}
int q = i >> 5;
int r = i & 0x1f;
int bit = v[q] & (1 << r);
if (bit == 0)
{
buf.append('0');
}
else
{
buf.append('1');
}
}
return buf.toString();
}
}