/* 
 * Reed-Solomon error-correcting code decoder (Java)
 * 
 * Copyright (c) 2019 Project Nayuki
 * All rights reserved. Contact Nayuki for licensing.
 * https://www.nayuki.io/page/reed-solomon-error-correcting-code-decoder
 */
import java.util.Objects;
/**
 * A finite field of the form Zp, where p is a prime number.
 * Each element of this kind of field is an integer in the range [0, p).
 * Both the field and the elements are immutable and thread-safe.
 */
public final class PrimeField extends Field {
	
	/*---- Fields ----*/
	
	/**
	 * The modulus of this field, which is also the number
	 * of elements in this finite field. Must be prime.
	 */
	public final int modulus;
	
	
	
	/*---- Constructor ----*/
	
	/**
	 * Constructs a prime field with the specified modulus. The modulus must be a
	 * prime number, but this critical property is not checked by the constructor.
	 * @param mod the modulus, which must be prime
	 * @throws IllegalArgumentException if {@code mod} < 2
	 */
	public PrimeField(int mod) {
		if (mod < 2)
			throw new IllegalArgumentException("Modulus must be prime");
		modulus = mod;
	}
	
	
	
	/*---- Methods ----*/
	
	public Integer zero() {
		return 0;
	}
	
	public Integer one() {
		return 1;
	}
	
	
	public boolean equals(Integer x, Integer y) {
		return check(x) == check(y);
	}
	
	public Integer negate(Integer x) {
		return (modulus - check(x)) % modulus;
	}
	
	public Integer add(Integer x, Integer y) {
		return (int)(((long)check(x) + check(y)) % modulus);
	}
	
	public Integer subtract(Integer x, Integer y) {
		return (int)(((long)check(x) + modulus - check(y)) % modulus);
	}
	
	public Integer multiply(Integer x, Integer y) {
		return (int)((long)check(x) * check(y) % modulus);
	}
	
	
	public Integer reciprocal(Integer w) {
		// Extended Euclidean GCD algorithm
		int x = modulus;
		int y = check(w);
		if (y == 0)
			throw new ArithmeticException("Division by zero");
		int a = 0;
		int b = 1;
		while (y != 0) {
			int z = x % y;
			int c = a - x / y * b;
			x = y;
			y = z;
			a = b;
			b = c;
		}
		if (x == 1)
			return (int)(((long)a + modulus) % modulus);
		else  // All non-zero values must have a reciprocal
			throw new IllegalStateException("Field modulus is not prime");
	}
	
	
	// Checks if the given object is non-null and within the range
	// of valid values, and returns the unboxed primitive value.
	private int check(Integer x) {
		Objects.requireNonNull(x);
		int y = x.intValue();
		if (y < 0 || y >= modulus)
			throw new IllegalArgumentException("Not an element of this field: " + y);
		return y;
	}
	
}