/* * Gauss-Jordan elimination over any field (Java) * * Copyright (c) 2019 Project Nayuki * All rights reserved. Contact Nayuki for licensing. * https://www.nayuki.io/page/gauss-jordan-elimination-over-any-field */ import java.math.BigInteger; import java.util.Objects; /** * A Galois field of the form GF(2n/mod). Each element of this kind of * field is a polynomial of degree less than n where each monomial coefficient is either 0 or 1. * Both the field and the elements are immutable and thread-safe. */ public final class BinaryField extends Field { /*---- Fields ----*/ /** * The modulus of this field represented as a string of bits in natural order. * For example, the modulus x^5 + x^1 + x^0 * is represented by the integer value 0b100011 (binary) or 35 (decimal). */ public final BigInteger modulus; /*---- Constructor ----*/ /** * Constructs a binary field with the specified modulus. The modulus must have * degree at least 1. Also the modulus must be irreducible (not factorable) * in Z2, but this critical property is not checked by the constructor. * @param mod the modulus polynomial * @throws NullPointerException if the modulus is {@code null} * @throws IllegalArgumentException if the modulus has degree less than 1 */ public BinaryField(BigInteger mod) { Objects.requireNonNull(mod); if (mod.compareTo(BigInteger.ONE) <= 0) throw new IllegalArgumentException("Invalid modulus"); modulus = mod; } /*---- Methods ----*/ public BigInteger zero() { return BigInteger.ZERO; } public BigInteger one() { return BigInteger.ONE; } public boolean equals(BigInteger x, BigInteger y) { return check(x).equals(check(y)); } public BigInteger negate(BigInteger x) { return check(x); } public BigInteger add(BigInteger x, BigInteger y) { return check(x).xor(check(y)); } public BigInteger subtract(BigInteger x, BigInteger y) { return add(x, y); } public BigInteger multiply(BigInteger x, BigInteger y) { check(x); check(y); BigInteger result = BigInteger.ZERO; for (int i = 0; i < y.bitLength(); i++) { if (y.testBit(i)) result = result.xor(x); x = x.shiftLeft(1); if (x.testBit(modulus.bitLength() - 1)) x = x.xor(modulus); } return result; } public BigInteger reciprocal(BigInteger w) { // Extended Euclidean GCD algorithm BigInteger x = modulus; BigInteger y = check(w); if (y.equals(BigInteger.ZERO)) throw new ArithmeticException("Division by zero"); BigInteger a = BigInteger.ZERO; BigInteger b = BigInteger.ONE; while (y.signum() != 0) { BigInteger[] quotrem = divideAndRemainder(x, y); if (quotrem[0].equals(modulus)) quotrem[0] = BigInteger.ZERO; BigInteger c = a.xor(multiply(quotrem[0], b)); x = y; y = quotrem[1]; a = b; b = c; } if (x.equals(BigInteger.ONE)) return a; else // All non-zero values must have a reciprocal throw new IllegalStateException("Field modulus is not irreducible"); } // Returns a new array containing the pair of values (x div y, x mod y). private BigInteger[] divideAndRemainder(BigInteger x, BigInteger y) { BigInteger quotient = BigInteger.ZERO; int topY = y.bitLength() - 1; for (int i = x.bitLength() - y.bitLength(); i >= 0; i--) { if (x.testBit(topY + i)) { x = x.xor(y.shiftLeft(i)); quotient = quotient.setBit(i); } } return new BigInteger[]{quotient, x}; } // Checks if the given object is non-null and within the // range of valid values, and returns the value itself. private BigInteger check(BigInteger x) { Objects.requireNonNull(x); if (x.signum() == -1 || x.bitLength() >= modulus.bitLength()) throw new IllegalArgumentException("Not an element of this field"); return x; } }