added jmath library
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
alias(catalog.plugins.lombok)
|
||||
alias(catalog.plugins.envelope)
|
||||
}
|
||||
|
||||
|
61
build.gradle
61
build.gradle
@@ -1,13 +1,11 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
alias(catalog.plugins.multi.release.jar)
|
||||
alias(catalog.plugins.lombok) apply false
|
||||
alias(catalog.plugins.lombok)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: catalog.plugins.lombok.get().pluginId
|
||||
|
||||
group = "net.woggioni"
|
||||
version = getProperty('jwo.version')
|
||||
|
||||
@@ -17,16 +15,45 @@ allprojects {
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation catalog.junit.jupiter.api
|
||||
testImplementation catalog.junit.jupiter.params
|
||||
testRuntimeOnly catalog.junit.jupiter.engine
|
||||
|
||||
pluginManager.withPlugin('java-library') {
|
||||
|
||||
dependencies {
|
||||
testImplementation catalog.junit.jupiter.api
|
||||
testImplementation catalog.junit.jupiter.params
|
||||
testRuntimeOnly catalog.junit.jupiter.engine
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
lombok {
|
||||
version = catalog.versions.lombok.get()
|
||||
pluginManager.withPlugin(catalog.plugins.lombok.get().pluginId) {
|
||||
lombok {
|
||||
version = catalog.versions.lombok.get()
|
||||
}
|
||||
}
|
||||
|
||||
pluginManager.withPlugin('maven-publish') {
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://mvn.woggioni.net/'
|
||||
}
|
||||
}
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
ext {
|
||||
@@ -76,18 +103,6 @@ test {
|
||||
jvmArgs(['--add-opens', 'java.base/sun.nio.fs=ALL-UNNAMED'])
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://mvn.woggioni.net/'
|
||||
}
|
||||
}
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -1,3 +1,3 @@
|
||||
jwo.version = 2023.03
|
||||
jwo.version = 2023.05
|
||||
lys.version = 2023.03
|
||||
guice.version = 5.0.1
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
18
jmath/build.gradle
Normal file
18
jmath/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
alias(catalog.plugins.lombok)
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':')
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
50
jmath/src/main/java/net/woggioni/jmath/BigIntegerExt.java
Normal file
50
jmath/src/main/java/net/woggioni/jmath/BigIntegerExt.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class BigIntegerExt {
|
||||
|
||||
static public int gcd(int a, int b) {
|
||||
int tmp;
|
||||
while (b != 0) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp % b;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static public BigInteger gcd(BigInteger a, BigInteger b) {
|
||||
BigInteger tmp;
|
||||
while (!BigInteger.ZERO.equals(b)) {
|
||||
tmp = a;
|
||||
a = b;
|
||||
b = tmp.mod(b);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// static BigInteger gcd(BigInteger n1, BigInteger n2) {
|
||||
// BigInteger remainder;
|
||||
// BigInteger result;
|
||||
// while (true) {
|
||||
// remainder = n1.mod(n2);
|
||||
// result = n2;
|
||||
// if (BigInteger.ZERO.equals(remainder)) break;
|
||||
// else {
|
||||
// n1 = n2;
|
||||
// n2 = remainder;
|
||||
// }
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
public static BigInteger mcm(BigInteger n1, BigInteger n2) {
|
||||
return n1.multiply(n2).divide(gcd(n1, n2));
|
||||
}
|
||||
}
|
32
jmath/src/main/java/net/woggioni/jmath/DecimalFactory.java
Normal file
32
jmath/src/main/java/net/woggioni/jmath/DecimalFactory.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DecimalFactory implements NumericTypeFactory<DecimalValue> {
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final MathContext ctx;
|
||||
@Override
|
||||
public DecimalValue getZero() {
|
||||
return new DecimalValue(BigDecimal.ZERO, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue getOne() {
|
||||
return new DecimalValue(BigDecimal.ONE, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue[] getArray(int size) {
|
||||
return new DecimalValue[size];
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static final DecimalFactory instance = new DecimalFactory(MathContext.DECIMAL128);
|
||||
}
|
63
jmath/src/main/java/net/woggioni/jmath/DecimalValue.java
Normal file
63
jmath/src/main/java/net/woggioni/jmath/DecimalValue.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@RequiredArgsConstructor
|
||||
public class DecimalValue implements NumericType<DecimalValue> {
|
||||
|
||||
@EqualsAndHashCode.Include
|
||||
private final BigDecimal value;
|
||||
private final DecimalFactory decimalFactory;
|
||||
|
||||
@Override
|
||||
public DecimalValue add(DecimalValue other) {
|
||||
return new DecimalValue(value.add(other.value), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue sub(DecimalValue other) {
|
||||
return new DecimalValue(value.subtract(other.value), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue mul(DecimalValue other) {
|
||||
return new DecimalValue(value.multiply(other.value), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue div(DecimalValue other) {
|
||||
return new DecimalValue(value.divide(other.value, decimalFactory.getCtx()), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue abs() {
|
||||
return new DecimalValue(value.abs(), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecimalValue sqrt() {
|
||||
return new DecimalValue(value.sqrt(decimalFactory.getCtx()), decimalFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DecimalValue o) {
|
||||
return value.compareTo(o.value);
|
||||
}
|
||||
|
||||
public static DecimalValue of(double n, DecimalFactory decimalFactory) {
|
||||
return new DecimalValue(BigDecimal.valueOf(n), decimalFactory);
|
||||
}
|
||||
|
||||
public static DecimalValue of(double n) {
|
||||
return new DecimalValue(BigDecimal.valueOf(n), DecimalFactory.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
23
jmath/src/main/java/net/woggioni/jmath/FloatFactory.java
Normal file
23
jmath/src/main/java/net/woggioni/jmath/FloatFactory.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class FloatFactory implements NumericTypeFactory<FloatValue> {
|
||||
@Override
|
||||
public FloatValue getZero() {
|
||||
return new FloatValue(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue getOne() {
|
||||
return new FloatValue(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue[] getArray(int size) {
|
||||
return new FloatValue[size];
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static final NumericTypeFactory<FloatValue> instance = new FloatFactory();
|
||||
}
|
58
jmath/src/main/java/net/woggioni/jmath/FloatValue.java
Normal file
58
jmath/src/main/java/net/woggioni/jmath/FloatValue.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@RequiredArgsConstructor
|
||||
public class FloatValue implements NumericType<FloatValue> {
|
||||
@EqualsAndHashCode.Include
|
||||
private final float value;
|
||||
|
||||
@Override
|
||||
public FloatValue add(FloatValue other) {
|
||||
return new FloatValue(value + other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue sub(FloatValue other) {
|
||||
return new FloatValue(value - other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue mul(FloatValue other) {
|
||||
return new FloatValue(value * other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue div(FloatValue other) {
|
||||
return new FloatValue(value / other.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue abs() {
|
||||
return new FloatValue(Math.abs(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue sqrt() {
|
||||
return new FloatValue((float) Math.sqrt(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(FloatValue o) {
|
||||
return Comparator.<Float>naturalOrder().compare(value, o.value);
|
||||
}
|
||||
|
||||
public static FloatValue of(float n) {
|
||||
return new FloatValue(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Objects.toString(value);
|
||||
}
|
||||
}
|
25
jmath/src/main/java/net/woggioni/jmath/IntegerFactory.java
Normal file
25
jmath/src/main/java/net/woggioni/jmath/IntegerFactory.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class IntegerFactory implements NumericTypeFactory<IntegerValue> {
|
||||
@Override
|
||||
public IntegerValue getZero() {
|
||||
return new IntegerValue(BigInteger.ZERO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue getOne() {
|
||||
return new IntegerValue(BigInteger.ONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue[] getArray(int size) {
|
||||
return new IntegerValue[size];
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static final NumericTypeFactory<IntegerValue> instance = new IntegerFactory();
|
||||
}
|
57
jmath/src/main/java/net/woggioni/jmath/IntegerValue.java
Normal file
57
jmath/src/main/java/net/woggioni/jmath/IntegerValue.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
@RequiredArgsConstructor
|
||||
public class IntegerValue implements NumericType<IntegerValue> {
|
||||
@EqualsAndHashCode.Include
|
||||
private final BigInteger value;
|
||||
|
||||
@Override
|
||||
public IntegerValue add(IntegerValue other) {
|
||||
return new IntegerValue(value.add(other.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue sub(IntegerValue other) {
|
||||
return new IntegerValue(value.subtract(other.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue mul(IntegerValue other) {
|
||||
return new IntegerValue(value.multiply(other.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue div(IntegerValue other) {
|
||||
return new IntegerValue(value.divide(other.value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue abs() {
|
||||
return new IntegerValue(value.abs());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue sqrt() {
|
||||
return new IntegerValue(value.sqrt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntegerValue o) {
|
||||
return value.compareTo(o.value);
|
||||
}
|
||||
|
||||
public static IntegerValue of(long n) {
|
||||
return new IntegerValue(BigInteger.valueOf(n));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
561
jmath/src/main/java/net/woggioni/jmath/Matrix.java
Normal file
561
jmath/src/main/java/net/woggioni/jmath/Matrix.java
Normal file
@@ -0,0 +1,561 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.woggioni.jwo.Requirement.require;
|
||||
|
||||
public class Matrix<T extends NumericType<T>> {
|
||||
|
||||
public interface Pivot {
|
||||
<U extends NumericType<U>> Matrix<U> mul(Matrix<U> m);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class PivotImpl implements Pivot {
|
||||
@Getter
|
||||
private final int[] values;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int permutations = 0;
|
||||
|
||||
@Override
|
||||
public <U extends NumericType<U>> Matrix<U> mul(Matrix<U> m) {
|
||||
return Matrix.of(m.numericTypeFactory, m.getRows(), m.getColumns(), (i, j) -> m.get(values[i], j));
|
||||
}
|
||||
}
|
||||
|
||||
public interface ValueGenerator<T extends NumericType<T>> {
|
||||
T generate(int row, int column);
|
||||
}
|
||||
|
||||
private final NumericTypeFactory<T> numericTypeFactory;
|
||||
private final T[] values;
|
||||
private final int rows;
|
||||
private final int columns;
|
||||
|
||||
@SneakyThrows
|
||||
public Matrix(int rows, int columns, NumericTypeFactory<T> numericTypeFactory) {
|
||||
this.numericTypeFactory = numericTypeFactory;
|
||||
this.rows = rows;
|
||||
this.columns = columns;
|
||||
this.values = numericTypeFactory.getArray(rows * columns);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < columns; j++) {
|
||||
set(i, j, numericTypeFactory.getZero());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Matrix<T> of(
|
||||
NumericTypeFactory<T> numericTypeFactory,
|
||||
int rows,
|
||||
int columns,
|
||||
ValueGenerator<T> generator) {
|
||||
Matrix<T> result = new Matrix<>(rows, columns, numericTypeFactory);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < columns; j++) {
|
||||
result.set(i, j, generator.generate(i, j));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Matrix<T> of(
|
||||
NumericTypeFactory<T> numericTypeFactory,
|
||||
int rows,
|
||||
int columns) {
|
||||
return of(numericTypeFactory, rows, columns, (i, j) -> numericTypeFactory.getZero());
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Matrix<T> of(NumericTypeFactory<T> numericTypeFactory, T[][] values) {
|
||||
int rows = values.length;
|
||||
int columns = values[0].length;
|
||||
return of(numericTypeFactory, rows, columns, (i, j) -> values[i][j]);
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Matrix<T> of(
|
||||
NumericTypeFactory<T> numericTypeFactory, int rows, int columns, T... values) {
|
||||
return of(numericTypeFactory, rows, columns, (i, j) -> values[i * columns + j]);
|
||||
}
|
||||
|
||||
public T get(int row, int column) {
|
||||
return values[row * columns + column];
|
||||
}
|
||||
|
||||
public void set(int row, int column, T value) {
|
||||
values[row * columns + column] = value;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public int getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
private void requireSameSize(Matrix<T> other) {
|
||||
require(() -> getRows() == other.getRows() && getColumns() == other.getColumns())
|
||||
.otherwise(SizeException.class, "Matrix dimension mismatch: (%d, %d) vs (%d, %d)",
|
||||
getRows(), getColumns(), other.getRows(), other.getColumns()
|
||||
);
|
||||
}
|
||||
|
||||
public Matrix<T> map(Matrix<T> other, BiFunction<T, T, T> op) {
|
||||
requireSameSize(other);
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) -> op.apply(get(i, j), other.get(i, j)));
|
||||
}
|
||||
|
||||
public <U extends NumericType<U>> Matrix<U> map(NumericTypeFactory<U> numericTypeFactory, Function<T, U> op) {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) -> op.apply(get(i, j)));
|
||||
}
|
||||
|
||||
public Matrix<T> map(Function<T, T> op) {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) -> op.apply(get(i, j)));
|
||||
}
|
||||
|
||||
public Matrix<T> add(Matrix<T> other) {
|
||||
return map(other, T::add);
|
||||
}
|
||||
|
||||
public Matrix<T> sub(Matrix<T> other) {
|
||||
return map(other, T::sub);
|
||||
}
|
||||
|
||||
public Matrix<T> mul(Matrix<T> other) {
|
||||
return map(other, T::mul);
|
||||
}
|
||||
|
||||
public Matrix<T> div(Matrix<T> other) {
|
||||
return map(other, T::div);
|
||||
}
|
||||
|
||||
public Matrix<T> add(T value) {
|
||||
return map(numericTypeFactory, (T v) -> v.add(value));
|
||||
}
|
||||
|
||||
public Matrix<T> sub(T value) {
|
||||
return map(numericTypeFactory, (T v) -> v.sub(value));
|
||||
}
|
||||
|
||||
public Matrix<T> mul(T value) {
|
||||
return map(numericTypeFactory, (T v) -> v.mul(value));
|
||||
}
|
||||
|
||||
public Matrix<T> div(T value) {
|
||||
return map(numericTypeFactory, (T v) -> v.div(value));
|
||||
}
|
||||
|
||||
|
||||
public Vector<T> solve(Vector<T> b) {
|
||||
Matrix<T> tmp = clone();
|
||||
Pivot pivot = tmp.lup();
|
||||
return tmp.luSolve(b, pivot);
|
||||
}
|
||||
|
||||
private void swapRows(int id1, int id2) {
|
||||
for (int i = 0; i < getColumns(); i++) {
|
||||
T tmp = get(id1, i);
|
||||
set(id1, i, get(id2, i));
|
||||
set(id2, i, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
private void swapRows(int id1, int id2, PivotImpl pivot) {
|
||||
swapRows(id1, id2);
|
||||
int tmp = pivot.getValues()[id1];
|
||||
pivot.getValues()[id1] = pivot.getValues()[id2];
|
||||
pivot.getValues()[id2] = tmp;
|
||||
pivot.permutations += 1;
|
||||
}
|
||||
|
||||
private void swapRows(int id1, int id2, PivotImpl pivot, Matrix<T> other) {
|
||||
swapRows(id1, id2, pivot);
|
||||
other.swapRows(id1, id2);
|
||||
}
|
||||
|
||||
private void luRow(int i) {
|
||||
if (Objects.equals(numericTypeFactory.getZero(), get(i, i))) {
|
||||
throw new RuntimeException("Matrix is singular");
|
||||
}
|
||||
for (int j = i; j < getColumns(); j++) {
|
||||
for (int k = 0; k < i; k++) {
|
||||
set(i, j, get(i, j).sub(get(i, k).mul(get(k, j))));
|
||||
}
|
||||
}
|
||||
for (int j = i + 1; j < getColumns(); j++) {
|
||||
for (int k = 0; k < i; k++) {
|
||||
set(j, i, get(j, i).sub(get(j, k).mul(get(k, i))));
|
||||
}
|
||||
set(j, i, get(j, i).div(get(i, i)));
|
||||
}
|
||||
}
|
||||
|
||||
private void luPivot(int i, Pivot pivot) {
|
||||
T max = get(i, i).abs();
|
||||
int max_index = i;
|
||||
for (int j = i + 1; j < getRows(); j++) {
|
||||
if (get(j, i).abs().compareTo(max) > 0) {
|
||||
max = get(i, j).abs();
|
||||
max_index = j;
|
||||
}
|
||||
}
|
||||
if (max_index != i) {
|
||||
swapRows(i, max_index, (PivotImpl) pivot);
|
||||
}
|
||||
}
|
||||
|
||||
public Pivot lup() {
|
||||
if (getRows() != getColumns()) throw new SizeException("Matrix must be square");
|
||||
int size = getRows();
|
||||
PivotImpl pivot = newPivot();
|
||||
for (int i = 0; i < size; i++) {
|
||||
luPivot(i, pivot);
|
||||
luRow(i);
|
||||
}
|
||||
return pivot;
|
||||
}
|
||||
|
||||
private PivotImpl newPivot() {
|
||||
int sz = getRows();
|
||||
int[] result = new int[sz];
|
||||
for (int i = 0; i < sz; i++) {
|
||||
result[i] = i;
|
||||
}
|
||||
return new PivotImpl(result);
|
||||
}
|
||||
|
||||
private void addRow(int sourceIndex, int destIndex, T factor) {
|
||||
int columns = getColumns();
|
||||
for (int i = 0; i < columns; i++) {
|
||||
set(destIndex, i, get(destIndex, i).add(get(sourceIndex, i).mul(factor)));
|
||||
}
|
||||
}
|
||||
|
||||
private void addRow(int sourceIndex, int destIndex, T factor, Matrix<T> other) {
|
||||
addRow(sourceIndex, destIndex, factor);
|
||||
other.addRow(sourceIndex, destIndex, factor);
|
||||
}
|
||||
|
||||
public void gaussJordanLow() {
|
||||
PivotImpl pivot = newPivot();
|
||||
int rows = getRows();
|
||||
int columns = getColumns();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
if (Objects.equals(numericTypeFactory.getZero(), get(i, i))) {
|
||||
for (int j = i + 1; j < columns; j++) {
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), get(j, i))) {
|
||||
swapRows(i, j, pivot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = i + 1; j < rows; j++) {
|
||||
T ii = get(i, i);
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), ii)) {
|
||||
T factor = get(j, i).div(ii).mul(numericTypeFactory.getMinusOne());
|
||||
addRow(i, j, factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gaussJordanLow(Matrix<T> other) {
|
||||
PivotImpl pivot = newPivot();
|
||||
int rows = getRows();
|
||||
int columns = getColumns();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
if (Objects.equals(numericTypeFactory.getZero(), get(i, i))) {
|
||||
for (int j = i + 1; j < columns; j++) {
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), get(j, i))) {
|
||||
swapRows(i, j, pivot, other);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = i + 1; j < rows; j++) {
|
||||
T ii = get(i, i);
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), ii)) {
|
||||
T factor = get(j, i).div(ii).mul(numericTypeFactory.getMinusOne());
|
||||
addRow(i, j, factor, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gaussJordanHigh() {
|
||||
PivotImpl pivot = newPivot();
|
||||
int i = getRows();
|
||||
while (i-- > 0) {
|
||||
if (Objects.equals(numericTypeFactory.getZero(), get(i, i))) {
|
||||
int j = i;
|
||||
while (j-- > 0) {
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), get(j, i))) {
|
||||
swapRows(i, j, pivot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int j = i;
|
||||
while (j-- > 0) {
|
||||
T ii = get(i, i);
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), ii)) {
|
||||
T factor = get(j, i).div(ii).mul(numericTypeFactory.getMinusOne());
|
||||
addRow(i, j, factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void gaussJordanHigh(Matrix<T> other) {
|
||||
PivotImpl pivot = newPivot();
|
||||
int i = getRows();
|
||||
while (i-- > 0) {
|
||||
if (Objects.equals(numericTypeFactory.getZero(), get(i, i))) {
|
||||
int j = i;
|
||||
while (j-- > 0) {
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), get(j, i))) {
|
||||
swapRows(i, j, pivot, other);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int j = i;
|
||||
while (j-- > 0) {
|
||||
T ii = get(i, i);
|
||||
if (!Objects.equals(numericTypeFactory.getZero(), ii)) {
|
||||
T factor = get(j, i).div(ii).mul(numericTypeFactory.getMinusOne());
|
||||
addRow(i, j, factor, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T det() {
|
||||
require(() -> getRows() == getColumns()).otherwise(SizeException.class, "Matrix must be square");
|
||||
Matrix<T> clone = clone();
|
||||
clone.gaussJordanLow();
|
||||
T result = numericTypeFactory.getOne();
|
||||
for (int i = 0; i < getRows(); i++)
|
||||
result = result.mul(clone.get(i, i));
|
||||
return result;
|
||||
}
|
||||
|
||||
public Matrix<T> invert() {
|
||||
require(() -> getRows() == getColumns()).otherwise(SizeException.class, "Matrix must be square");
|
||||
int sz = getRows();
|
||||
int col = getColumns();
|
||||
Matrix<T> tmp = clone();
|
||||
Matrix<T> result = identity(numericTypeFactory, sz);
|
||||
tmp.gaussJordanLow(result);
|
||||
tmp.gaussJordanHigh(result);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
T f = tmp.get(i, i);
|
||||
for (int j = 0; j < col; j++) {
|
||||
result.set(i, j, result.get(i, j).div(f));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Matrix<T> triu() {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) ->
|
||||
i <= j ? get(i, j) : numericTypeFactory.getZero()
|
||||
);
|
||||
}
|
||||
|
||||
public Matrix<T> triu(T diagValue) {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) -> {
|
||||
T result;
|
||||
if (i < j) {
|
||||
result = get(i, j);
|
||||
} else if (i == j) {
|
||||
result = diagValue;
|
||||
} else {
|
||||
result = numericTypeFactory.getZero();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public Matrix<T> tril() {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) ->
|
||||
i >= j ? get(i, j) : numericTypeFactory.getZero()
|
||||
);
|
||||
}
|
||||
|
||||
public Matrix<T> tril(T diagValue) {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), (i, j) -> {
|
||||
T result;
|
||||
if (i > j) {
|
||||
result = get(i, j);
|
||||
} else if (i == j) {
|
||||
result = diagValue;
|
||||
} else {
|
||||
result = numericTypeFactory.getZero();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public Vector<T> luSolve(Vector<T> b, Pivot pivot) {
|
||||
Objects.requireNonNull(b);
|
||||
PivotImpl pivotImpl = (PivotImpl) pivot;
|
||||
int[] pivotValues = pivotImpl.getValues();
|
||||
int size = getRows();
|
||||
if (pivotValues.length != size) throw new SizeException(
|
||||
String.format("Pivot length is %d must be %d instead", pivotValues.length, size));
|
||||
|
||||
for (int i = 0; i < pivotValues.length; i++) pivotValues[i] = i;
|
||||
Vector<T> x = Vector.of(numericTypeFactory, size);
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
x.set(i, b.get(pivotValues[i]));
|
||||
for (int k = 0; k < i; k++) {
|
||||
x.set(i, x.get(i).sub(get(i, k).mul(x.get(k))));
|
||||
}
|
||||
}
|
||||
int i = size;
|
||||
while (i-- > 0) {
|
||||
for (int k = i + 1; k < size; k++) {
|
||||
x.set(i, x.get(i).sub(get(i, k).mul(x.get(k))));
|
||||
}
|
||||
if (!Objects.equals(get(i, i), numericTypeFactory.getZero())) {
|
||||
x.set(i, x.get(i).div(get(i, i)));
|
||||
} else throw new SingularMatrixException("Matrix is singular");
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('[');
|
||||
for (int i = 0; i < getRows(); i++) {
|
||||
sb.append('[');
|
||||
for (int j = 0; j < getRows(); j++) {
|
||||
if (j > 0) sb.append(", ");
|
||||
sb.append(get(i, j));
|
||||
}
|
||||
sb.append(']');
|
||||
sb.append('\n');
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix<T> clone() {
|
||||
return of(numericTypeFactory, getRows(), getColumns(), this::get);
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Matrix<T> identity(NumericTypeFactory<T> numericTypeFactory, int size) {
|
||||
return of(numericTypeFactory, size, size, (i, j) -> {
|
||||
T result;
|
||||
if (i == j) result = numericTypeFactory.getOne();
|
||||
else result = numericTypeFactory.getZero();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public T luDet() {
|
||||
require(() -> getRows() == getColumns()).otherwise(SizeException.class, "Matrix must be square");
|
||||
Matrix<T> clone = clone();
|
||||
PivotImpl pivot = (PivotImpl) clone.lup();
|
||||
T result = numericTypeFactory.getOne();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
result = result.mul(clone.get(i, i));
|
||||
}
|
||||
if (pivot.permutations % 2 != 0) {
|
||||
result = result.mul(numericTypeFactory.getMinusOne());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Matrix<T> transpose() {
|
||||
return Matrix.of(numericTypeFactory, getColumns(), getRows(), (i, j) -> get(j, i));
|
||||
}
|
||||
|
||||
public Matrix<T> mmul(Matrix<T> m2) {
|
||||
return Matrix.of(numericTypeFactory, getRows(), m2.getColumns(), (i, j) -> {
|
||||
T result = numericTypeFactory.getZero();
|
||||
for (int k = 0; k < getColumns(); k++) {
|
||||
result = result.add(get(i, k).mul(m2.get(k, j)));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public Vector<T> mmul(Vector<T> v) {
|
||||
return Vector.of(numericTypeFactory, getRows(), i -> {
|
||||
int columns = getColumns();
|
||||
T result = numericTypeFactory.getZero();
|
||||
for (int j = 0; j < columns; j++) {
|
||||
result = result.add(get(i, j).mul(v.get(j)));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
// public T luDet() {
|
||||
// if(getRows() != getColumns()) {
|
||||
// throw newThrowable(
|
||||
// SizeException.class,
|
||||
// "Matrix must be square in order to compute the determinant");
|
||||
// }
|
||||
// int[] pivot = lup();
|
||||
// T result = numericTypeFactory.getOne();
|
||||
// for(int i=0; i<getRows(); i++) {
|
||||
// result = result.mul(get(i, i));
|
||||
// if pivot.permutations mod 2 != 0:
|
||||
// result *= -1
|
||||
// }
|
||||
//
|
||||
// proc lu_det*[T](m : HMatrix[T]) : T =
|
||||
// if m.rows != m.columns:
|
||||
// raise newException(SizeError, "Matrix must be square in order to compute the determinant")
|
||||
// var clone = m.clone()
|
||||
// lu_det(clone)
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof Matrix<? extends NumericType<?>> other))
|
||||
return false;
|
||||
if (getRows() != other.getRows() || getColumns() != other.getColumns()) {
|
||||
return false;
|
||||
}
|
||||
int r = getRows();
|
||||
int c = getColumns();
|
||||
for (int i = 0; i < r; i++) {
|
||||
for (int j = 0; j < c; j++) {
|
||||
if (!Objects.equals(get(i, j), other.get(i, j))) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public T squaredNorm2() {
|
||||
T result = numericTypeFactory.getZero();
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < columns; j++) {
|
||||
T value = get(i, j);
|
||||
result = result.add(value.mul(value));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public T norm2() {
|
||||
return squaredNorm2().sqrt();
|
||||
}
|
||||
}
|
10
jmath/src/main/java/net/woggioni/jmath/NumericType.java
Normal file
10
jmath/src/main/java/net/woggioni/jmath/NumericType.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
public interface NumericType<T> extends Comparable<T> {
|
||||
T add(T other);
|
||||
T sub(T other);
|
||||
T mul(T other);
|
||||
T div(T other);
|
||||
T abs();
|
||||
T sqrt();
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
public interface NumericTypeFactory<T extends NumericType<T>> {
|
||||
T getZero();
|
||||
T getOne();
|
||||
default T getMinusOne() {
|
||||
return getZero().sub(getOne());
|
||||
}
|
||||
T[] getArray(int size);
|
||||
}
|
125
jmath/src/main/java/net/woggioni/jmath/Rational.java
Normal file
125
jmath/src/main/java/net/woggioni/jmath/Rational.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.woggioni.jmath.BigIntegerExt.mcm;
|
||||
|
||||
public class Rational implements NumericType<Rational> {
|
||||
|
||||
public static final Rational ZERO = new Rational(0, 1);
|
||||
public static final Rational ONE = new Rational(1, 1);
|
||||
public static final Rational MINUS_ONE = new Rational(-1, 1);
|
||||
public static final BigInteger MINUS_ONE_BI = BigInteger.ZERO.subtract(BigInteger.ONE);
|
||||
|
||||
private BigInteger num;
|
||||
private BigInteger den;
|
||||
|
||||
@Override
|
||||
public Rational add(Rational other) {
|
||||
return new Rational(
|
||||
this.num.multiply(other.den).add(other.num.multiply(this.den)),
|
||||
this.den.multiply(other.den)
|
||||
).simplify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational sub(Rational other) {
|
||||
return new Rational(
|
||||
this.num.multiply(other.den).subtract(other.num.multiply(this.den)),
|
||||
this.den.multiply(other.den)
|
||||
).simplify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational mul(Rational other) {
|
||||
return new Rational(this.num.multiply(other.num), this.den.multiply(other.den)).simplify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational div(Rational other) {
|
||||
return new Rational(this.num.multiply(other.den), this.den.multiply(other.num)).simplify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational sqrt() {
|
||||
return new Rational(num.sqrt(), den.sqrt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational abs() {
|
||||
return new Rational(num.abs(), den.abs());
|
||||
}
|
||||
|
||||
public Rational(BigInteger num, BigInteger den) {
|
||||
this.num = num;
|
||||
this.den = den;
|
||||
}
|
||||
|
||||
public Rational(long num, long den) {
|
||||
this.num = BigInteger.valueOf(num);
|
||||
this.den = BigInteger.valueOf(den);
|
||||
}
|
||||
|
||||
public static Rational of(long num, long den) {
|
||||
return new Rational(num, den);
|
||||
}
|
||||
|
||||
public static Rational of(long n) {
|
||||
return new Rational(n, 1);
|
||||
}
|
||||
|
||||
private Rational simplify() {
|
||||
BigInteger gcd = BigIntegerExt.gcd(num.abs(), den.abs());
|
||||
num = num.divide(gcd);
|
||||
den = den.divide(gcd);
|
||||
if(den.compareTo(BigInteger.ZERO) < 0) {
|
||||
num = num.multiply(MINUS_ONE_BI);
|
||||
den = den.multiply(MINUS_ONE_BI);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public BigInteger getNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
public BigInteger getDen() {
|
||||
return den;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
String result;
|
||||
if (Objects.equals(BigInteger.ZERO, num) && !Objects.equals(BigInteger.ZERO, den)) result = "0";
|
||||
else if (Objects.equals(BigInteger.ONE, den.abs())) result = num.multiply(den.abs().divide(den)).toString();
|
||||
else {
|
||||
boolean negative = Objects.equals(num, num.abs()) ^ Objects.equals(den, den.abs());
|
||||
result = String.format("%s%d/%d", negative ? "-" : "", num.abs(), den.abs());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Rational rational = (Rational) o;
|
||||
BigInteger mcm = mcm(den, rational.den);
|
||||
BigInteger v1 = mcm.divide(den).multiply(num);
|
||||
BigInteger v2 = mcm.divide(rational.den).multiply(rational.num);
|
||||
return Objects.equals(v1, v2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(num, den);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Rational o) {
|
||||
BigInteger mcm = mcm(getDen(), o.getDen());
|
||||
BigInteger n1 = mcm.divide(getDen()).multiply(getNum());
|
||||
BigInteger n2 = mcm.divide(o.getDen()).multiply(o.getNum());
|
||||
return n1.compareTo(n2);
|
||||
}
|
||||
}
|
23
jmath/src/main/java/net/woggioni/jmath/RationalFactory.java
Normal file
23
jmath/src/main/java/net/woggioni/jmath/RationalFactory.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class RationalFactory implements NumericTypeFactory<Rational> {
|
||||
@Override
|
||||
public Rational getZero() {
|
||||
return Rational.ZERO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational getOne() {
|
||||
return Rational.ONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rational[] getArray(int size) {
|
||||
return new Rational[size];
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static final NumericTypeFactory<Rational> instance = new RationalFactory();
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
public class SingularMatrixException extends RuntimeException {
|
||||
public SingularMatrixException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
public class SizeException extends RuntimeException {
|
||||
public SizeException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
142
jmath/src/main/java/net/woggioni/jmath/Vector.java
Normal file
142
jmath/src/main/java/net/woggioni/jmath/Vector.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import static net.woggioni.jwo.Requirement.require;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Vector<T extends NumericType<T>> {
|
||||
private final NumericTypeFactory<T> numericTypeFactory;
|
||||
private final T[] values;
|
||||
|
||||
private Vector(NumericTypeFactory<T> numericTypeFactory, int size) {
|
||||
this(numericTypeFactory, numericTypeFactory.getArray(size));
|
||||
}
|
||||
|
||||
private Vector(NumericTypeFactory<T> numericTypeFactory, int size, IntFunction<T> valueGenerator) {
|
||||
this(numericTypeFactory, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
set(i, valueGenerator.apply(i));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return values.length;
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return values[index];
|
||||
}
|
||||
|
||||
public void set(int index, T value) {
|
||||
values[index] = value;
|
||||
}
|
||||
|
||||
private void requireSameSize(Vector<T> other) {
|
||||
require(() -> other.size() == size()).otherwise(SizeException.class, "Vectors must be of same size");
|
||||
}
|
||||
|
||||
public Vector<T> sum(Vector<T> other) {
|
||||
requireSameSize(other);
|
||||
return new Vector<>(numericTypeFactory, size(), index -> get(index).add(other.get(index)));
|
||||
}
|
||||
|
||||
public Vector<T> sub(Vector<T> other) {
|
||||
requireSameSize(other);
|
||||
return new Vector<>(numericTypeFactory, size(), index -> get(index).sub(other.get(index)));
|
||||
}
|
||||
|
||||
public Vector<T> mul(Vector<T> other) {
|
||||
requireSameSize(other);
|
||||
return new Vector<>(numericTypeFactory, size(), index -> get(index).mul(other.get(index)));
|
||||
}
|
||||
|
||||
public Vector<T> div(Vector<T> other) {
|
||||
requireSameSize(other);
|
||||
return new Vector<>(numericTypeFactory, size(), index -> get(index).div(other.get(index)));
|
||||
}
|
||||
|
||||
public Vector<T> mul(Matrix<T> m) {
|
||||
return Vector.of(numericTypeFactory, size(), i -> {
|
||||
T result = numericTypeFactory.getZero();
|
||||
for (int j = 0; j < m.getRows(); j++) {
|
||||
result = result.add(get(j).mul(m.get(j, i)));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public T innerProduct(Vector<T> other) {
|
||||
requireSameSize(other);
|
||||
T result = numericTypeFactory.getZero();
|
||||
int sz = size();
|
||||
for (int i = 0; i < sz; i++)
|
||||
result = result.add(get(i).mul(other.get(i)));
|
||||
return result;
|
||||
}
|
||||
|
||||
public T norm() {
|
||||
T result = numericTypeFactory.getZero();
|
||||
int sz = size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
T value = get(i);
|
||||
result = result.add(value.mul(value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public T abs() {
|
||||
return norm().sqrt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector<T> clone() {
|
||||
int sz = size();
|
||||
Vector<T> result = new Vector<>(numericTypeFactory, sz);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
result.set(i, get(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Vector vector)) {
|
||||
return false;
|
||||
}
|
||||
if (size() != vector.size()) return false;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!Objects.equals(get(i), vector.get(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('[');
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (i > 0) sb.append(", ");
|
||||
sb.append(get(i));
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Vector<T> of(NumericTypeFactory<T> numericTypeFactory, T... values) {
|
||||
return new Vector<>(numericTypeFactory, values);
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Vector<T> of(NumericTypeFactory<T> numericTypeFactory, int size) {
|
||||
return new Vector<>(numericTypeFactory, size);
|
||||
}
|
||||
|
||||
public static <T extends NumericType<T>> Vector<T> of(NumericTypeFactory<T> numericTypeFactory,
|
||||
int size,
|
||||
IntFunction<T> generator) {
|
||||
return new Vector<>(numericTypeFactory, size, generator);
|
||||
}
|
||||
}
|
206
jmath/src/test/java/net/woggioni/jmath/MatrixTest.java
Normal file
206
jmath/src/test/java/net/woggioni/jmath/MatrixTest.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class MatrixTest {
|
||||
|
||||
private static Matrix<Rational> testMatrix;
|
||||
private static Matrix<IntegerValue> integerTestMatrix;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
testMatrix = Matrix.of(RationalFactory.getInstance(), new Rational[][]{
|
||||
new Rational[]{
|
||||
Rational.ONE, Rational.ZERO, Rational.of(-1, 2), Rational.ZERO, Rational.ZERO
|
||||
},
|
||||
new Rational[]{
|
||||
Rational.ZERO, Rational.ONE, Rational.of(-1, 2), Rational.ZERO, Rational.ZERO
|
||||
},
|
||||
new Rational[]{
|
||||
Rational.of(-4, 9), Rational.ZERO, Rational.ONE, Rational.ZERO, Rational.ZERO
|
||||
},
|
||||
new Rational[]{
|
||||
Rational.of(-1, 3), Rational.ZERO, Rational.ZERO, Rational.ONE, Rational.ZERO
|
||||
},
|
||||
new Rational[]{
|
||||
Rational.of(-2, 9), Rational.ZERO, Rational.ZERO, Rational.ZERO, Rational.ONE
|
||||
},
|
||||
});
|
||||
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
integerTestMatrix = Matrix.of(integerFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
}
|
||||
|
||||
@Test
|
||||
void trilTest() {
|
||||
NumericTypeFactory<IntegerValue> rationalFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> expected = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(1, 0, 0, 4, 5, 0, 7, 8, 9).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected, integerTestMatrix.tril());
|
||||
Matrix<IntegerValue> expected2 = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(-3, 0, 0, 4, -3, 0, 7, 8, -3).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected2, integerTestMatrix.tril(IntegerValue.of(-3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void triuTest() {
|
||||
NumericTypeFactory<IntegerValue> rationalFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> expected = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 0, 5, 6, 0, 0, 9).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected, integerTestMatrix.triu());
|
||||
Matrix<IntegerValue> expected2 = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(-3, 2, 3, 0, -3, 6, 0, 0, -3).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected2, integerTestMatrix.triu(IntegerValue.of(-3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void transposeTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> m = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> IntegerValue.of(1 + i * 3L + j));
|
||||
Matrix<IntegerValue> transpose = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> m.get(j, i));
|
||||
Assertions.assertEquals(transpose, m.transpose());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInverse() {
|
||||
NumericTypeFactory<Rational> rationalFactory = RationalFactory.getInstance();
|
||||
int[] nums = new int[]{1, 2, 3, 4, 5, 6, 8, 7, 9};
|
||||
Matrix<Rational> m = Matrix.of(rationalFactory, 3, 3,
|
||||
(i, j) -> Rational.of(nums[i * 3 + j], 1));
|
||||
Matrix<Rational> inverse = m.invert();
|
||||
Matrix<Rational> identity = Matrix.identity(rationalFactory, 3);
|
||||
Assertions.assertEquals(identity, m.mmul(inverse));
|
||||
Assertions.assertEquals(identity, inverse.mmul(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> m2 = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> integerTestMatrix.get(i, j).mul(integerFactory.getMinusOne()));
|
||||
Matrix<IntegerValue> expected = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> integerFactory.getZero());
|
||||
Assertions.assertEquals(expected, integerTestMatrix.add(m2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void subTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> expected = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> integerFactory.getZero());
|
||||
Assertions.assertEquals(expected, integerTestMatrix.sub(integerTestMatrix));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mulTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> expected = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> integerTestMatrix.get(i, j).mul(integerTestMatrix.get(i, j)));
|
||||
Assertions.assertEquals(expected, integerTestMatrix.mul(integerTestMatrix));
|
||||
}
|
||||
|
||||
@Test
|
||||
void divTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> expected = Matrix.of(integerFactory, 3, 3,
|
||||
(i, j) -> IntegerValue.of(1));
|
||||
Assertions.assertEquals(expected, integerTestMatrix.div(integerTestMatrix));
|
||||
}
|
||||
|
||||
@Test
|
||||
void linProdTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> m = Matrix.of(integerFactory, 2, 5,
|
||||
IntStream.of(1, 4, 8, 2, 5, 7, 3, 6, 9, 0).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Matrix<IntegerValue> expected = Matrix.of(integerFactory, 2, 2,
|
||||
IntStream.of(110, 85, 85, 175).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected, m.mmul(m.transpose()));
|
||||
|
||||
Matrix<IntegerValue> expected2 = Matrix.of(integerFactory, 5, 5,
|
||||
IntStream.of(50, 25, 50, 65, 5,
|
||||
25, 25, 50, 35, 20,
|
||||
50, 50, 100, 70, 40,
|
||||
65, 35, 70, 85, 10,
|
||||
5, 20, 40, 10, 25)
|
||||
.mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected2, m.transpose().mmul(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
void linProdVectorTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> m = Matrix.of(integerFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 4, 5, 6, 8, 7, 9).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Vector<IntegerValue> v = Vector.of(integerFactory,
|
||||
IntStream.of(1, 2, 3).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Vector<IntegerValue> expected = Vector.of(integerFactory,
|
||||
IntStream.of(14, 32, 49).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected, m.mmul(v));
|
||||
}
|
||||
|
||||
@Test
|
||||
void determinantTest() {
|
||||
NumericTypeFactory<Rational> rationalFactory = RationalFactory.getInstance();
|
||||
Matrix<Rational> m = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 4, 5, 6, 8, 7, 9).mapToObj(Rational::of).toArray(Rational[]::new));
|
||||
Assertions.assertEquals(Rational.of(-9), m.det());
|
||||
Assertions.assertEquals(Rational.of(-9), m.luDet());
|
||||
Matrix<Rational> m2 = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).mapToObj(Rational::of).toArray(Rational[]::new));
|
||||
Assertions.assertEquals(Rational.ZERO, m2.det());
|
||||
Assertions.assertEquals(Rational.ZERO, m2.luDet());
|
||||
}
|
||||
|
||||
@Test
|
||||
void luTest() {
|
||||
NumericTypeFactory<Rational> rationalFactory = RationalFactory.getInstance();
|
||||
Matrix<Rational> m = Matrix.of(rationalFactory, 3, 3,
|
||||
IntStream.of(8, 7, 9, 1, 2, 3, 4, 5, 6).mapToObj(Rational::of).toArray(Rational[]::new));
|
||||
Matrix<Rational> lu = m.clone();
|
||||
Matrix.Pivot p = lu.lup();
|
||||
Matrix<Rational> result = p.mul(lu.tril(Rational.ONE).mmul(lu.triu()));
|
||||
Assertions.assertEquals(m, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void mmulTest() {
|
||||
NumericTypeFactory<IntegerValue> integerFactory = IntegerFactory.getInstance();
|
||||
Matrix<IntegerValue> m = Matrix.of(integerFactory, 3, 3,
|
||||
IntStream.of(1, 2, 3, 4, 5, 6, 8, 7, 9).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Vector<IntegerValue> v = Vector.of(
|
||||
integerFactory, IntStream.of(1, 2, 3)
|
||||
.mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Vector<IntegerValue> expected = Vector.of(integerFactory,
|
||||
IntStream.of(33, 33, 42).mapToObj(IntegerValue::of).toArray(IntegerValue[]::new));
|
||||
Assertions.assertEquals(expected, v.mul(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linearSystemTest() {
|
||||
Vector<Rational> expectedSolution = Vector.of(RationalFactory.getInstance(),
|
||||
Rational.of(9, 14),
|
||||
Rational.of(9, 14),
|
||||
Rational.of(2, 7),
|
||||
Rational.of(3, 14),
|
||||
Rational.of(1, 7)
|
||||
);
|
||||
|
||||
Vector<Rational> b = Vector.of(RationalFactory.getInstance(),
|
||||
Rational.of(1, 2), Rational.of(1, 2), Rational.ZERO, Rational.ZERO, Rational.ZERO
|
||||
);
|
||||
|
||||
Vector<Rational> solution = testMatrix.solve(b);
|
||||
|
||||
Assertions.assertEquals(expectedSolution, solution);
|
||||
Matrix<Rational> inverse = testMatrix.invert();
|
||||
Assertions.assertEquals(expectedSolution, inverse.mmul(b));
|
||||
}
|
||||
|
||||
}
|
102
jmath/src/test/java/net/woggioni/jmath/RationalTest.java
Normal file
102
jmath/src/test/java/net/woggioni/jmath/RationalTest.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package net.woggioni.jmath;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import static java.math.BigInteger.valueOf;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class RationalTest {
|
||||
|
||||
private enum Operation {
|
||||
ADD, SUBTRACT, MULTIPLY, DIVIDE
|
||||
}
|
||||
|
||||
private static class GcdTestCaseProvider implements ArgumentsProvider {
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||
return Stream.of(
|
||||
Arguments.of(valueOf(10), valueOf(25), valueOf(5)),
|
||||
Arguments.of(valueOf(9), valueOf(7), valueOf(1)),
|
||||
Arguments.of(valueOf(101), valueOf(3), valueOf(1)),
|
||||
Arguments.of(valueOf(5), valueOf(105), valueOf(5)),
|
||||
Arguments.of(valueOf(91), valueOf(91), valueOf(91)),
|
||||
Arguments.of(valueOf(45), valueOf(81), valueOf(9)),
|
||||
Arguments.of(valueOf(1), valueOf(1), valueOf(1)),
|
||||
Arguments.of(valueOf(11), valueOf(34), valueOf(1))
|
||||
);
|
||||
}
|
||||
}
|
||||
@ParameterizedTest(name="input: {0}, {1}, expected outcome: {2}")
|
||||
@ArgumentsSource(GcdTestCaseProvider.class)
|
||||
void gcdTest(BigInteger n1, BigInteger n2, BigInteger expected) {
|
||||
Assertions.assertEquals(expected, BigIntegerExt.gcd(n1, n2));
|
||||
}
|
||||
|
||||
private static class RationalTestCaseProvider implements ArgumentsProvider {
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
Rational.of(3, 4), Rational.of(1, 5),
|
||||
Operation.ADD,
|
||||
Rational.of(19, 20)),
|
||||
Arguments.of(
|
||||
Rational.of(1, 2), Rational.of(1, 2),
|
||||
Operation.ADD,
|
||||
Rational.of(1, 1)),
|
||||
Arguments.of(
|
||||
Rational.of(3, 4), Rational.of(2, 3),
|
||||
Operation.MULTIPLY,
|
||||
Rational.of(1, 2)),
|
||||
Arguments.of(
|
||||
Rational.of(4, 5), Rational.of(3, 2),
|
||||
Operation.DIVIDE,
|
||||
Rational.of(8, 15)),
|
||||
Arguments.of(
|
||||
Rational.of(1, 1), Rational.of(2, 1),
|
||||
Operation.SUBTRACT,
|
||||
Rational.of(1, -1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest(name="input: {0}, {1}, operation: {2}, expected outcome: {3}")
|
||||
@ArgumentsSource(RationalTestCaseProvider.class)
|
||||
void rationalTest(Rational r1, Rational r2, Operation operation, Rational expected) {
|
||||
Rational result;
|
||||
switch (operation) {
|
||||
case ADD:
|
||||
result = r1.add(r2);
|
||||
break;
|
||||
case SUBTRACT:
|
||||
result = r1.sub(r2);
|
||||
break;
|
||||
case MULTIPLY:
|
||||
result = r1.mul(r2);
|
||||
break;
|
||||
case DIVIDE:
|
||||
result = r1.div(r2);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("This should never happen");
|
||||
}
|
||||
Assertions.assertEquals(expected, result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void sumTest() {
|
||||
Rational s5 = Rational.of(9, 22);
|
||||
Rational s6 = Rational.of(3, 22);
|
||||
Rational s4 = Rational.of(5, 22);
|
||||
Rational s3 = Rational.of(5, 22);
|
||||
Assertions.assertEquals(Rational.ONE, s5.add(s6).add(s4).add(s3));
|
||||
}
|
||||
}
|
@@ -31,3 +31,4 @@ dependencyResolutionManagement {
|
||||
rootProject.name = 'jwo'
|
||||
|
||||
include('benchmark')
|
||||
include('jmath')
|
||||
|
@@ -22,6 +22,11 @@ public class Hash {
|
||||
SHA512("SHA-512");
|
||||
|
||||
private final String key;
|
||||
|
||||
@SneakyThrows
|
||||
public MessageDigest newMessageDigest() {
|
||||
return MessageDigest.getInstance(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
25
src/main/java/net/woggioni/jwo/Requirement.java
Normal file
25
src/main/java/net/woggioni/jwo/Requirement.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.woggioni.jwo;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.woggioni.jwo.JWO.newThrowable;
|
||||
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Requirement {
|
||||
private final Supplier<Boolean> booleanSupplier;
|
||||
|
||||
public static Requirement require(Supplier<Boolean> booleanSupplier) {
|
||||
return new Requirement(booleanSupplier);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public <T extends Throwable> void otherwise(Class<T> cls, String format, Object... args) {
|
||||
if(!booleanSupplier.get()) {
|
||||
throw newThrowable(cls, format, args);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user