ch.openchvote.utilities.algebra.ZZ Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utilities Show documentation
Show all versions of utilities Show documentation
This module provides a collection of utility classes, especially for various mathematical concepts.
The newest version!
/*
* Copyright (C) 2024 Berner Fachhochschule https://e-voting.bfh.ch
*
* - This program is free software: you can redistribute it and/or modify -
* - it under the terms of the GNU Affero General Public License as published by -
* - the Free Software Foundation, either version 3 of the License, or -
* - (at your option) any later version. -
* - -
* - This program is distributed in the hope that it will be useful, -
* - but WITHOUT ANY WARRANTY; without even the implied warranty of -
* - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -
* - GNU General Public License for more details. -
* - -
* - You should have received a copy of the GNU Affero General Public License -
* - along with this program. If not, see . -
*/
package ch.openchvote.utilities.algebra;
import ch.openchvote.utilities.UtilityException;
import ch.openchvote.utilities.sequence.Vector;
import ch.openchvote.utilities.set.FiniteSet;
import ch.openchvote.utilities.tools.Streamable;
import ch.openchvote.utilities.tools.VMGJFassade;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import static ch.openchvote.utilities.UtilityException.Type.*;
/**
* This class represents the ring of integers modulo {@code n}. The ring offers two binary operation (addition and
* multiplication) from which other operations (subtraction, division, etc.) can be derived. The elements of the ring
* are represented by objects of type {@link BigInteger}. The ring is a {@link FiniteSet} with a size of {@code n}.
* Instances of this class are immutable.
*/
public final class ZZ implements FiniteSet, Streamable {
// a cache of previously constructed instances
static private final Map CACHE = Collections.synchronizedMap(new HashMap<>());
// the modulus
private final BigInteger n;
// private constructor used by factory method
private ZZ(BigInteger n) {
this.n = n;
}
/**
* Returns the ring {@code {0,...,n-1}} of integers modulo {@code n}.
*
* @param n The modulus
* @return The ring of integers modulo {@code n}
*/
static public ZZ of(BigInteger n) {
if (n == null) {
throw new UtilityException(NULL_POINTER, ZZ.class);
}
return CACHE.computeIfAbsent(n, key -> {
if (n.signum() <= 0) {
throw new UtilityException(INVALID_PARAMETERS, ZZ.class, "Value n must be positive");
}
return new ZZ(n);
});
}
/**
* Maps a given integer into the ring of integers modulo {@code n}.
*
* @param x The given integer
* @return The corresponding element of the ring
*/
public BigInteger mapToRing(BigInteger x) {
return x.mod(this.n);
}
/**
* Computes the modular sum of two integers {@code x} and {@code y}.
*
* @param x The first integer
* @param y The second integer
* @return The modular sum {@code x + y mod n}
*/
public BigInteger add(BigInteger x, BigInteger y) {
return x.add(y).mod(this.n);
}
/**
* Computes the modular sum of a vector of integers. Returns 1 if the vector is empty.
*
* @param bold_x The vector of integers
* @return The modular sum of the given vector of integers
*/
public BigInteger sum(Vector bold_x) {
return bold_x.toStream().reduce(BigInteger.ZERO, this::add);
}
/**
* Computes the additive inverse of {@code x} modulo {@code p}.
*
* @param x The given integer
* @return The additive inverse of {@code x} modulo {@code p}
*/
public BigInteger minus(BigInteger x) {
return this.n.subtract(x).mod(this.n);
}
/**
* Computes the modular difference of two integers {@code x} and {@code y}.
*
* @param x The first integer
* @param y The second integer
* @return The modular difference {@code x - y mod n}
*/
@SuppressWarnings("SuspiciousNameCombination")
public BigInteger subtract(BigInteger x, BigInteger y) {
return this.add(x, this.minus(y));
}
/**
* Computes the modular product of two integers {@code x} and {@code y}.
*
* @param x The first integer
* @param y The second integer
* @return The modular product {@code x * y mod n}
*/
public BigInteger multiply(BigInteger x, BigInteger y) {
return x.multiply(y).mod(this.n);
}
/**
* Computes the modular product of three integers {@code x}, {@code y}, and {@code z}.
*
* @param x The first integer
* @param y The second integer
* @param z The third integer
* @return The modular product {@code x * y * y mod n}
*/
public BigInteger multiply(BigInteger x, BigInteger y, BigInteger z) {
return this.multiply(this.multiply(x, y), z);
}
/**
* Computes the modular product of a vector of integers. Returns 1 if the vector is empty.
*
* @param bold_x The vector of integers
* @return The modular product of the given vector of integers
*/
public BigInteger prod(Vector bold_x) {
return bold_x.toStream().reduce(BigInteger.ONE, this::multiply);
}
/**
* Computes {@code x} to the power of {@code y}.
*
* @param x The base
* @param y The exponent
* @return {@code x} to the power of {@code y}
*/
public BigInteger pow(BigInteger x, BigInteger y) {
if (VMGJFassade.isLoaded()) {
return VMGJFassade.modPow(x, y, this.n);
} else {
return x.modPow(y, this.n);
}
}
/**
* Computes the multiplicative inverse of {@code x} modulo {@code p}.
*
* @param x The given integer
* @return The multiplicative inverse of {@code x} modulo {@code p}
*/
public BigInteger invert(BigInteger x) {
return x.modInverse(this.n);
}
/**
* Computes the modular division of two integers {@code x} and {@code y}.
*
* @param x The first integer
* @param y The second integer
* @return The modular division {@code x / y mod n}
*/
@SuppressWarnings("SuspiciousNameCombination")
public BigInteger divide(BigInteger x, BigInteger y) {
return this.multiply(x, this.invert(y));
}
/**
* Computes the modular sum of products obtained from combining the values of two equally long vectors of integers.
* Returns 0 if the vectors are empty.
*
* @param bold_x The first vector of integers
* @param bold_y The second vector of integers
* @return The resulting modular sum of products
*/
public BigInteger sumProd(Vector bold_x, Vector bold_y) {
return this.sum(bold_x.map(bold_y, this::multiply));
}
/**
* Computes the modular product of the modular powers obtained from combining the values of two equally long vectors
* of bases and exponents. Returns 1 if the vectors are empty. Tries to delegate the computation to the VMGJ
* library.
*
* @param bold_x The first vector of integers
* @param bold_y The second vector of integers
* @return The modular product of powers
*/
public BigInteger prodPow(Vector bold_x, Vector bold_y) {
if (bold_x.getLength() != bold_y.getLength()) {
throw new UtilityException(INCOMPATIBLE_LENGTHS, ZZ.class);
}
if (VMGJFassade.isLoaded()) {
return VMGJFassade.modProdPow(bold_x.toArray(), bold_y.toArray(BigInteger[]::new), this.n);
} else {
return this.prod(bold_x.map(bold_y, this::pow));
}
}
@Override
public boolean contains(BigInteger x) {
return x != null && x.signum() >= 0 && x.compareTo(this.n) < 0;
}
@Override
public BigInteger getSize() {
return this.n;
}
@Override
public Stream toStream() {
return Stream.iterate(BigInteger.ZERO, x -> x.compareTo(this.n) < 0, x -> x.add(BigInteger.ONE));
}
}