From 2628ee4cbea9c8001caa5c9775a51054024e4908 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 28 Mar 2021 12:40:20 +0100 Subject: [PATCH 01/40] math: create math module configuration --- math/build.gradle | 0 math/lombok.config | 3 +++ settings.gradle | 1 + 3 files changed, 4 insertions(+) create mode 100644 math/build.gradle create mode 100644 math/lombok.config diff --git a/math/build.gradle b/math/build.gradle new file mode 100644 index 00000000..e69de29b diff --git a/math/lombok.config b/math/lombok.config new file mode 100644 index 00000000..189c0bef --- /dev/null +++ b/math/lombok.config @@ -0,0 +1,3 @@ +# This file is generated by the 'io.freefair.lombok' Gradle plugin +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true diff --git a/settings.gradle b/settings.gradle index d107c168..6395b49b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,6 @@ rootProject.name = "stickyapi" include ":common" include ":common:serverversion" // subproject that's shaded into common include ":config" // common subproject; not included in common jar +include ":math" include ":bukkit" include ":bungee" From c531e506e431d2e9e3da4e986d0f9314878670d9 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 28 Mar 2021 12:41:00 +0100 Subject: [PATCH 02/40] math: Interpolation --- .../stickyapi/math/Interpolation.java | 88 +++++++++++++++++++ .../stickyapi/math/InterpolationTest.java | 40 +++++++++ 2 files changed, 128 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java new file mode 100644 index 00000000..f3fdb5cb --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import com.google.common.base.Preconditions; + +/** + * Utility class for interpolating between values. + */ +public final class Interpolation { + private Interpolation() {} + + /** + * Represents a cubic bezier curve. + */ + public class CubicBezier { + private double a; + private double b; + private double c; + private double d; + + /** + * Construct a new cubic bezier with the specified parameters + * @param a The first parameter + * @param b The second parameter + * @param c The third parameter + * @param d The fourth parameter + */ + public CubicBezier(double a, double b, double c, double d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + /** + * Evaluate this cubic bezier for time t. + * @param t The time parameter + * @return A value based on the cubic bezier. + */ + public double evaluate(Number t) { + return t.doubleValue(); + } + } + + /** + * Clamp the value `t` between `min` and `max`. + * @param max The minimum value + * @param min The maximum value + * @param t The value to clamp + * @return The clamped value between `max` and `min`. + */ + public static double clamp(Number min, Number max, Number t) { + Preconditions.checkNotNull(min); + Preconditions.checkNotNull(max); + Preconditions.checkNotNull(t); + return Double.min(max.doubleValue(), Double.max(min.doubleValue(), t.doubleValue())); + } + + /** + * Linearly interpolate between a and b with time parameter t. + * `t` is clamped between `0` and `1`. + * @param a The first value + * @param b The second value + * @param t The time parameter + * @return The linearly interpolated value between `a` and `b`. + */ + public static double lerp(Number a, Number b, Number t) { + return lerpUnclamped(a, b, clamp(0, 1, t)); + } + + /**s + * Linearly interpolate between a and b with time parameter t. + * `t` is not clamped between `0` and `1`. + * @param a The first value + * @param b The second value + * @param t The time parameter + * @return The linearly interpolated value. + */ + public static double lerpUnclamped(Number a, Number b, Number t) { + Preconditions.checkNotNull(a); + Preconditions.checkNotNull(b); + Preconditions.checkNotNull(t); + return b.doubleValue() * t.doubleValue() + (1 - t.doubleValue()) * a.doubleValue(); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java new file mode 100644 index 00000000..68cf7574 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class InterpolationTest { + @Test() + void testUnclampedLerp() { + // test lerp 0 => 1, t = 0.5 + Assertions.assertEquals(0.5, Interpolation.lerpUnclamped(0, 1, 0.5)); + // test lerp 0 => 2, t = 0.5 + Assertions.assertEquals(1.0, Interpolation.lerpUnclamped(0, 2, 0.5)); + // test lerp 0 => 1, t = 2 + Assertions.assertEquals(2.0, Interpolation.lerpUnclamped(0, 1, 2)); + } + + @Test() + void testClamp() { + // test clamp 0 => 1, t = 0.5 + Assertions.assertEquals(0.5, Interpolation.clamp(0, 1, 0.5)); + // test clamp 0 => 2, t = 0.5 + Assertions.assertEquals(0.5, Interpolation.clamp(0, 2, 0.5)); + // test clamp 0 => 1, t = 2 + Assertions.assertEquals(1.0, Interpolation.clamp(0, 1, 2)); + } + + @Test() + void testLerp() { + // test clamp 0 => 1, t = 0.5 + Assertions.assertEquals(0.5, Interpolation.lerp(0, 1, 0.5)); + // test clamp 0 => 2, t = 0.5 + Assertions.assertEquals(1.0, Interpolation.lerp(0, 2, 0.5)); + // test clamp 0 => 1, t = 2 + Assertions.assertEquals(1.0, Interpolation.lerp(0, 1, 2)); + } +} From 56bc2b24b7c36becae7e6f53833d930af9abead5 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 28 Mar 2021 12:48:45 +0100 Subject: [PATCH 03/40] max: include guava to fix missing Preconditions --- math/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/math/build.gradle b/math/build.gradle index e69de29b..7d4f56a5 100644 --- a/math/build.gradle +++ b/math/build.gradle @@ -0,0 +1,5 @@ +dependencies { + implementation project(":common") + // TODO: Investigate why this isn't provided by common. + implementation "com.google.guava:guava:30.1.1-jre" +} From 05a42aa3447a15e99c8e781ec2b282e1ceb7ec79 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Mon, 29 Mar 2021 12:42:50 +0100 Subject: [PATCH 04/40] wip: initial implementation of vector class --- .../stickyapi/math/transform/Matrix.java | 8 +++ .../stickyapi/math/vector/Vector.java | 57 +++++++++++++++++ .../stickyapi/math/vector/Vector2.java | 64 +++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java new file mode 100644 index 00000000..58dc3904 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.transform; + +public class Matrix { +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java new file mode 100644 index 00000000..1d9d6fce --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.vector; + +import com.sun.net.httpserver.HttpServer; + +/** + * Base abstract class for an immutable vector. + * @param + */ +abstract class Vector { + /** + * @return The number of dimensions this vector has. + */ + abstract int getDimensions(); + + abstract Vector add(Vector vector); + + abstract Vector subtract(Vector vector); + + /** + * Return the vector from this vector to the target vector. + * @param vector The target vector + * @return The vector from this vector to the target vector. + */ + Vector to(Vector vector) { + return vector.subtract(vector); + }; + + /** + * Scale this vector by the given scalar. + * @param scalar The scalar to scale by + * @return The scaled vector. + */ + abstract Vector scale(Number scalar); + + /** + * @return The magnitude of this vector. + */ + abstract double abs(); + + /** + * @return A unit vector of this vector. + */ + Vector normalize() { + return this.scale(this.abs()); + } + + /** + * Compute the dot product of two vectors. + * @param vec The other vectors + * @return The dot product of the two vectors. + */ + abstract double dot(T vec); +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java new file mode 100644 index 00000000..14e29283 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.vector; + +import lombok.Getter; + +/** + * Represents a two dimensional vector. + */ +public class Vector2 extends Vector { + /** + * The x value of this vector. + */ + @Getter + private double x; + + /** + * The y value of this vector. + */ + @Getter + private double y; + + /** + * Construct a new 2D vector. + * @param x The x value + * @param y The y value + */ + public Vector2(double x, double y) { + this.x = x; + this.y = y; + } + + @Override + int getDimensions() { + return 2; + } + + @Override + Vector add(Vector vector) { + return new Vector2(this.x + vector.x, this.y + vector.y); + } + + @Override + Vector subtract(Vector vector) { + return null; + } + + @Override + Vector2 scale(Number scalar) { + return new Vector2(this.x * scalar.doubleValue(), this.y * scalar.doubleValue()); + } + + @Override + double abs() { + return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); + } + + @Override + double dot(Vector2 vec) { + return this.x * vec.x + this.y * vec.y; + } +} From f495c46527029786a51b373181f88428651e20be Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 12:57:37 +0100 Subject: [PATCH 05/40] add ordinal generator --- .../dumbdogdiner/stickyapi/math/Ordinal.java | 50 +++++++++++++++++ .../stickyapi/math/OrdinalTest.java | 55 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java new file mode 100644 index 00000000..a73997fe --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import com.google.common.base.Preconditions; + +/** + * Utility class for generating ordinals e.g. `1st, 2nd, 3rd` + */ +public final class Ordinal { + private Ordinal() {} + + /** + * Get the appropriate ordinal for the + * @param number The number to fetch the ordinal for + * @return A {@link String} containing the appropriate ordinal. + */ + public static String getOrdinal(int number) { + Preconditions.checkArgument(number > 0); + // store modulo 10 and 100 of target - useful for calculating things + int mod10 = number % 10; + int mod100= number % 100; + // st is used with numbers ending in 1 (e.g. 1st, pronounced first) + if (mod10 == 1 && mod100 != 11) { + return "st"; + } + // nd is used with numbers ending in 2 (e.g. 92nd, pronounced ninety-second) + if (mod10 == 2 && mod100 != 12) { + return "nd"; + } + // rd is used with numbers ending in 3 (e.g. 33rd, pronounced thirty-third) + // As an exception to the above rules, all the "teen" numbers ending with 11, 12 or 13 use -th (e.g. 11th, pronounced eleventh, 112th, pronounced one hundred [and] twelfth) + if (mod10 == 3 && mod100 != 13) { + return "rd"; + } + // th is used for all other numbers (e.g. 9th, pronounced ninth). + return "th"; + } + + /** + * Return a number formatted with its ordinal. + * @param number The number to format with its ordinal + * @return A {@link String} containing the number with its ordinal. + */ + public static String withOrdinal(int number) { + return number + getOrdinal(number); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java new file mode 100644 index 00000000..c2296aef --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class OrdinalTest { + @Test() + void testFirst() { + Assertions.assertEquals("st", Ordinal.getOrdinal(1)); + // mod 10 + Assertions.assertEquals("st", Ordinal.getOrdinal(21)); + // mod 100 + Assertions.assertEquals("st", Ordinal.getOrdinal(101)); + } + + @Test() + void testSecond() { + Assertions.assertEquals("nd", Ordinal.getOrdinal(2)); + // mod 10 + Assertions.assertEquals("nd", Ordinal.getOrdinal(22)); + // mod 100 + Assertions.assertEquals("nd", Ordinal.getOrdinal(102)); + } + + @Test() + void testThird() { + Assertions.assertEquals("rd", Ordinal.getOrdinal(3)); + // mod 10 + Assertions.assertEquals("rd", Ordinal.getOrdinal(23)); + // mod 100 + Assertions.assertEquals("rd", Ordinal.getOrdinal(103)); + } + + @Test() + void testTeenException() { + Assertions.assertEquals("th", Ordinal.getOrdinal(11)); + Assertions.assertEquals("th", Ordinal.getOrdinal(12)); + Assertions.assertEquals("th", Ordinal.getOrdinal(13)); + // mod 100 + Assertions.assertEquals("th", Ordinal.getOrdinal(111)); + } + + @Test() + void testGeneral() { + Assertions.assertEquals("th", Ordinal.getOrdinal(4)); + // mod 10 + Assertions.assertEquals("th", Ordinal.getOrdinal(14)); + // mod 100 + Assertions.assertEquals("th", Ordinal.getOrdinal(104)); + } +} From db633ee9402266389ff62cac1a231aea40643522 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 12:59:21 +0100 Subject: [PATCH 06/40] add missing space --- math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java index a73997fe..37855317 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java @@ -21,7 +21,7 @@ public static String getOrdinal(int number) { Preconditions.checkArgument(number > 0); // store modulo 10 and 100 of target - useful for calculating things int mod10 = number % 10; - int mod100= number % 100; + int mod100 = number % 100; // st is used with numbers ending in 1 (e.g. 1st, pronounced first) if (mod10 == 1 && mod100 != 11) { return "st"; From d4714afd4f29162b0cfa069d0223b3b2bbf3766b Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 13:16:37 +0100 Subject: [PATCH 07/40] wip :sparkles: fixup vector definition --- .../stickyapi/math/vector/Vector.java | 64 ++++++++++++-- .../stickyapi/math/vector/Vector2.java | 48 ++++++---- .../stickyapi/math/vector/Vector3.java | 88 +++++++++++++++++++ 3 files changed, 175 insertions(+), 25 deletions(-) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java index 1d9d6fce..eced765c 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -4,7 +4,8 @@ */ package com.dumbdogdiner.stickyapi.math.vector; -import com.sun.net.httpserver.HttpServer; +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; /** * Base abstract class for an immutable vector. @@ -14,18 +15,44 @@ abstract class Vector { /** * @return The number of dimensions this vector has. */ - abstract int getDimensions(); + protected abstract int getDimensions(); - abstract Vector add(Vector vector); + /** + * Get an array of values representing this vector. + * @return An array of values. + */ + protected abstract double[] getValues(); - abstract Vector subtract(Vector vector); + /** + * Get the value of the target dimension. + * @param index The index of the dimension + * @return The value of the target dimension. + */ + protected double getDimension(int index) { + Preconditions.checkArgument(index < this.getDimensions(), "Index out of range for vector dimension."); + return this.getValues()[index]; + } + + /** + * Add the target vector to this vector. + * @param vector The target vector + * @return A new {@link Vector}. + */ + abstract @NotNull Vector add(@NotNull Vector vector); + + /** + * Subtract the target vector from this vector. + * @param vector The target vector + * @return A new {@link Vector}. + */ + abstract @NotNull Vector subtract(@NotNull Vector vector); /** * Return the vector from this vector to the target vector. * @param vector The target vector * @return The vector from this vector to the target vector. */ - Vector to(Vector vector) { + @NotNull Vector to(@NotNull Vector vector) { return vector.subtract(vector); }; @@ -34,17 +61,25 @@ Vector to(Vector vector) { * @param scalar The scalar to scale by * @return The scaled vector. */ - abstract Vector scale(Number scalar); + abstract @NotNull Vector scale(@NotNull Number scalar); /** * @return The magnitude of this vector. */ - abstract double abs(); + double abs() { + double acc = 0; + // iterate over each dimension and append square to accumulator + for (int i = 0; i < this.getDimensions(); i++) { + acc += Math.pow(this.getDimension(i), 2); + } + // return square root of accumulator + return Math.sqrt(acc); + } /** * @return A unit vector of this vector. */ - Vector normalize() { + @NotNull Vector normalize() { return this.scale(this.abs()); } @@ -53,5 +88,16 @@ Vector normalize() { * @param vec The other vectors * @return The dot product of the two vectors. */ - abstract double dot(T vec); + double dot(@NotNull Vector vec) { + // ensure dimensions are equal. + Preconditions.checkNotNull(vec); + Preconditions.checkArgument(this.getDimensions() == vec.getDimensions(), "Cannot compute dot product of vectors with mismatching dimensions."); + double acc = 0; + // iterate over each dimension and append it to accumulator + for (int i = 0; i < this.getDimensions(); i++) { + acc += this.getDimension(i) * vec.getDimension(i); + } + // return the accumulator value. + return acc; + } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java index 14e29283..202cb523 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -4,23 +4,25 @@ */ package com.dumbdogdiner.stickyapi.math.vector; +import com.google.common.base.Preconditions; import lombok.Getter; +import org.jetbrains.annotations.NotNull; /** - * Represents a two dimensional vector. + * Represents a two-dimensional immutable vector. */ public class Vector2 extends Vector { /** * The x value of this vector. */ @Getter - private double x; + private final double x; /** * The y value of this vector. */ @Getter - private double y; + private final double y; /** * Construct a new 2D vector. @@ -32,33 +34,47 @@ public Vector2(double x, double y) { this.y = y; } - @Override - int getDimensions() { - return 2; + /** + * Construct a new 2D vector. + * @param x The x value + * @param y The y value + */ + public Vector2(@NotNull Number x, @NotNull Number y) { + // ensure arguments are not null + Preconditions.checkNotNull(x); + Preconditions.checkNotNull(y); + this.x = x.doubleValue(); + this.y = y.doubleValue(); } @Override - Vector add(Vector vector) { - return new Vector2(this.x + vector.x, this.y + vector.y); + protected int getDimensions() { + return 2; } @Override - Vector subtract(Vector vector) { - return null; + protected double[] getValues() { + return new double[]{this.x, this.y}; } @Override - Vector2 scale(Number scalar) { - return new Vector2(this.x * scalar.doubleValue(), this.y * scalar.doubleValue()); + @NotNull + Vector2 add(@NotNull Vector vector) { + Preconditions.checkNotNull(vector); + return new Vector2(this.x + vector.getDimension(0), this.y + vector.getDimension(1)); } @Override - double abs() { - return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); + @NotNull + Vector2 subtract(@NotNull Vector vector) { + Preconditions.checkNotNull(vector); + return new Vector2(this.x - vector.getDimension(0), this.y - vector.getDimension(1)); } @Override - double dot(Vector2 vec) { - return this.x * vec.x + this.y * vec.y; + @NotNull + Vector2 scale(@NotNull Number scalar) { + Preconditions.checkNotNull(scalar); + return new Vector2(this.x * scalar.doubleValue(), this.y * scalar.doubleValue()); } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java new file mode 100644 index 00000000..59fa33f7 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.vector; + +import com.google.common.base.Preconditions; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +/** + * Represents a three-dimensional immutable vector. + */ +public class Vector3 extends Vector { + /** + * The x value of this vector. + */ + @Getter + private final double x; + + /** + * The y value of this vector. + */ + @Getter + private final double y; + + /** + * The z value of this vector. + */ + @Getter + private final double z; + + /** + * Construct a new 3D vector. + * @param x The x value + * @param y The y value + * @param z The z value + */ + public Vector3(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Construct a new 3D vector. + * @param x The x value + * @param y The y value + * @param z The z value + */ + public Vector3(@NotNull Number x, @NotNull Number y, @NotNull Number z) { + // ensure arguments are not null + Preconditions.checkNotNull(x); + Preconditions.checkNotNull(y); + Preconditions.checkNotNull(z); + this.x = x.doubleValue(); + this.y = y.doubleValue(); + this.z = z.doubleValue(); + } + + @Override + protected int getDimensions() { + return 3; + } + + @Override + protected double[] getValues() { + return new double[]{this.x, this.y, this.z}; + } + + @Override + @NotNull + Vector3 add(@NotNull Vector vector) { + return new Vector3(this.x + vector.getDimension(0), this.y + vector.getDimension(1), this.z + vector.getDimension(3)); + } + + @Override + @NotNull + Vector3 subtract(@NotNull Vector vector) { + return new Vector3(this.x - vector.getDimension(0), this.y - vector.getDimension(1), this.z - vector.getDimension(3)); + } + + @Override + @NotNull + Vector3 scale(@NotNull Number scalar) { + return new Vector3(this.x * scalar.doubleValue(), this.y * scalar.doubleValue(), this.z * scalar.doubleValue()); + } +} From f2ff3dbe0ba56da461dbe9c724f3ec6f54fe64a4 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 13:38:17 +0100 Subject: [PATCH 08/40] wip :sparkles: add vector tests --- .../stickyapi/math/vector/Vector.java | 30 ++++++++++++- .../stickyapi/math/vector/Vector2.java | 14 +++++- .../stickyapi/math/vector/Vector3.java | 11 +++++ .../stickyapi/math/vector/Vector2Test.java | 42 ++++++++++++++++++ .../stickyapi/math/vector/Vector3Test.java | 43 +++++++++++++++++++ 5 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java index eced765c..2c0bf8b1 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -6,12 +6,13 @@ import com.google.common.base.Preconditions; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Base abstract class for an immutable vector. * @param */ -abstract class Vector { +abstract class Vector implements Cloneable { /** * @return The number of dimensions this vector has. */ @@ -33,6 +34,33 @@ protected double getDimension(int index) { return this.getValues()[index]; } + @Override + public boolean equals(Object vector) { + // check if vector is null or is not a vector + if (!(vector instanceof Vector)) { + return false; + } + // iterate over each dimension and append square to accumulator + for (int i = 0; i < this.getDimensions(); i++) { + if (this.getDimension(i) != ((Vector)vector).getDimension(i)) { + return false; + } + } + // all dimensions equal - must be same vector. + return true; + } + + @Override + public int hashCode() { + // use two prime numbers because they're hot and sexy and help with uniqueness + int hash = 7; + // iterate over each dimension and append hash to accumulator + for (int i = 0; i < this.getDimensions(); i++) { + hash = 31 * hash + Double.hashCode(this.getDimension(i)); + } + return hash; + } + /** * Add the target vector to this vector. * @param vector The target vector diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java index 202cb523..5788dfa8 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -16,13 +16,13 @@ public class Vector2 extends Vector { * The x value of this vector. */ @Getter - private final double x; + private double x; /** * The y value of this vector. */ @Getter - private final double y; + private double y; /** * Construct a new 2D vector. @@ -47,6 +47,16 @@ public Vector2(@NotNull Number x, @NotNull Number y) { this.y = y.doubleValue(); } + /** + * Construct a new 2D vector from an existing vector. + * @param target The existing vector + */ + public Vector2(@NotNull Vector2 target) { + Preconditions.checkNotNull(target); + this.x = target.x; + this.y = target.y; + } + @Override protected int getDimensions() { return 2; diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java index 59fa33f7..f5444a02 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java @@ -58,6 +58,17 @@ public Vector3(@NotNull Number x, @NotNull Number y, @NotNull Number z) { this.z = z.doubleValue(); } + /** + * Construct a new 3D vector from an existing vector. + * @param target The existing vector + */ + public Vector3(@NotNull Vector3 target) { + Preconditions.checkNotNull(target); + this.x = target.x; + this.y = target.y; + this.z = target.z; + } + @Override protected int getDimensions() { return 3; diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java new file mode 100644 index 00000000..2b9506a6 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.vector; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class Vector2Test { + @Test() + void testVector2Add() { + Vector2 a = new Vector2(0, 1); + Vector2 b = new Vector2(1, 1); + Assertions.assertEquals(new Vector2(1, 2), a.add(b)); + } + + @Test() + void testVector2Subtract() { + Vector2 a = new Vector2(0, 1); + Vector2 b = new Vector2(1, 1); + Assertions.assertEquals(new Vector2(-1, 0), a.subtract(b)); + } + + @Test() + void testVector2Scale() { + Vector2 a = new Vector2(10, 5); + // check unitary scale + Assertions.assertEquals(a, a.scale(1)); + // check 2 scale + Assertions.assertEquals(new Vector2(20, 10), a.scale(2)); + // check negative scale + Assertions.assertEquals(new Vector2(-10, -5), a.scale(-1)); + } + + @Test() + void testVector2To() { + Vector2 a = new Vector2(0, 1); + Vector2 b = new Vector2(1, 0); + Assertions.assertEquals(new Vector2(1, -1), a.to(b)); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java new file mode 100644 index 00000000..14f059ed --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.vector; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class Vector3Test { + @Test() + void testVector3Add() { + Vector3 a = new Vector3(0, 1, 2); + Vector3 b = new Vector3(1, 1, 1); + Assertions.assertEquals(new Vector3(1, 2, 3), a.add(b)); + } + + @Test() + void testVector3Subtract() { + Vector3 a = new Vector3(0, 1, 2); + Vector3 b = new Vector3(1, 1, 1); + Assertions.assertEquals(new Vector3(-1, 0, 1), a.subtract(b)); + } + + @Test() + void testVector3Scale() { + Vector3 a = new Vector3(10, 5, 20); + // check unitary scale + Assertions.assertEquals(a, a.scale(1)); + // check 2 scale + Assertions.assertEquals(new Vector3(20, 10, 40), a.scale(2)); + // check negative scale + Assertions.assertEquals(new Vector3(-10, -5, -20), a.scale(-1)); + } + + @Test() + void testVector3To() { + Vector3 a = new Vector3(0, 1, 2); + Vector3 b = new Vector3(1, 1, 1); + // TODO: skye do some math and work out what this should be you lazy hoe + Assertions.assertEquals(new Vector3(1, -1, -1), a.to(b)); + } +} From d58024967dd9c8e63a39724065e4e461227a7c2f Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 14:09:22 +0100 Subject: [PATCH 09/40] wip :sparkles: add averages methods --- .../stickyapi/math/stats/Averages.java | 168 ++++++++++++++++++ .../stickyapi/math/stats/AveragesTest.java | 53 ++++++ 2 files changed, 221 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java new file mode 100644 index 00000000..fa3bee78 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.stats; + +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Utility class for computing various types of average. + */ +public final class Averages { + private Averages() {} + + /** + * Compute the mean of the target dataset. + * @param dataset The target dataset + * @return The mean of the target dataset + */ + public static double getMean(@NotNull List<@NotNull Number> dataset) { + Preconditions.checkNotNull(dataset); + double acc = 0; + // iterate over dataset and add values to accumulator + for (Number val : dataset) { + acc += val.doubleValue(); + } + // return acc / length + return acc / dataset.size(); + } + + /** + * Compute the mean of the target dataset. + * @param dataset The target dataset + * @return The mean of the target dataset + */ + public static double getMean(int[] dataset) { + return getMean(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the mean of the target dataset. + * @param dataset The target dataset + * @return The mean of the target dataset + */ + public static double getMean(long[] dataset) { + return getMean(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the mean of the target dataset. + * @param dataset The target dataset + * @return The mean of the target dataset + */ + public static double getMean(double[] dataset) { + return getMean(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the median of the target dataset. Not guaranteed to produce a value in the dataset. + * @param dataset The target dataset + * @return The median of the target dataset. + */ + public static double getMedian(@NotNull List<@NotNull Number> dataset) { + Preconditions.checkNotNull(dataset); + // sort the dataset + dataset = dataset.stream().sorted().collect(Collectors.toList()); + // compute the center index of the dataset + // integer division rounds down so this works + int center = dataset.size() / 2; + // median exists within the dataset + if (dataset.size() % 2 == 1) { + return dataset.get(center).doubleValue(); + } + // median is between two values + return (dataset.get(center).doubleValue() + dataset.get(center + 1).doubleValue()) / 2; + } + + /** + * Compute the median of the target dataset. Not guaranteed to produce a value in the dataset. + * @param dataset The target dataset + * @return The median of the target dataset. + */ + public static double getMedian(int[] dataset) { + return getMedian(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the median of the target dataset. Not guaranteed to produce a value in the dataset. + * @param dataset The target dataset + * @return The median of the target dataset. + */ + public static double getMedian(long[] dataset) { + return getMedian(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the median of the target dataset. Not guaranteed to produce a value in the dataset. + * @param dataset The target dataset + * @return The median of the target dataset. + */ + public static double getMedian(double[] dataset) { + return getMedian(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the mode (most common value) of the dataset. This method + * does not account for multiple modes. + * @param dataset The target dataset + * @return The mode of the dataset. + */ + public static double getMode(@NotNull List<@NotNull Number> dataset) { + Preconditions.checkNotNull(dataset); + double mode = -1; + int maxCount = 0; + // iterate over the dataset + for (Number target : dataset) { + int count = 0; + // iterate over the dataset again and compare + for (Number number : dataset) { + // only need to check for null here, since this will catch the outer loop anyway. + Preconditions.checkNotNull(number); + if (number.equals(target)) count++; + } + // if count of this value is greater than the max, set it as new max + if (count > maxCount) { + maxCount = count; + mode = target.doubleValue(); + } + } + // return the mode + return mode; + } + + /** + * Compute the mode (most common value) of the dataset. This method + * does not account for multiple modes. + * @param dataset The target dataset + * @return The mode of the dataset. + */ + public static double getMode(int[] dataset) { + return getMode(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the mode (most common value) of the dataset. This method + * does not account for multiple modes. + * @param dataset The target dataset + * @return The mode of the dataset. + */ + public static double getMode(long[] dataset) { + return getMode(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } + + /** + * Compute the mode (most common value) of the dataset. This method + * does not account for multiple modes. + * @param dataset The target dataset + * @return The mode of the dataset. + */ + public static double getMode(double[] dataset) { + return getMode(Arrays.stream(dataset).boxed().collect(Collectors.toList())); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java new file mode 100644 index 00000000..42cb0ad5 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.stats; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +class AveragesTest { + @Test() + void testMean() { + // primitive lists + int[] primitiveIntDataset = {1, 2, 3}; + long[] primitiveLongDataset = {1, 2, 3}; + double[] primitiveDoubleDataset = {1, 2, 3}; + Assertions.assertEquals(2, Averages.getMean(primitiveIntDataset)); + Assertions.assertEquals(2, Averages.getMean(primitiveLongDataset)); + Assertions.assertEquals(2, Averages.getMean(primitiveDoubleDataset)); + // boxed list + List dataset = Arrays.stream(primitiveDoubleDataset).boxed().collect(Collectors.toList()); + Assertions.assertEquals(2, Averages.getMean(dataset)); + } + + @Test() + void testMedian() { + // primitive lists + int[] primitiveIntDataset = {1, 2, 3}; + long[] primitiveLongDataset = {1, 2, 3}; + double[] primitiveDoubleDataset = {1, 2, 3}; + Assertions.assertEquals(2, Averages.getMedian(primitiveIntDataset)); + Assertions.assertEquals(2, Averages.getMedian(primitiveLongDataset)); + Assertions.assertEquals(2, Averages.getMedian(primitiveDoubleDataset)); + // boxed list + List dataset = Arrays.stream(primitiveDoubleDataset).boxed().collect(Collectors.toList()); + Assertions.assertEquals(2, Averages.getMedian(dataset)); + + // intermediate values + primitiveIntDataset = new int[]{1, 2, 3, 4}; + primitiveLongDataset = new long[]{1, 2, 3, 4}; + primitiveDoubleDataset = new double[]{1, 2, 3, 4}; + Assertions.assertEquals(2.5, Averages.getMedian(primitiveIntDataset)); + Assertions.assertEquals(2.5, Averages.getMedian(primitiveLongDataset)); + Assertions.assertEquals(2.5, Averages.getMedian(primitiveDoubleDataset)); + + dataset = Arrays.stream(primitiveDoubleDataset).boxed().collect(Collectors.toList()); + Assertions.assertEquals(2.5, Averages.getMedian(dataset)); + } +} From 510b4904185339c39eda3f3957bcdd15ba8e6253 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 15:42:11 +0100 Subject: [PATCH 10/40] wip :sparkles: port over NumberUtil from common --- .../stickyapi/math/NumberUtil.java | 158 ++++++++++++++++++ .../stickyapi/math/NumberUtilTest.java | 70 ++++++++ 2 files changed, 228 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/NumberUtilTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java new file mode 100644 index 00000000..ca6e8088 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Pattern; + +/** + * Utility class for working with numbers. + */ +public final class NumberUtil { + private NumberUtil() {} + + /** + * Test whether the specified number is within the specified range. + * + * @param number The specified number + * @param min The minimum value (inclusive) + * @param max The maximum value (inclusive) + * @return true if given number is in the given range. + */ + public static boolean inRange(int number, int min, int max) { + return number >= min && number <= max; + } + + /** + * Test if the target string can be considered an integer. Checks if every character is a unicode digit. + * + *
+	 * NumberUtil.isInteger("123")         = true
+	 * NumberUtil.isInteger("-123")        = true
+	 * NumberUtil.isInteger(null)          = false
+	 * NumberUtil.isInteger("")            = false
+	 * NumberUtil.isInteger("  ")          = false
+	 * NumberUtil.isInteger("12 3")        = false
+	 * NumberUtil.isInteger("ab2c")        = false
+	 * NumberUtil.isInteger("12-3")        = false
+	 * NumberUtil.isInteger("12.3")        = false
+	 * NumberUtil.isInteger("-123", false) = false
+	 * 
+ * + * @param string The string to check + * @param signed Whether to allow signed numbers + * @return true if the target string can be considered an integer. + */ + public static boolean isInteger(@Nullable String string, boolean signed) { + // an empty or null string is very definitely not an integer + if (string == null || string.length() == 0) { + return false; + } + // sign is allowed, but the first char is neither a sign or a digit + if (signed && !Character.isDigit(string.charAt(0)) && string.charAt(0) != '-') { + return false; + } + // iterate over chars and check if they are digits + // starts from position 1 if the string is allowed to be signed + for (int i = signed ? 1 : 0; i < string.length(); i++) { + if (!Character.isDigit(string.charAt(i))) { + return false; + } + } + // made it here, must be an integer + return true; + } + + /** + * Test if the target string can be considered an integer. Checks if every character is a unicode digit. + * Allows signed numbers. + * @param string The target string + * @return true if the target string can be considered a signed integer. + */ + public static boolean isInteger(@Nullable String string) { + return isInteger(string, true); + } + + // format: sign value decimal exponent + private static final Pattern NUMERIC_REGEX = Pattern.compile("-?[0-9]+\\.?[0-9]+(?:e-?[0-9]+)?$"); + + /** + * Checks if the string is considered numeric. + * + * @param string the String to check, may be null + * @return true if the string is numeric + */ + public static boolean isNumeric(@NotNull String string) { + Preconditions.checkNotNull(string); + return NUMERIC_REGEX.matcher(string).matches(); + } + + /** + * Get a number as the percentage of another. + * + * @param x The number who's percentage of the total this method will return + * @param total The total + * @param string If this should return as a string with `%` appended to the end + * @return {@link Double} + */ + public static String getPercentage(double x, double total, boolean string) { + double percent = (x / total); + StringBuilder out = new StringBuilder(); + if (string) { + out.append(percent * 100).append("%"); + } else { + out.append((percent)); + } + return out.toString(); + } + + /** + * Get a number as the percentage of another. + * + * @param x The number who's percentage of the total this method will return + * @param total The total + * @return {@link Double} + */ + public static Double getPercentage(int x, int total) { + return Double.valueOf(getPercentage(x, total, false)); + } + + /** + * Get a number as the percentage of another. + * + * @param x The number who's percentage of the total this method will return + * @param total The total + * @return {@link String} + */ + public static String getPercentageString(int x, int total) { + return getPercentage(x, total, true); + } + + /** + * Try to return long as an int, capped at int max and int min + * + * @param lng The long to convert + * @return {@link Integer} + */ + public static int longToInt(long lng) { + try { + return Math.toIntExact(lng); + } catch (ArithmeticException ae) { + switch (Long.compare(lng, 0)) { + case 1: + return Integer.MAX_VALUE; + case 0: + return 0; + case -1: + return Integer.MIN_VALUE; + default: + throw new ArithmeticException(); // Somehow Long.compare is broken?? This should be impossible + } + } + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/NumberUtilTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/NumberUtilTest.java new file mode 100644 index 00000000..128fd073 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/NumberUtilTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class NumberUtilTest { + @Test + void testIsSignedInteger() { + // truthy cases + Assertions.assertTrue(NumberUtil.isInteger("123")); + Assertions.assertTrue(NumberUtil.isInteger("-123")); + // falsey cases + Assertions.assertFalse(NumberUtil.isInteger("")); + Assertions.assertFalse(NumberUtil.isInteger(" ")); + Assertions.assertFalse(NumberUtil.isInteger("12 3")); + Assertions.assertFalse(NumberUtil.isInteger("ab2c")); + Assertions.assertFalse(NumberUtil.isInteger("12-3")); + Assertions.assertFalse(NumberUtil.isInteger("12.3")); + } + + @Test + void testIsUnsignedInteger() { + // truthy cases + Assertions.assertTrue(NumberUtil.isInteger("123", false)); + // falsey cases + Assertions.assertFalse(NumberUtil.isInteger("-123", false)); + Assertions.assertFalse(NumberUtil.isInteger("", false)); + Assertions.assertFalse(NumberUtil.isInteger(" ", false)); + Assertions.assertFalse(NumberUtil.isInteger("12 3", false)); + Assertions.assertFalse(NumberUtil.isInteger("ab2c", false)); + Assertions.assertFalse(NumberUtil.isInteger("12-3", false)); + Assertions.assertFalse(NumberUtil.isInteger("12.3", false)); + } + + @Test + void testIsNumeric() { + // truthy cases + Assertions.assertTrue(NumberUtil.isNumeric("123")); + Assertions.assertTrue(NumberUtil.isNumeric("-123")); + Assertions.assertTrue(NumberUtil.isNumeric("-123.123")); + Assertions.assertTrue(NumberUtil.isNumeric("-123e123")); + Assertions.assertTrue(NumberUtil.isNumeric("-123e-123")); + Assertions.assertTrue(NumberUtil.isNumeric("-123.123e-123")); + // falsey cases + Assertions.assertFalse(NumberUtil.isInteger("", false)); + Assertions.assertFalse(NumberUtil.isInteger(" ", false)); + Assertions.assertFalse(NumberUtil.isInteger("12 3", false)); + Assertions.assertFalse(NumberUtil.isInteger("ab2c", false)); + Assertions.assertFalse(NumberUtil.isInteger("12-3", false)); + } + + @Test + void testIntHelper() { + Assertions.assertEquals(NumberUtil.longToInt(1L), 1); + } + + @Test + void testIntHelperMax() { + Assertions.assertEquals(NumberUtil.longToInt(((long)Integer.MAX_VALUE) + 1L), Integer.MAX_VALUE); + } + + @Test + void testIntHelperMin() { + Assertions.assertEquals(NumberUtil.longToInt(((long) Integer.MIN_VALUE) - 1L), Integer.MIN_VALUE); + } +} From 627b84a7c4cf804944aaece97536d7633ba15465 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 15:42:45 +0100 Subject: [PATCH 11/40] wip :sparkles: port over MathUtil -> RandomUtil from common --- .../stickyapi/math/RandomUtil.java | 164 ++++++++++++++++++ .../stickyapi/math/RandomUtilTest.java | 27 +++ 2 files changed, 191 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java new file mode 100644 index 00000000..bdabe1aa --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import com.google.common.base.Preconditions; +import com.google.common.primitives.Chars; +import com.google.common.primitives.Ints; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +/** + * Utility class for dealing with randomness. + * TODO: Consistency issues - randomInt(int max) is exclusive, randomInt(int max, int min) isn't. + */ +public class RandomUtil { + private RandomUtil() { + } + + private static final Random random = new Random(); + + /** + * Get a random number between 0 and the specified maximum value. + * + * @param max The maximum value + * @return A random integer between 0 and the specified maximum value. + */ + public static int randomInt(int max) { + return random.nextInt(max); + } + + /** + * Get a random number within a range. + * + * @param min The minimum value (inclusive) + * @param max The maximum value (inclusive) + * @return A random integer within the specified range + * @throws IllegalArgumentException if min is greater than max. + */ + public static int randomInt(int min, int max) { + // validate argument + Preconditions.checkArgument(min <= max, "The minimum value may not be greater than the maximum."); + // exception case + if (min == max) + return min; + return min + randomInt(1 + max - min); + } + + /** + * Get a random number between 0 and the specified maximum value. + * + * @param max maximum value + * @return A random double between 0 and the specified maximum value. + */ + public static double randomDouble(double max) { + return max * random.nextDouble(); + } + + /** + * Get a random double within a range + * + * @param min minimum value + * @param max maximum value + * @return a random double within the specified range + * @throws IllegalArgumentException when min is greater than max + */ + public static double randomDouble(double min, double max) { + // validate argument + Preconditions.checkArgument(min <= max, "The minimum value may not be greater than the maximum."); + // exception case + if (min == max) + return min; + return min + (1 + max - min) * random.nextDouble(); + } + + /** + * Get a random element from the target array. + * + * @param array The target array + * @return A random element from the target array. + */ + public static @Nullable T randomElement(@NotNull T @NotNull [] array) { + // exception - array is empty + if (array.length < 1) return null; + // pick random index, return element at index + return array[randomInt(array.length)]; + } + + /** + * Get a random element from the target list. + * + * @param list The target list + * @return A random element from the target list. + */ + public static @Nullable T randomElement(@NotNull List list) { + // exception - list is empty + if (list.isEmpty()) return null; + // pick random index, return element at index + return list.get(randomInt(list.size())); + } + + /** + * Round a double value. + * + * @param value the value that should be rounded + * @param places amount of decimal places + * @return {@link Double} + */ + public static double round(double value, int places) { + long factor = (long) Math.pow(10, places); + value = value * factor; + long tmp = Math.round(value); + return (double) tmp / factor; + } + + /** + * Convert a number of bytes to a human-readable value. + * TODO: How the absolute fuck does this work? + * + * @param bytes the value that should be converted + * @return a human-readable byte value + */ + public static String bytesToReadable(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + if (absB < 1024) { + return bytes + " B"; + } + long value = absB; + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + value *= Long.signum(bytes); + return String.format("%.1f %ciB", value / 1024.0, ci.current()); + } + + + + /** + * @see #randomElement(List) + * Primitive values cannot be used with type generics, so these methods exist for ease of use. + * @since 3.0 + */ + public static char randomElement(char [] choices) { + return Objects.requireNonNull(randomElement(Chars.asList(choices))); + } + + /** + * @see #randomElement(List) + * Primitive values cannot be used with type generics, so these methods exist for ease of use. + * @since 3.0 + */ + public static int randomElement(int [] choices) { + return Objects.requireNonNull(randomElement(Ints.asList(choices))); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java new file mode 100644 index 00000000..fa0b4876 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.RepeatedTest;; + +// TODO: Random element tests +class RandomUtilTest { + @RepeatedTest(100) + void testMaxRandomInt() { + Assertions.assertTrue(RandomUtil.randomInt(5) < 5); + } + + @RepeatedTest(100) + void testRangedRandomInt() { + int value = RandomUtil.randomInt(3, 7); + Assertions.assertTrue(value >= 3 && value <= 7); + } + + @RepeatedTest(100) + void testMaxRandomDouble() { + Assertions.assertTrue(RandomUtil.randomDouble(5) < 5); + } +} From 17183e608cf2b7c212065d95938bb6bf40fb3833 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 15:44:04 +0100 Subject: [PATCH 12/40] tweaks to median calculation, consistent @Test annotation --- .../com/dumbdogdiner/stickyapi/math/Interpolation.java | 1 + .../dumbdogdiner/stickyapi/math/stats/Averages.java | 4 ++-- .../stickyapi/math/vector/package-info.java | 4 ++++ .../dumbdogdiner/stickyapi/math/InterpolationTest.java | 6 +++--- .../com/dumbdogdiner/stickyapi/math/OrdinalTest.java | 10 +++++----- 5 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/package-info.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java index f3fdb5cb..8bbe9ca9 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java @@ -8,6 +8,7 @@ /** * Utility class for interpolating between values. + * TODO: Inaccuracy issues for longs. */ public final class Interpolation { private Interpolation() {} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java index fa3bee78..9f7db26c 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java @@ -76,8 +76,8 @@ public static double getMedian(@NotNull List<@NotNull Number> dataset) { if (dataset.size() % 2 == 1) { return dataset.get(center).doubleValue(); } - // median is between two values - return (dataset.get(center).doubleValue() + dataset.get(center + 1).doubleValue()) / 2; + // median is between two values - TODO: comment about the minus sign below vvvv + return (dataset.get(center).doubleValue() + dataset.get(center - 1).doubleValue()) / 2; } /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/package-info.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/package-info.java new file mode 100644 index 00000000..47e58a7a --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides implementations of mathematically oriented, immutable vectors. + */ +package com.dumbdogdiner.stickyapi.math.vector; diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java index 68cf7574..b3cf8c0a 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/InterpolationTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; class InterpolationTest { - @Test() + @Test void testUnclampedLerp() { // test lerp 0 => 1, t = 0.5 Assertions.assertEquals(0.5, Interpolation.lerpUnclamped(0, 1, 0.5)); @@ -18,7 +18,7 @@ void testUnclampedLerp() { Assertions.assertEquals(2.0, Interpolation.lerpUnclamped(0, 1, 2)); } - @Test() + @Test void testClamp() { // test clamp 0 => 1, t = 0.5 Assertions.assertEquals(0.5, Interpolation.clamp(0, 1, 0.5)); @@ -28,7 +28,7 @@ void testClamp() { Assertions.assertEquals(1.0, Interpolation.clamp(0, 1, 2)); } - @Test() + @Test void testLerp() { // test clamp 0 => 1, t = 0.5 Assertions.assertEquals(0.5, Interpolation.lerp(0, 1, 0.5)); diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java index c2296aef..117618d5 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/OrdinalTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; class OrdinalTest { - @Test() + @Test void testFirst() { Assertions.assertEquals("st", Ordinal.getOrdinal(1)); // mod 10 @@ -17,7 +17,7 @@ void testFirst() { Assertions.assertEquals("st", Ordinal.getOrdinal(101)); } - @Test() + @Test void testSecond() { Assertions.assertEquals("nd", Ordinal.getOrdinal(2)); // mod 10 @@ -26,7 +26,7 @@ void testSecond() { Assertions.assertEquals("nd", Ordinal.getOrdinal(102)); } - @Test() + @Test void testThird() { Assertions.assertEquals("rd", Ordinal.getOrdinal(3)); // mod 10 @@ -35,7 +35,7 @@ void testThird() { Assertions.assertEquals("rd", Ordinal.getOrdinal(103)); } - @Test() + @Test void testTeenException() { Assertions.assertEquals("th", Ordinal.getOrdinal(11)); Assertions.assertEquals("th", Ordinal.getOrdinal(12)); @@ -44,7 +44,7 @@ void testTeenException() { Assertions.assertEquals("th", Ordinal.getOrdinal(111)); } - @Test() + @Test void testGeneral() { Assertions.assertEquals("th", Ordinal.getOrdinal(4)); // mod 10 From e01d72142b7a5740a1de6042b6a1735e3e5975a6 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 15:57:44 +0100 Subject: [PATCH 13/40] wip :sparkles: matrix2 and matrix3 implementations, translation builders --- .../stickyapi/math/transform/Matrix.java | 8 --- .../stickyapi/math/transform/Matrix2.java | 65 +++++++++++++++++++ .../stickyapi/math/transform/Matrix3.java | 4 ++ .../math/transform/TranslationBuilder2.java | 32 +++++++++ .../math/transform/TranslationBuilder3.java | 7 ++ 5 files changed, 108 insertions(+), 8 deletions(-) delete mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java deleted file mode 100644 index 58dc3904..00000000 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix.java +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. - * Licensed under the MIT license, see LICENSE for more information... - */ -package com.dumbdogdiner.stickyapi.math.transform; - -public class Matrix { -} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java new file mode 100644 index 00000000..61d3a48d --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.transform; + +import lombok.Getter; + +/** + * Represents a 2x2 mathematical, immutable matrix. The matrix has the following layout: + *
+ * (a b)
+ * (c d)
+ * 
+ */ +public class Matrix2 { + /** + * Generate an anticlockwise rotation matrix for the target angle. + * @param theta The target angle in radians + * @return A {@link Matrix2} representing a rotation theta radians about the origin. + */ + public static Matrix2 getRotationMatrix(double theta) { + return new Matrix2(Math.cos(theta), -Math.sin(theta), Math.sin(theta), Math.cos(theta)); + } + + /** + * Generate an enlargement matrix for the target scale factor. + * @param k The target scale factor + * @return A {@link Matrix2} representing an enlargement scale factor k centered at the origin. + */ + public static Matrix2 getEnlargementMatrix(double k) { + return new Matrix2(k, 0, 0, k); + } + + /** + * Generate a new identity matrix. + * @return A {@link Matrix2} representing an identity matrix. + */ + public static Matrix2 getIdentityMatrix() { + return getEnlargementMatrix(1); + } + + @Getter + private final double a; + @Getter + private final double b; + @Getter + private final double c; + @Getter + private final double d; + + /** + * Construct a new matrix with the following parameters. + * @param a The a parameter of the matrix + * @param b The b parameter of the matrix + * @param c The c parameter of the matrix + * @param d The d parameter of the matrix + */ + public Matrix2(double a, double b, double c, double d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java new file mode 100644 index 00000000..9bf23af8 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java @@ -0,0 +1,4 @@ +package com.dumbdogdiner.stickyapi.math.transform; + +public class Matrix3 { +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java new file mode 100644 index 00000000..997417b8 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java @@ -0,0 +1,32 @@ +package com.dumbdogdiner.stickyapi.math.transform; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Applies transformations sequentially. + */ +public class TranslationBuilder2 { + @Getter + private final List transformations = new ArrayList<>(); + + /** + * Apply the transformation to this builder. + * @param transformation The transformation to apply + * @return The {@link TranslationBuilder2} with the new matrix applied. + */ + public TranslationBuilder2 apply(Matrix2 transformation) { + this.transformations.add(transformation); + return this; + } + + /** + * @return The translation builder as a {@link Matrix2} + */ + public Matrix2 toMatrix() { + // TODO: implement + return Matrix2.getIdentityMatrix(); + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java new file mode 100644 index 00000000..94860059 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java @@ -0,0 +1,7 @@ +package com.dumbdogdiner.stickyapi.math.transform; + +/** + * Applies transformations sequentially. + */ +public class TranslationBuilder3 { +} From fb3e29b2aae393b183841c56a543b51684e988e7 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 16:34:35 +0100 Subject: [PATCH 14/40] wip :sparkles: add distribution methods --- .../stickyapi/math/stats/Distribution.java | 79 +++++++++++++++++++ .../stickyapi/math/stats/package-info.java | 4 + .../stickyapi/math/transform/Matrix3.java | 4 + .../math/transform/TranslationBuilder2.java | 4 + .../math/transform/TranslationBuilder3.java | 4 + .../math/transform/package-info.java | 4 + .../math/stats/DistributionTest.java | 36 +++++++++ 7 files changed, 135 insertions(+) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/package-info.java create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/package-info.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java new file mode 100644 index 00000000..114509f5 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.stats; + +import com.google.common.base.Preconditions; + +/** + * Utility class for dealing with distribution of data. + */ +public final class Distribution { + private Distribution() {} + + /** + * Calculate the expected value of the target dataset. This is just an alias for the mean + * of the dataset. + * @param dataset The target dataset + * @return The expected value of the target dataset. + */ + public static double expectation(double[] dataset) { + return Averages.getMean(dataset); + } + + /** + * Compute the variance of the target dataset. This is a measure of + * how much the data varies about its mean value. + * @param dataset The target dataset + * @return The variation of the target dataset. + */ + public static double variance(double[] dataset) { + // compute squares of dataset + double[] squares = new double[dataset.length]; + for (int i = 0; i < dataset.length; i++) { + squares[i] = Math.pow(dataset[i], 2); + } + // Var(X) = E(X^2) - E(X)^2 + return expectation(squares) - Math.pow(expectation(dataset), 2); + } + + /** + * Compute the standard deviation of the target dataset. This is a measure + * of how much the data varies about its mean value. + * @param dataset The target dataset + * @return The standard deviation of the target dataset. + */ + public static double standardDeviation(double[] dataset) { + return Math.sqrt(variance(dataset)); + } + + /** + * Compute the covariance of two datasets. + * @param x The first dataset + * @param y The second dataset + * @return The covariance of the two datasets. + */ + public static double covariance(double[] x, double[] y) { + Preconditions.checkArgument(x.length == y.length, "Datasets must be of equal size to compute covariance."); + double[] products = new double[x.length]; + // compute product of values + for (int i = 0; i < x.length; i++) { + products[i] = x[i] * y[i]; + } + // cov(X, Y) = E(XY) - E(X)E(Y) + return expectation(products) - expectation(x) * expectation(y); + } + + /** + * Compute the Pearson correlation coefficient (PCC) of the target datasets. + * This is a measure of how much two datasets correlate with each other. + * @param x The first dataset + * @param y The second dataset + * @return The PCC of the target datasets. + */ + public static double pcc(double[] x, double[] y) { + Preconditions.checkArgument(x.length == y.length, "Datasets must be of equal size to compute PCC."); + return covariance(x, y) / (standardDeviation(x) * standardDeviation(y)); + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/package-info.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/package-info.java new file mode 100644 index 00000000..30ff1388 --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides methods and utilities for computing statistics on datasets. + */ +package com.dumbdogdiner.stickyapi.math.stats; diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java index 9bf23af8..fce09cb4 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix3.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ package com.dumbdogdiner.stickyapi.math.transform; public class Matrix3 { diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java index 997417b8..f63b892e 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ package com.dumbdogdiner.stickyapi.math.transform; import lombok.Getter; diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java index 94860059..c8844bac 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder3.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ package com.dumbdogdiner.stickyapi.math.transform; /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/package-info.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/package-info.java new file mode 100644 index 00000000..1a3ac53c --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides implementations of mathematical matrices, used for translating vectors. + */ +package com.dumbdogdiner.stickyapi.math.transform; diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java new file mode 100644 index 00000000..c1703edd --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.stats; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class DistributionTest { + static final double[] DATASET_X = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + static final double[] DATASET_Y = {9, 8, 7, 6, 5, 4, 3, 2, 1}; + + @Test + void testExpectation() { + Assertions.assertEquals(5, Distribution.expectation(DATASET_X)); + Assertions.assertEquals(5, Distribution.expectation(DATASET_Y)); + } + + @Test + void testVariance() { + Assertions.assertEquals(6.666666666666668, Distribution.variance(DATASET_X)); + Assertions.assertEquals(6.666666666666668, Distribution.variance(DATASET_Y)); + } + + @Test + void testStandardDeviation() { + Assertions.assertEquals(2.5819888974716116, Distribution.standardDeviation(DATASET_X)); + Assertions.assertEquals(2.5819888974716116, Distribution.standardDeviation(DATASET_Y)); + } + + @Test + void testPCC() { + Assertions.assertEquals(-1, Distribution.pcc(DATASET_X, DATASET_Y)); + } +} From 5a20b834fe501f03685caac47a2821182e9b0a9e Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 16:38:19 +0100 Subject: [PATCH 15/40] fix floating point inaccuracy in pcc test --- .../stickyapi/math/NumberUtil.java | 23 +++++++++++++++++++ .../math/stats/DistributionTest.java | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index ca6e8088..0094b0bc 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -92,6 +92,29 @@ public static boolean isNumeric(@NotNull String string) { return NUMERIC_REGEX.matcher(string).matches(); } + /** + * Utility method for checking if two doubles are very very almost equal. Useful for getting rid + * of floating point inaccuracies. + * @param a The first double + * @param b The second double + * @param eps The maximum difference between them + * @return true if the numbers are within epsilon of each other. + */ + public static boolean almostEquals(double a, double b, double eps) { + return Math.abs(a-b)true if the numbers are within 1 part in a million of each other. + */ + public static boolean almostEquals(double a, double b) { + return Math.abs(a-b)< 10e-6; + } + /** * Get a number as the percentage of another. * diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java index c1703edd..36f4b4f2 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java @@ -4,6 +4,7 @@ */ package com.dumbdogdiner.stickyapi.math.stats; +import com.dumbdogdiner.stickyapi.math.NumberUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -31,6 +32,6 @@ void testStandardDeviation() { @Test void testPCC() { - Assertions.assertEquals(-1, Distribution.pcc(DATASET_X, DATASET_Y)); + Assertions.assertTrue(NumberUtil.almostEquals(-1, Distribution.pcc(DATASET_X, DATASET_Y))); } } From a31b9740f5c8408e538a82b3213b4cc3da7ac5bf Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 17:33:09 +0100 Subject: [PATCH 16/40] add Distribution.isAssociated --- .../stickyapi/math/NumberUtil.java | 4 ++-- .../stickyapi/math/stats/Distribution.java | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index 0094b0bc..031ba4da 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -24,8 +24,8 @@ private NumberUtil() {} * @param max The maximum value (inclusive) * @return true if given number is in the given range. */ - public static boolean inRange(int number, int min, int max) { - return number >= min && number <= max; + public static boolean inRange(Number number, Number min, Number max) { + return number.doubleValue() >= min.doubleValue() && number.doubleValue() <= max.doubleValue(); } /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java index 114509f5..dd2a10bf 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java @@ -4,6 +4,7 @@ */ package com.dumbdogdiner.stickyapi.math.stats; +import com.dumbdogdiner.stickyapi.math.NumberUtil; import com.google.common.base.Preconditions; /** @@ -76,4 +77,26 @@ public static double pcc(double[] x, double[] y) { Preconditions.checkArgument(x.length == y.length, "Datasets must be of equal size to compute PCC."); return covariance(x, y) / (standardDeviation(x) * standardDeviation(y)); } + + /** + * Test if two datasets are associated with the specified confidence. + * @param x The first dataset + * @param y The second dataset + * @param confidence The confidence to test with + * @return true if the two datasets are associated. + */ + public static boolean isAssociated(double[] x, double[] y, double confidence) { + Preconditions.checkArgument(NumberUtil.inRange(0, 1, confidence)); + return Math.abs(pcc(x, y)) > confidence; + } + + /** + * Test if two datasets are associated with the specified confidence. + * @param x The first dataset + * @param y The second dataset + * @return true if the two datasets are associated. + */ + public static boolean isAssociated(double[] x, double[] y) { + return isAssociated(x, y, 0.8); + } } From 404244e1f2eda97e16e0fd00f3a1fec9b7a0f2d4 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 17:38:03 +0100 Subject: [PATCH 17/40] add README --- math/README.md | 3 +++ .../main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 math/README.md diff --git a/math/README.md b/math/README.md new file mode 100644 index 00000000..02082f2c --- /dev/null +++ b/math/README.md @@ -0,0 +1,3 @@ +# stickyapi:math + +Provides a lightweight mathematics library for general purpose use. diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index 031ba4da..69e73a64 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -112,7 +112,7 @@ public static boolean almostEquals(double a, double b, double eps) { * @return true if the numbers are within 1 part in a million of each other. */ public static boolean almostEquals(double a, double b) { - return Math.abs(a-b)< 10e-6; + return Math.abs(a - b) < 10e-6; } /** From d9c784b1079c3ccadbfddfa6bb4728140fb896b2 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 17:50:25 +0100 Subject: [PATCH 18/40] tweaks to NumberUtil and RandomUtil --- .../stickyapi/math/NumberUtil.java | 63 ++++++++++++------- .../stickyapi/math/RandomUtil.java | 2 - 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index 69e73a64..fd3a2713 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -28,6 +28,20 @@ public static boolean inRange(Number number, Number min, Number max) { return number.doubleValue() >= min.doubleValue() && number.doubleValue() <= max.doubleValue(); } + /** + * Round a double value + * + * @param value the value that should be rounded + * @param places amount of decimal places + * @return {@link Double} + */ + public static double round(double value, int places) { + long factor = (long) Math.pow(10, places); + value = value * factor; + long tmp = Math.round(value); + return (double) tmp / factor; + } + /** * Test if the target string can be considered an integer. Checks if every character is a unicode digit. * @@ -97,11 +111,11 @@ public static boolean isNumeric(@NotNull String string) { * of floating point inaccuracies. * @param a The first double * @param b The second double - * @param eps The maximum difference between them + * @param epsilon The maximum difference between them * @return true if the numbers are within epsilon of each other. */ - public static boolean almostEquals(double a, double b, double eps) { - return Math.abs(a-b)true if the numbers are within 1 part in a million of each other. */ public static boolean almostEquals(double a, double b) { - return Math.abs(a - b) < 10e-6; + return almostEquals(a, b, 10e-6); } /** @@ -120,29 +134,34 @@ public static boolean almostEquals(double a, double b) { * * @param x The number who's percentage of the total this method will return * @param total The total - * @param string If this should return as a string with `%` appended to the end - * @return {@link Double} + * @return The percentage value */ - public static String getPercentage(double x, double total, boolean string) { - double percent = (x / total); - StringBuilder out = new StringBuilder(); - if (string) { - out.append(percent * 100).append("%"); - } else { - out.append((percent)); - } - return out.toString(); + public static double getPercentage(Number x, Number total) { + return x.doubleValue() / total.doubleValue() * 100; + } + + /** + * Get a number as the percentage of another. + * + * @param x The number who's percentage of the total this method will return + * @param total The total + * @param places The number of decimal places to round to + * @return A rounded percentage value + */ + public static double getPercentage(Number x, Number total, int places) { + return round(getPercentage(x, total), places); } /** * Get a number as the percentage of another. * - * @param x The number who's percentage of the total this method will return + * @param x The number who's percentage of the total this method will return * @param total The total - * @return {@link Double} + * @param places The number of decimal places to return + * @return A formatted {@link String} containing the percentage to the specified number of decimal places. */ - public static Double getPercentage(int x, int total) { - return Double.valueOf(getPercentage(x, total, false)); + public static String getPercentageString(Number x, Number total, int places) { + return getPercentage(x, total, places) + "%"; } /** @@ -150,10 +169,10 @@ public static Double getPercentage(int x, int total) { * * @param x The number who's percentage of the total this method will return * @param total The total - * @return {@link String} + * @return A formatted {@link String} containing the percentage to 2 decimal places. */ - public static String getPercentageString(int x, int total) { - return getPercentage(x, total, true); + public static String getPercentageString(Number x, Number total) { + return getPercentageString(x, total, 2); } /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index bdabe1aa..757cadc7 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -142,8 +142,6 @@ public static String bytesToReadable(long bytes) { return String.format("%.1f %ciB", value / 1024.0, ci.current()); } - - /** * @see #randomElement(List) * Primitive values cannot be used with type generics, so these methods exist for ease of use. From 98cc75f41557ecc789faf79fd2a86440948519ae Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 18:24:03 +0100 Subject: [PATCH 19/40] fix vector unit tests --- .../stickyapi/math/vector/Vector2Test.java | 13 +++++++++---- .../stickyapi/math/vector/Vector3Test.java | 15 ++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java index 2b9506a6..e72bdd8e 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java @@ -8,21 +8,26 @@ import org.junit.jupiter.api.Test; class Vector2Test { - @Test() + @Test + void testVector2Equal() { + Assertions.assertEquals(new Vector2(1, 2), new Vector2(1, 2)); + } + + @Test void testVector2Add() { Vector2 a = new Vector2(0, 1); Vector2 b = new Vector2(1, 1); Assertions.assertEquals(new Vector2(1, 2), a.add(b)); } - @Test() + @Test void testVector2Subtract() { Vector2 a = new Vector2(0, 1); Vector2 b = new Vector2(1, 1); Assertions.assertEquals(new Vector2(-1, 0), a.subtract(b)); } - @Test() + @Test void testVector2Scale() { Vector2 a = new Vector2(10, 5); // check unitary scale @@ -33,7 +38,7 @@ void testVector2Scale() { Assertions.assertEquals(new Vector2(-10, -5), a.scale(-1)); } - @Test() + @Test void testVector2To() { Vector2 a = new Vector2(0, 1); Vector2 b = new Vector2(1, 0); diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java index 14f059ed..87061b55 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java @@ -8,21 +8,26 @@ import org.junit.jupiter.api.Test; class Vector3Test { - @Test() + @Test + void testVector3Equal() { + Assertions.assertEquals(new Vector3(1, 2, 3), new Vector3(1, 2, 3)); + } + + @Test void testVector3Add() { Vector3 a = new Vector3(0, 1, 2); Vector3 b = new Vector3(1, 1, 1); Assertions.assertEquals(new Vector3(1, 2, 3), a.add(b)); } - @Test() + @Test void testVector3Subtract() { Vector3 a = new Vector3(0, 1, 2); Vector3 b = new Vector3(1, 1, 1); Assertions.assertEquals(new Vector3(-1, 0, 1), a.subtract(b)); } - @Test() + @Test void testVector3Scale() { Vector3 a = new Vector3(10, 5, 20); // check unitary scale @@ -33,11 +38,11 @@ void testVector3Scale() { Assertions.assertEquals(new Vector3(-10, -5, -20), a.scale(-1)); } - @Test() + @Test void testVector3To() { Vector3 a = new Vector3(0, 1, 2); Vector3 b = new Vector3(1, 1, 1); // TODO: skye do some math and work out what this should be you lazy hoe - Assertions.assertEquals(new Vector3(1, -1, -1), a.to(b)); + Assertions.assertEquals(new Vector3(1, 0, -1), a.to(b)); } } From c088345afb3f8477c8e900020fabb04723b7fb2c Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 18:36:27 +0100 Subject: [PATCH 20/40] add null safety to Averages --- .../stickyapi/math/stats/Averages.java | 17 ++++++++++++++--- .../stickyapi/math/stats/AveragesTest.java | 4 ++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java index 9f7db26c..92972a23 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java @@ -24,9 +24,11 @@ private Averages() {} */ public static double getMean(@NotNull List<@NotNull Number> dataset) { Preconditions.checkNotNull(dataset); + Preconditions.checkArgument(dataset.isEmpty(), "Cannot compute mean of an empty dataset"); double acc = 0; // iterate over dataset and add values to accumulator for (Number val : dataset) { + Preconditions.checkNotNull(val, "Cannot compute mean of a dataset containing a null value"); acc += val.doubleValue(); } // return acc / length @@ -67,6 +69,8 @@ public static double getMean(double[] dataset) { */ public static double getMedian(@NotNull List<@NotNull Number> dataset) { Preconditions.checkNotNull(dataset); + Preconditions.checkArgument(dataset.isEmpty(), "Cannot compute median of an empty dataset"); + // sort the dataset dataset = dataset.stream().sorted().collect(Collectors.toList()); // compute the center index of the dataset @@ -74,10 +78,17 @@ public static double getMedian(@NotNull List<@NotNull Number> dataset) { int center = dataset.size() / 2; // median exists within the dataset if (dataset.size() % 2 == 1) { - return dataset.get(center).doubleValue(); + Number value = dataset.get(center); + // ensure value is not null + Preconditions.checkNotNull(value, "Cannot compute median of a dataset containing a null value"); + return value.doubleValue(); } // median is between two values - TODO: comment about the minus sign below vvvv - return (dataset.get(center).doubleValue() + dataset.get(center - 1).doubleValue()) / 2; + Number upper = dataset.get(center); + Number lower = dataset.get(center - 1); + Preconditions.checkNotNull(upper,"Cannot compute median of a dataset containing a null value"); + Preconditions.checkNotNull(lower,"Cannot compute median of a dataset containing a null value"); + return (upper.doubleValue() + lower.doubleValue()) / 2; } /** @@ -123,7 +134,7 @@ public static double getMode(@NotNull List<@NotNull Number> dataset) { // iterate over the dataset again and compare for (Number number : dataset) { // only need to check for null here, since this will catch the outer loop anyway. - Preconditions.checkNotNull(number); + Preconditions.checkNotNull(number, "Cannot compute mode of a dataset containing a null value"); if (number.equals(target)) count++; } // if count of this value is greater than the max, set it as new max diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java index 42cb0ad5..75605f48 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/AveragesTest.java @@ -12,7 +12,7 @@ import java.util.stream.Collectors; class AveragesTest { - @Test() + @Test void testMean() { // primitive lists int[] primitiveIntDataset = {1, 2, 3}; @@ -26,7 +26,7 @@ void testMean() { Assertions.assertEquals(2, Averages.getMean(dataset)); } - @Test() + @Test void testMedian() { // primitive lists int[] primitiveIntDataset = {1, 2, 3}; From a15af504823f4ae8d071ec16afc97b5d6ac0c31c Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 18:37:27 +0100 Subject: [PATCH 21/40] fix fundamental flaw in vector methods --- .../java/com/dumbdogdiner/stickyapi/math/vector/Vector.java | 2 +- .../java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java index 2c0bf8b1..0d4bf284 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -81,7 +81,7 @@ public int hashCode() { * @return The vector from this vector to the target vector. */ @NotNull Vector to(@NotNull Vector vector) { - return vector.subtract(vector); + return vector.subtract(this); }; /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java index f5444a02..21b34c28 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java @@ -82,13 +82,13 @@ protected double[] getValues() { @Override @NotNull Vector3 add(@NotNull Vector vector) { - return new Vector3(this.x + vector.getDimension(0), this.y + vector.getDimension(1), this.z + vector.getDimension(3)); + return new Vector3(this.x + vector.getDimension(0), this.y + vector.getDimension(1), this.z + vector.getDimension(2)); } @Override @NotNull Vector3 subtract(@NotNull Vector vector) { - return new Vector3(this.x - vector.getDimension(0), this.y - vector.getDimension(1), this.z - vector.getDimension(3)); + return new Vector3(this.x - vector.getDimension(0), this.y - vector.getDimension(1), this.z - vector.getDimension(2)); } @Override From 3e620ef0b816895e90112d8b255b11a341ad96cc Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 18:43:34 +0100 Subject: [PATCH 22/40] fix: dataset is empty? trying to divide by zero? PROCEED AS NORMAL :3 --- .../java/com/dumbdogdiner/stickyapi/math/stats/Averages.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java index 92972a23..9a5cc607 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Averages.java @@ -24,7 +24,7 @@ private Averages() {} */ public static double getMean(@NotNull List<@NotNull Number> dataset) { Preconditions.checkNotNull(dataset); - Preconditions.checkArgument(dataset.isEmpty(), "Cannot compute mean of an empty dataset"); + Preconditions.checkArgument(!dataset.isEmpty(), "Cannot compute mean of an empty dataset"); double acc = 0; // iterate over dataset and add values to accumulator for (Number val : dataset) { @@ -69,7 +69,7 @@ public static double getMean(double[] dataset) { */ public static double getMedian(@NotNull List<@NotNull Number> dataset) { Preconditions.checkNotNull(dataset); - Preconditions.checkArgument(dataset.isEmpty(), "Cannot compute median of an empty dataset"); + Preconditions.checkArgument(!dataset.isEmpty(), "Cannot compute median of an empty dataset"); // sort the dataset dataset = dataset.stream().sorted().collect(Collectors.toList()); @@ -126,6 +126,7 @@ public static double getMedian(double[] dataset) { */ public static double getMode(@NotNull List<@NotNull Number> dataset) { Preconditions.checkNotNull(dataset); + Preconditions.checkArgument(!dataset.isEmpty(), "Cannot compute mode of an empty dataset"); double mode = -1; int maxCount = 0; // iterate over the dataset From 5838c4582d7ec5f7822f08f29cc7bbb243b14b25 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 19:35:34 +0100 Subject: [PATCH 23/40] wip :sparkles: matrix2 implementation --- .../stickyapi/math/transform/Matrix2.java | 83 ++++++++++++++++++- .../transform/TransformationBuilder2.java | 53 ++++++++++++ .../math/transform/TranslationBuilder2.java | 36 -------- .../stickyapi/math/transform/Matrix2Test.java | 59 +++++++++++++ .../transform/TransformationBuilder2Test.java | 44 ++++++++++ 5 files changed, 238 insertions(+), 37 deletions(-) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2.java delete mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2Test.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java index 61d3a48d..764b183e 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2.java @@ -6,6 +6,8 @@ import lombok.Getter; +import java.util.Objects; + /** * Represents a 2x2 mathematical, immutable matrix. The matrix has the following layout: *
@@ -13,7 +15,7 @@
  * (c d)
  * 
*/ -public class Matrix2 { +public class Matrix2 implements Cloneable { /** * Generate an anticlockwise rotation matrix for the target angle. * @param theta The target angle in radians @@ -62,4 +64,83 @@ public Matrix2(double a, double b, double c, double d) { this.c = c; this.d = d; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Matrix2 matrix2 = (Matrix2) o; + return Double.compare(matrix2.a, a) == 0 && Double.compare(matrix2.b, b) == 0 && Double.compare(matrix2.c, c) == 0 && Double.compare(matrix2.d, d) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(a, b, c, d); + } + + /** + * Add the target matrix to this matrix. + * @param target The target matrix + * @return The resulting {@link Matrix2}. + */ + public Matrix2 add(Matrix2 target) { + return new Matrix2( + this.a + target.a, + this.b + target.b, + this.c + target.c, + this.b + target.b + ); + } + + /** + * Subtract the target matrix from this matrix. + * @param target The target matrix + * @return The resulting {@link Matrix2}. + */ + public Matrix2 subtract(Matrix2 target) { + return new Matrix2( + this.a - target.a, + this.b - target.b, + this.c - target.c, + this.d - target.d + ); + } + + /** + * Scale this matrix by the target scalar. + * @param k The target scalar + * @return The resulting scaled {@link Matrix2}. + */ + public Matrix2 scale(double k) { + return new Matrix2( + this.a * k, + this.b * k, + this.c * k, + this.d * k + ); + } + + /** + * Pre-multiply the target matrix with this matrix. + * @param target The matrix being pre-multiplied. + * @return The resulting {@link Matrix2} + */ + public Matrix2 preMultiply(Matrix2 target) { + return new Matrix2( + target.a * this.a + target.b * this.c, + target.a * this.b + target.b * this.d, + target.c * this.a + target.d * this.c, + target.c * this.b + target.d * this.d + ); + } + + /** + * Post-multiply the target matrix with this matrix. This is equivalent + * to pre-multiplying the input matrix with this matrix. + * @param target The matrix being post-multiplied. + * @return The resulting {@link Matrix2} + */ + public Matrix2 postMultiply(Matrix2 target) { + return target.preMultiply(this); + } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2.java new file mode 100644 index 00000000..94a08daf --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.transform; + +import com.dumbdogdiner.stickyapi.math.vector.Vector2; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Applies transformations sequentially. + */ +public class TransformationBuilder2 { + @Getter + private final List transformations = new ArrayList<>(); + + /** + * Apply the transformation to this builder. + * @param transformation The transformation to apply + * @return The {@link TransformationBuilder2} with the new matrix applied. + */ + public TransformationBuilder2 apply(Matrix2 transformation) { + this.transformations.add(transformation); + return this; + } + + /** + * @return The translation builder as a {@link Matrix2} + */ + public Matrix2 toMatrix() { + Matrix2 out = Matrix2.getIdentityMatrix(); + for (Matrix2 matrix : this.transformations) { + out = out.postMultiply(matrix); + } + return out; + } + + /** + * Transform the target matrix through the translation represented by this builder. + * @param input The input vector + * @return The output {@link Vector2} + */ + public Vector2 transform(Vector2 input) { + Matrix2 transform = this.toMatrix(); + return new Vector2( + transform.getA() * input.getX() + transform.getB() * input.getY(), + transform.getC() * input.getX() + transform.getD() * input.getY() + ); + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java deleted file mode 100644 index f63b892e..00000000 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/transform/TranslationBuilder2.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. - * Licensed under the MIT license, see LICENSE for more information... - */ -package com.dumbdogdiner.stickyapi.math.transform; - -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; - -/** - * Applies transformations sequentially. - */ -public class TranslationBuilder2 { - @Getter - private final List transformations = new ArrayList<>(); - - /** - * Apply the transformation to this builder. - * @param transformation The transformation to apply - * @return The {@link TranslationBuilder2} with the new matrix applied. - */ - public TranslationBuilder2 apply(Matrix2 transformation) { - this.transformations.add(transformation); - return this; - } - - /** - * @return The translation builder as a {@link Matrix2} - */ - public Matrix2 toMatrix() { - // TODO: implement - return Matrix2.getIdentityMatrix(); - } -} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2Test.java new file mode 100644 index 00000000..fad9a830 --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/Matrix2Test.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.transform; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class Matrix2Test { + @Test + void testMatrix2Equals() { + Matrix2 a = Matrix2.getIdentityMatrix(); + Assertions.assertEquals(new Matrix2(1, 0, 0 ,1), a); + } + + @Test + void testMatrix2Identity() { + Matrix2 a = new Matrix2(1, 2, 3, 4); + Assertions.assertEquals(a, a.preMultiply(Matrix2.getIdentityMatrix())); + Assertions.assertEquals(a, a.postMultiply(Matrix2.getIdentityMatrix())); + } + + @Test + void testMatrix2Add() { + Matrix2 a = new Matrix2(1, 2, 3, 4); + Matrix2 b = new Matrix2(4, 3, 2, 1); + Assertions.assertEquals(new Matrix2(5, 5, 5, 5), a.add(b)); + } + + @Test + void testMatrix2Subtract() { + Matrix2 a = new Matrix2(4, 8, 7, 4); + Matrix2 b = new Matrix2(5, 4, 6, 5); + Assertions.assertEquals(new Matrix2(-1, 4, 1, -1), a.subtract(b)); + } + + @Test + void testMatrix2Scale() { + Matrix2 a = new Matrix2(1, 7, -6, 2); + Assertions.assertEquals(a, a.scale(1)); + Assertions.assertEquals(new Matrix2(2, 14, -12, 4), a.scale(2)); + Assertions.assertEquals(new Matrix2(-1, -7, 6, -2), a.scale(-1)); + } + + @Test + void testMatrixPreMultiply() { + Matrix2 a = new Matrix2(1, 2, 3, 4); + Matrix2 b = new Matrix2(4, 3, 2, 1); + Assertions.assertEquals(new Matrix2(13, 20, 5, 8), a.preMultiply(b)); + } + + @Test + void testMatrixPostMultiply() { + Matrix2 a = new Matrix2(1, 2, 3, 4); + Matrix2 b = new Matrix2(4, 3, 2, 1); + Assertions.assertEquals(new Matrix2(8, 5, 20, 13), a.postMultiply(b)); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java new file mode 100644 index 00000000..7160232a --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.transform; + +import com.dumbdogdiner.stickyapi.math.vector.Vector2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class TransformationBuilder2Test { + @Test + void testSimpleTransformation() { + // enlargement s.f. 2 + TransformationBuilder2 builder = new TransformationBuilder2().apply(Matrix2.getEnlargementMatrix(2)); + Assertions.assertEquals(new Vector2(2, -4), builder.transform(new Vector2(1, -2))); + } + + @Test + void testRepeatedTransformation() { + // enlargement s.f. 2, then rotate pi radians + TransformationBuilder2 builder = new TransformationBuilder2() + .apply(Matrix2.getEnlargementMatrix(2)) + .apply(Matrix2.getEnlargementMatrix(2)); + Assertions.assertEquals(new Vector2(8, -16), builder.transform(new Vector2(2, -4))); + } + + @Test + void testInverseTransformation() { + // enlargement s.f. 2, then back again + TransformationBuilder2 builder = new TransformationBuilder2() + .apply(Matrix2.getEnlargementMatrix(2)) + .apply(Matrix2.getEnlargementMatrix(0.5)); + Vector2 a = new Vector2(2, -4); + Assertions.assertEquals(a, builder.transform(a)); + } + + @Test + void testRotationTransformation() { + TransformationBuilder2 builder = new TransformationBuilder2() + .apply(Matrix2.getRotationMatrix(Math.PI)); + Assertions.assertEquals(new Vector2(-1, -1), builder.transform(new Vector2(1, 1))); + } +} From 6ed6eafda670044449708eff11dfbe3eeafd260f Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sat, 24 Apr 2021 19:42:24 +0100 Subject: [PATCH 24/40] compensate for floating point inaccuracy in TransformationBuilder test --- .../transform/TransformationBuilder2Test.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java index 7160232a..9b19ac6b 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/transform/TransformationBuilder2Test.java @@ -4,6 +4,7 @@ */ package com.dumbdogdiner.stickyapi.math.transform; +import com.dumbdogdiner.stickyapi.math.NumberUtil; import com.dumbdogdiner.stickyapi.math.vector.Vector2; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -39,6 +40,24 @@ void testInverseTransformation() { void testRotationTransformation() { TransformationBuilder2 builder = new TransformationBuilder2() .apply(Matrix2.getRotationMatrix(Math.PI)); - Assertions.assertEquals(new Vector2(-1, -1), builder.transform(new Vector2(1, 1))); + // translate vectors + Vector2 original = new Vector2(1, 1); + Vector2 translated = builder.transform(original); + // due to floating point inaccuracy + Assertions.assertTrue(NumberUtil.almostEquals(-1, translated.getX())); + Assertions.assertTrue(NumberUtil.almostEquals(-1, translated.getY())); + } + + @Test + void testCompositeTransformation() { + TransformationBuilder2 builder = new TransformationBuilder2() + .apply(Matrix2.getEnlargementMatrix(2)) + .apply(Matrix2.getRotationMatrix(Math.PI)); + // translate vectors + Vector2 original = new Vector2(1, 1); + Vector2 translated = builder.transform(original); + // due to floating point inaccuracy + Assertions.assertTrue(NumberUtil.almostEquals(-2, translated.getX())); + Assertions.assertTrue(NumberUtil.almostEquals(-2, translated.getY())); } } From 99b37c9dee45610788da06fa9426a167a70a1807 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 11:24:45 +0100 Subject: [PATCH 25/40] wip :sparkles: add parametric class --- .../stickyapi/math/function/Parametric2.java | 43 +++++++++++++++++++ .../stickyapi/math/stats/Distribution.java | 2 +- .../math/function/Parametric2Test.java | 20 +++++++++ .../math/stats/DistributionTest.java | 6 +++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/function/Parametric2.java create mode 100644 math/src/test/java/com/dumbdogdiner/stickyapi/math/function/Parametric2Test.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/Parametric2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/Parametric2.java new file mode 100644 index 00000000..8b512faa --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/Parametric2.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.function; + +import com.dumbdogdiner.stickyapi.math.vector.Vector2; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * Represents a parametric curve. + */ +public class Parametric2 { + final Function x; + final Function y; + + /** + * Construct a new parametric curve from the specified functions. + * @param x The function describing the x co-ordinate + * @param y The function describing the y co-ordinate + */ + public Parametric2( + Function<@NotNull Double, @NotNull Double> x, + Function<@NotNull Double, @NotNull Double> y + ) { + this.x = x; + this.y = y; + } + + /** + * Evaluate this parametric curve at time t. + * @param t The time parameter + * @return A {@link Vector2} representing the parametric output. + */ + public Vector2 evaluate(double t) { + return new Vector2( + this.x.apply(t), + this.y.apply(t) + ); + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java index dd2a10bf..2935ddb6 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/stats/Distribution.java @@ -86,7 +86,7 @@ public static double pcc(double[] x, double[] y) { * @return true if the two datasets are associated. */ public static boolean isAssociated(double[] x, double[] y, double confidence) { - Preconditions.checkArgument(NumberUtil.inRange(0, 1, confidence)); + Preconditions.checkArgument(NumberUtil.inRange(confidence, 0, 1)); return Math.abs(pcc(x, y)) > confidence; } diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/function/Parametric2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/function/Parametric2Test.java new file mode 100644 index 00000000..de7f989e --- /dev/null +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/function/Parametric2Test.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ +package com.dumbdogdiner.stickyapi.math.function; + +import com.dumbdogdiner.stickyapi.math.vector.Vector2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class Parametric2Test { + @Test + void testParametric2() { + Parametric2 curve = new Parametric2( + (t) -> Math.pow(t,2), + (t) -> Math.pow(t, 2) - 3 * t + ); + Assertions.assertEquals(new Vector2(9, 0), curve.evaluate(3)); + } +} diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java index 36f4b4f2..2049c3e8 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/stats/DistributionTest.java @@ -34,4 +34,10 @@ void testStandardDeviation() { void testPCC() { Assertions.assertTrue(NumberUtil.almostEquals(-1, Distribution.pcc(DATASET_X, DATASET_Y))); } + + @Test + void testIsAssociated() { + Assertions.assertTrue(NumberUtil.almostEquals(-1, Distribution.pcc(DATASET_X, DATASET_Y))); + Assertions.assertTrue(Distribution.isAssociated(DATASET_X, DATASET_Y)); + } } From 612c72b75f5d695c808d7d2f8c518366ceb86003 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 11:46:41 +0100 Subject: [PATCH 26/40] fixes to randomutil --- .../stickyapi/math/NumberUtil.java | 24 ++++++++++ .../stickyapi/math/RandomUtil.java | 44 +++---------------- .../stickyapi/math/RandomUtilTest.java | 34 +++++++++++++- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index fd3a2713..3d3f217a 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -8,6 +8,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; import java.util.regex.Pattern; /** @@ -197,4 +199,26 @@ public static int longToInt(long lng) { } } } + + /** + * Convert a number of bytes to a human-readable value. + * TODO: How the absolute fuck does this work? + * + * @param bytes the value that should be converted + * @return a human-readable byte value + */ + public static String bytesToReadable(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + if (absB < 1024) { + return bytes + " B"; + } + long value = absB; + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + value *= Long.signum(bytes); + return String.format("%.1f %ciB", value / 1024.0, ci.current()); + } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index 757cadc7..8ca34661 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -27,12 +27,13 @@ private RandomUtil() { private static final Random random = new Random(); /** - * Get a random number between 0 and the specified maximum value. + * Get a random number between 0 and the specified maximum value (exclusive). * * @param max The maximum value * @return A random integer between 0 and the specified maximum value. */ public static int randomInt(int max) { + Preconditions.checkArgument(max >= 0, "Argument must be positive"); return random.nextInt(max); } @@ -50,6 +51,7 @@ public static int randomInt(int min, int max) { // exception case if (min == max) return min; + // add 1 here to ensure inclusivity return min + randomInt(1 + max - min); } @@ -77,7 +79,9 @@ public static double randomDouble(double min, double max) { // exception case if (min == max) return min; - return min + (1 + max - min) * random.nextDouble(); + // don't need to add 1 here since doubles will be inclusive as they + // are continuous, unlike integers. + return min + randomDouble(max - min); } /** @@ -106,42 +110,6 @@ public static double randomDouble(double min, double max) { return list.get(randomInt(list.size())); } - /** - * Round a double value. - * - * @param value the value that should be rounded - * @param places amount of decimal places - * @return {@link Double} - */ - public static double round(double value, int places) { - long factor = (long) Math.pow(10, places); - value = value * factor; - long tmp = Math.round(value); - return (double) tmp / factor; - } - - /** - * Convert a number of bytes to a human-readable value. - * TODO: How the absolute fuck does this work? - * - * @param bytes the value that should be converted - * @return a human-readable byte value - */ - public static String bytesToReadable(long bytes) { - long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); - if (absB < 1024) { - return bytes + " B"; - } - long value = absB; - CharacterIterator ci = new StringCharacterIterator("KMGTPE"); - for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { - value >>= 10; - ci.next(); - } - value *= Long.signum(bytes); - return String.format("%.1f %ciB", value / 1024.0, ci.current()); - } - /** * @see #randomElement(List) * Primitive values cannot be used with type generics, so these methods exist for ease of use. diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java index fa0b4876..aebf9876 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java @@ -5,23 +5,55 @@ package com.dumbdogdiner.stickyapi.math; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.RepeatedTest;; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test;; // TODO: Random element tests class RandomUtilTest { @RepeatedTest(100) void testMaxRandomInt() { + // this method is exclusive, so test for exclusivity. Assertions.assertTrue(RandomUtil.randomInt(5) < 5); } + @Test + void testRandomIntNonPositiveArgument() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + RandomUtil.randomInt(-1); + }); + } + @RepeatedTest(100) void testRangedRandomInt() { + // this method is inclusive, so test for inclusivity. int value = RandomUtil.randomInt(3, 7); Assertions.assertTrue(value >= 3 && value <= 7); } + @Test + void testRangedRandomIntIllegalRange() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + RandomUtil.randomInt(1, 0); + }); + } + + @RepeatedTest(100) + void testRangedRandomIntNegativeRange() { + Assertions.assertDoesNotThrow(() -> { + RandomUtil.randomInt(-5, 5); + }); + } + @RepeatedTest(100) void testMaxRandomDouble() { + // this method is exclusive, so test for exclusivity. Assertions.assertTrue(RandomUtil.randomDouble(5) < 5); } + + @RepeatedTest(100) + void testRangedRandomDouble() { + // this method is inclusive, so test for inclusivity. + double value = RandomUtil.randomDouble(3, 7); + Assertions.assertTrue(value >= 3 && value <= 7); + } } From 78f4dd2a8737eb308dfda30cc1b3f18a80274a7c Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:06:22 +0100 Subject: [PATCH 27/40] add random vector methods --- .../stickyapi/math/RandomUtil.java | 52 +++++++++++++++++++ .../stickyapi/math/vector/Vector.java | 2 +- .../stickyapi/math/vector/Vector2.java | 14 ++++- .../stickyapi/math/vector/Vector3.java | 15 ++++++ .../stickyapi/math/RandomUtilTest.java | 25 +++++++++ 5 files changed, 105 insertions(+), 3 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index 8ca34661..4b13655a 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -4,6 +4,8 @@ */ package com.dumbdogdiner.stickyapi.math; +import com.dumbdogdiner.stickyapi.math.vector.Vector2; +import com.dumbdogdiner.stickyapi.math.vector.Vector3; import com.google.common.base.Preconditions; import com.google.common.primitives.Chars; import com.google.common.primitives.Ints; @@ -127,4 +129,54 @@ public static char randomElement(char [] choices) { public static int randomElement(int [] choices) { return Objects.requireNonNull(randomElement(Ints.asList(choices))); } + + /** + * @return A random angle between 0 and 2pi. + */ + public static double randomAngle() { + return randomDouble(0, Math.PI * 2); + } + + /** + * @return A random angle between -pi and pi. + */ + public static double randomDualAngle() { + return randomDouble(-Math.PI, Math.PI); + } + + /** + * Return a random {@link Vector2} with magnitude r. + * @param r The radius, or magnitude of the vector + * @return A random {@link Vector2} with magnitude r. + */ + public static Vector2 randomVector2(double r) { + return Vector2.fromPolar(r, randomAngle()); + } + + /** + * Return a random unit {@link Vector2}. This vector will always + * have a magnitude of 1. + * @return A random {@link Vector2} with magnitude 1. + */ + public static Vector2 randomVector2() { + return randomVector2(1); + } + + /** + * Return a random {@link Vector3} with magnitude r. + * @param r The radius, or magnitude of the vector + * @return A random {@link Vector3} with magnitude r. + */ + public static Vector3 randomVector3(double r) { + return Vector3.fromPolar(r, randomAngle(), randomAngle()); + } + + /** + * Return a random unit {@link Vector3}. This vector will always + * have a magnitude of 1. + * @return A random {@link Vector3} with magnitude 1. + */ + public static Vector3 randomVector3() { + return randomVector3(1); + } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java index 0d4bf284..8569be41 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -94,7 +94,7 @@ public int hashCode() { /** * @return The magnitude of this vector. */ - double abs() { + public double abs() { double acc = 0; // iterate over each dimension and append square to accumulator for (int i = 0; i < this.getDimensions(); i++) { diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java index 5788dfa8..b11da535 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -12,17 +12,27 @@ * Represents a two-dimensional immutable vector. */ public class Vector2 extends Vector { + /** + * Create a new Vector2 from polar co-ordinates. + * @param r The radius of the vector + * @param theta The angle from the initial line + * @return A {@link Vector2} + */ + public static Vector2 fromPolar(double r, double theta) { + return new Vector2(r * Math.cos(theta), r * Math.sin(theta)); + } + /** * The x value of this vector. */ @Getter - private double x; + private final double x; /** * The y value of this vector. */ @Getter - private double y; + private final double y; /** * Construct a new 2D vector. diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java index 21b34c28..31b6147b 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java @@ -12,6 +12,21 @@ * Represents a three-dimensional immutable vector. */ public class Vector3 extends Vector { + /** + * Create a new Vector3 from polar co-ordinates. + * @param r The radius of the vector + * @param theta The 1st angle from the initial line + * @param phi The 2nd angle from the initial line + * @return A {@link Vector3} + */ + public static Vector3 fromPolar(double r, double theta, double phi) { + return new Vector3( + r * Math.sin(theta) * Math.cos(phi), + r * Math.sin(theta) * Math.sin(phi), + r * Math.cos(theta) + ); + } + /** * The x value of this vector. */ diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java index aebf9876..0561482a 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java @@ -56,4 +56,29 @@ void testRangedRandomDouble() { double value = RandomUtil.randomDouble(3, 7); Assertions.assertTrue(value >= 3 && value <= 7); } + + @RepeatedTest(100) + void testRandomAngle() { + double value = RandomUtil.randomAngle(); + Assertions.assertTrue(value >= 0 && value <= Math.PI * 2); + } + + @RepeatedTest(100) + void testRandomDualAngle() { + double value = RandomUtil.randomDualAngle(); + Assertions.assertTrue(value >= -Math.PI && value <= Math.PI); + } + + @RepeatedTest(100) + void testRandomVector2() { + Assertions.assertTrue(NumberUtil.almostEquals(RandomUtil.randomVector2().abs(), 1)); + Assertions.assertTrue(NumberUtil.almostEquals(RandomUtil.randomVector2(2).abs(), 2)); + } + + @RepeatedTest(100) + void testRandomVector3() { + System.out.println(RandomUtil.randomVector3().abs()); + Assertions.assertTrue(NumberUtil.almostEquals(RandomUtil.randomVector3().abs(), 1)); + Assertions.assertTrue(NumberUtil.almostEquals(RandomUtil.randomVector3(2).abs(), 2)); + } } From 3df79f54f79ea571c70ba9c8455ec8b8b9c7b219 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:11:31 +0100 Subject: [PATCH 28/40] add center parameter to random vector methods --- .../stickyapi/math/RandomUtil.java | 24 +++++++++++++++++-- .../stickyapi/math/vector/Vector2.java | 9 ++++++- .../stickyapi/math/vector/Vector3.java | 9 ++++++- .../stickyapi/math/vector/Vector2Test.java | 5 ++++ .../stickyapi/math/vector/Vector3Test.java | 5 ++++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index 4b13655a..d4d3108e 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -144,13 +144,23 @@ public static double randomDualAngle() { return randomDouble(-Math.PI, Math.PI); } + /** + * Return a random {@link Vector2} with magnitude r centered about center. + * @param r The radius, or magnitude of the vector + * @param center The vector around which the output vector will be centered + * @return A random {@link Vector2} with magnitude r. + */ + public static Vector2 randomVector2(double r, Vector2 center) { + return Vector2.fromPolar(r, randomAngle()).add(center); + } + /** * Return a random {@link Vector2} with magnitude r. * @param r The radius, or magnitude of the vector * @return A random {@link Vector2} with magnitude r. */ public static Vector2 randomVector2(double r) { - return Vector2.fromPolar(r, randomAngle()); + return randomVector2(r, Vector2.zero()); } /** @@ -162,13 +172,23 @@ public static Vector2 randomVector2() { return randomVector2(1); } + /** + * Return a random {@link Vector3} with magnitude r centered about center. + * @param r The radius, or magnitude of the vector + * @param center The vector around which the output vector will be centered + * @return A random {@link Vector3} with magnitude r. + */ + public static Vector3 randomVector3(double r, Vector3 center) { + return Vector3.fromPolar(r, randomAngle(), randomAngle()).add(center); + } + /** * Return a random {@link Vector3} with magnitude r. * @param r The radius, or magnitude of the vector * @return A random {@link Vector3} with magnitude r. */ public static Vector3 randomVector3(double r) { - return Vector3.fromPolar(r, randomAngle(), randomAngle()); + return randomVector3(r, Vector3.zero()); } /** diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java index b11da535..1f86e1af 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -12,6 +12,13 @@ * Represents a two-dimensional immutable vector. */ public class Vector2 extends Vector { + /** + * @return A zero vector. + */ + public static Vector2 zero() { + return new Vector2(0, 0); + } + /** * Create a new Vector2 from polar co-ordinates. * @param r The radius of the vector @@ -79,7 +86,7 @@ protected double[] getValues() { @Override @NotNull - Vector2 add(@NotNull Vector vector) { + public Vector2 add(@NotNull Vector vector) { Preconditions.checkNotNull(vector); return new Vector2(this.x + vector.getDimension(0), this.y + vector.getDimension(1)); } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java index 31b6147b..c46dd299 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector3.java @@ -12,6 +12,13 @@ * Represents a three-dimensional immutable vector. */ public class Vector3 extends Vector { + /** + * @return A zero vector. + */ + public static Vector3 zero() { + return new Vector3(0, 0, 0); + } + /** * Create a new Vector3 from polar co-ordinates. * @param r The radius of the vector @@ -96,7 +103,7 @@ protected double[] getValues() { @Override @NotNull - Vector3 add(@NotNull Vector vector) { + public Vector3 add(@NotNull Vector vector) { return new Vector3(this.x + vector.getDimension(0), this.y + vector.getDimension(1), this.z + vector.getDimension(2)); } diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java index e72bdd8e..3e1f99a6 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector2Test.java @@ -13,6 +13,11 @@ void testVector2Equal() { Assertions.assertEquals(new Vector2(1, 2), new Vector2(1, 2)); } + @Test + void testVector2Zero() { + Assertions.assertEquals(new Vector2(0 ,0), Vector2.zero()); + } + @Test void testVector2Add() { Vector2 a = new Vector2(0, 1); diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java index 87061b55..517afa1c 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/vector/Vector3Test.java @@ -13,6 +13,11 @@ void testVector3Equal() { Assertions.assertEquals(new Vector3(1, 2, 3), new Vector3(1, 2, 3)); } + @Test + void testVector3Zero() { + Assertions.assertEquals(new Vector3(0 ,0, 0), Vector3.zero()); + } + @Test void testVector3Add() { Vector3 a = new Vector3(0, 1, 2); From 77d0923face7f99e3184eaadb2dff24ebf1c68b7 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:16:53 +0100 Subject: [PATCH 29/40] add RandomUtil.randomIntExclusive --- .../stickyapi/math/RandomUtil.java | 30 +++++++++++++------ .../stickyapi/math/RandomUtilTest.java | 8 ++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index d4d3108e..1dd9a353 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -12,15 +12,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.text.CharacterIterator; -import java.text.StringCharacterIterator; import java.util.List; import java.util.Objects; import java.util.Random; /** * Utility class for dealing with randomness. - * TODO: Consistency issues - randomInt(int max) is exclusive, randomInt(int max, int min) isn't. */ public class RandomUtil { private RandomUtil() { @@ -29,12 +26,24 @@ private RandomUtil() { private static final Random random = new Random(); /** - * Get a random number between 0 and the specified maximum value (exclusive). + * Get a random number between 0 and the specified maximum value (inclusive). * * @param max The maximum value * @return A random integer between 0 and the specified maximum value. */ public static int randomInt(int max) { + Preconditions.checkArgument(max >= 0, "Argument must be positive"); + // add 1 to method call - not inclusive, so make it inclusive. + return random.nextInt(max + 1); + } + + /** + * Get a random number between 0 and the specified maximum value (exclusive). + * + * @param max The maximum value + * @return A random integer between 0 and the specified maximum value. + */ + public static int randomIntExclusive(int max) { Preconditions.checkArgument(max >= 0, "Argument must be positive"); return random.nextInt(max); } @@ -53,12 +62,13 @@ public static int randomInt(int min, int max) { // exception case if (min == max) return min; - // add 1 here to ensure inclusivity - return min + randomInt(1 + max - min); + return min + randomInt(max - min); } /** - * Get a random number between 0 and the specified maximum value. + * Get a random number between 0 and the specified maximum value. This method + * is exclusive, as doubles are (approximately) continuous. While it could generate + * a value equal to the maximum, this is really unlikely. * * @param max maximum value * @return A random double between 0 and the specified maximum value. @@ -96,7 +106,8 @@ public static double randomDouble(double min, double max) { // exception - array is empty if (array.length < 1) return null; // pick random index, return element at index - return array[randomInt(array.length)]; + // have to minus 1 here since this would throw an out of bounds exception + return array[randomInt(array.length - 1)]; } /** @@ -109,7 +120,8 @@ public static double randomDouble(double min, double max) { // exception - list is empty if (list.isEmpty()) return null; // pick random index, return element at index - return list.get(randomInt(list.size())); + // have to minus 1 here since this would throw an out of bounds exception + return list.get(randomInt(list.size() - 1)); } /** diff --git a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java index 0561482a..f22243f3 100644 --- a/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java +++ b/math/src/test/java/com/dumbdogdiner/stickyapi/math/RandomUtilTest.java @@ -12,8 +12,14 @@ class RandomUtilTest { @RepeatedTest(100) void testMaxRandomInt() { + // this method is inclusive, so test for inclusivity. + Assertions.assertTrue(RandomUtil.randomInt(5) <= 5); + } + + @RepeatedTest(100) + void testMaxRandomIntExclusive() { // this method is exclusive, so test for exclusivity. - Assertions.assertTrue(RandomUtil.randomInt(5) < 5); + Assertions.assertTrue(RandomUtil.randomIntExclusive(5) < 5); } @Test From 3f902ef11e34ef7ca432471f3b4d415e6d50a7c8 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:17:31 +0100 Subject: [PATCH 30/40] move RandomUtil.randomElement methods to randomIntExclusive --- .../main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index 1dd9a353..29937a1c 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -107,7 +107,7 @@ public static double randomDouble(double min, double max) { if (array.length < 1) return null; // pick random index, return element at index // have to minus 1 here since this would throw an out of bounds exception - return array[randomInt(array.length - 1)]; + return array[randomIntExclusive(array.length)]; } /** @@ -121,7 +121,7 @@ public static double randomDouble(double min, double max) { if (list.isEmpty()) return null; // pick random index, return element at index // have to minus 1 here since this would throw an out of bounds exception - return list.get(randomInt(list.size() - 1)); + return list.get(randomIntExclusive(list.size())); } /** From 90f3d9d056b23bf0c8e910f75c6665a8c321ef3c Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:24:21 +0100 Subject: [PATCH 31/40] tweaks to javadoc --- .../stickyapi/math/Interpolation.java | 1 + .../stickyapi/math/NumberUtil.java | 17 +++++------- .../dumbdogdiner/stickyapi/math/Ordinal.java | 2 +- .../stickyapi/math/RandomUtil.java | 27 ++++++++----------- .../stickyapi/math/vector/Vector.java | 3 +-- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java index 8bbe9ca9..60427f41 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java @@ -40,6 +40,7 @@ public CubicBezier(double a, double b, double c, double d) { * Evaluate this cubic bezier for time t. * @param t The time parameter * @return A value based on the cubic bezier. + * TODO: Implement this */ public double evaluate(Number t) { return t.doubleValue(); diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index 3d3f217a..40ebb3c5 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -20,7 +20,6 @@ private NumberUtil() {} /** * Test whether the specified number is within the specified range. - * * @param number The specified number * @param min The minimum value (inclusive) * @param max The maximum value (inclusive) @@ -31,22 +30,23 @@ public static boolean inRange(Number number, Number min, Number max) { } /** - * Round a double value - * + * Round a double value. * @param value the value that should be rounded * @param places amount of decimal places * @return {@link Double} */ public static double round(double value, int places) { + // compute power of ten equal to places long factor = (long) Math.pow(10, places); - value = value * factor; + value *= factor; + // chop off remaining decimal places long tmp = Math.round(value); + // divide back down to get fixed number of places return (double) tmp / factor; } /** * Test if the target string can be considered an integer. Checks if every character is a unicode digit. - * *
 	 * NumberUtil.isInteger("123")         = true
 	 * NumberUtil.isInteger("-123")        = true
@@ -59,7 +59,6 @@ public static double round(double value, int places) {
 	 * NumberUtil.isInteger("12.3")        = false
 	 * NumberUtil.isInteger("-123", false) = false
 	 * 
- * * @param string The string to check * @param signed Whether to allow signed numbers * @return true if the target string can be considered an integer. @@ -85,8 +84,8 @@ public static boolean isInteger(@Nullable String string, boolean signed) { } /** - * Test if the target string can be considered an integer. Checks if every character is a unicode digit. - * Allows signed numbers. + * Test if the target string can be considered an integer by checking if every character is a unicode digit. + * By default, this method allows signed numbers. * @param string The target string * @return true if the target string can be considered a signed integer. */ @@ -168,7 +167,6 @@ public static String getPercentageString(Number x, Number total, int places) { /** * Get a number as the percentage of another. - * * @param x The number who's percentage of the total this method will return * @param total The total * @return A formatted {@link String} containing the percentage to 2 decimal places. @@ -203,7 +201,6 @@ public static int longToInt(long lng) { /** * Convert a number of bytes to a human-readable value. * TODO: How the absolute fuck does this work? - * * @param bytes the value that should be converted * @return a human-readable byte value */ diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java index 37855317..de67062f 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Ordinal.java @@ -13,7 +13,7 @@ public final class Ordinal { private Ordinal() {} /** - * Get the appropriate ordinal for the + * Get the appropriate ordinal for the specified number. * @param number The number to fetch the ordinal for * @return A {@link String} containing the appropriate ordinal. */ diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index 29937a1c..e592c547 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -27,7 +27,6 @@ private RandomUtil() { /** * Get a random number between 0 and the specified maximum value (inclusive). - * * @param max The maximum value * @return A random integer between 0 and the specified maximum value. */ @@ -39,7 +38,6 @@ public static int randomInt(int max) { /** * Get a random number between 0 and the specified maximum value (exclusive). - * * @param max The maximum value * @return A random integer between 0 and the specified maximum value. */ @@ -49,8 +47,7 @@ public static int randomIntExclusive(int max) { } /** - * Get a random number within a range. - * + * Get a random number within the specified inclusive range. * @param min The minimum value (inclusive) * @param max The maximum value (inclusive) * @return A random integer within the specified range @@ -69,7 +66,6 @@ public static int randomInt(int min, int max) { * Get a random number between 0 and the specified maximum value. This method * is exclusive, as doubles are (approximately) continuous. While it could generate * a value equal to the maximum, this is really unlikely. - * * @param max maximum value * @return A random double between 0 and the specified maximum value. */ @@ -78,11 +74,12 @@ public static double randomDouble(double max) { } /** - * Get a random double within a range - * + * Get a random double within the specified inclusive range. This method + * is exclusive, as doubles are (approximately) continuous. While it could generate + * a value equal to the maximum, this is really unlikely. * @param min minimum value * @param max maximum value - * @return a random double within the specified range + * @return A random double between min and max. * @throws IllegalArgumentException when min is greater than max */ public static double randomDouble(double min, double max) { @@ -98,7 +95,6 @@ public static double randomDouble(double min, double max) { /** * Get a random element from the target array. - * * @param array The target array * @return A random element from the target array. */ @@ -112,7 +108,6 @@ public static double randomDouble(double min, double max) { /** * Get a random element from the target list. - * * @param list The target list * @return A random element from the target list. */ @@ -157,7 +152,7 @@ public static double randomDualAngle() { } /** - * Return a random {@link Vector2} with magnitude r centered about center. + * Generate a random {@link Vector2} with magnitude r centered about center. * @param r The radius, or magnitude of the vector * @param center The vector around which the output vector will be centered * @return A random {@link Vector2} with magnitude r. @@ -167,7 +162,7 @@ public static Vector2 randomVector2(double r, Vector2 center) { } /** - * Return a random {@link Vector2} with magnitude r. + * Generate a random {@link Vector2} with magnitude r. * @param r The radius, or magnitude of the vector * @return A random {@link Vector2} with magnitude r. */ @@ -176,7 +171,7 @@ public static Vector2 randomVector2(double r) { } /** - * Return a random unit {@link Vector2}. This vector will always + * Generate a random unit {@link Vector2}. This vector will always * have a magnitude of 1. * @return A random {@link Vector2} with magnitude 1. */ @@ -185,7 +180,7 @@ public static Vector2 randomVector2() { } /** - * Return a random {@link Vector3} with magnitude r centered about center. + * Generate a random {@link Vector3} with magnitude r centered about center. * @param r The radius, or magnitude of the vector * @param center The vector around which the output vector will be centered * @return A random {@link Vector3} with magnitude r. @@ -195,7 +190,7 @@ public static Vector3 randomVector3(double r, Vector3 center) { } /** - * Return a random {@link Vector3} with magnitude r. + * Generate a random {@link Vector3} with magnitude r. * @param r The radius, or magnitude of the vector * @return A random {@link Vector3} with magnitude r. */ @@ -204,7 +199,7 @@ public static Vector3 randomVector3(double r) { } /** - * Return a random unit {@link Vector3}. This vector will always + * Generate a random unit {@link Vector3}. This vector will always * have a magnitude of 1. * @return A random {@link Vector3} with magnitude 1. */ diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java index 8569be41..a5c80bf5 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector.java @@ -6,7 +6,6 @@ import com.google.common.base.Preconditions; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * Base abstract class for an immutable vector. @@ -20,7 +19,7 @@ abstract class Vector implements Cloneable { /** * Get an array of values representing this vector. - * @return An array of values. + * @return An array of values representing this vector's components. */ protected abstract double[] getValues(); From 8de420687624dc308db2f98b9b0f01c93bb0b9c6 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:29:28 +0100 Subject: [PATCH 32/40] add NumberUtil.isNumeric comment --- .../stickyapi/math/NumberUtil.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java index 40ebb3c5..ea2a4608 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/NumberUtil.java @@ -97,13 +97,30 @@ public static boolean isInteger(@Nullable String string) { private static final Pattern NUMERIC_REGEX = Pattern.compile("-?[0-9]+\\.?[0-9]+(?:e-?[0-9]+)?$"); /** - * Checks if the string is considered numeric. - * - * @param string the String to check, may be null + * Checks if the string is considered numeric. This method accounts for signed integers + * and engineering notation (i.e. 12e3). + *
+	 * NumberUtil.isNumeric("123")         = true
+	 * NumberUtil.isNumeric("-123")        = true
+	 * NumberUtil.isNumeric("12.3")        = true
+	 * NumberUtil.isNumeric("12e3")        = true
+	 * NumberUtil.isNumeric("12e-3")       = true
+	 * NumberUtil.isNumeric(null)          = false
+	 * NumberUtil.isNumeric("")            = false
+	 * NumberUtil.isNumeric("  ")          = false
+	 * NumberUtil.isNumeric("12 3")        = false
+	 * NumberUtil.isNumeric("ab2c")        = false
+	 * NumberUtil.isNumeric("12-3")        = false
+	 * 
+ * @param string the string to check * @return true if the string is numeric */ - public static boolean isNumeric(@NotNull String string) { - Preconditions.checkNotNull(string); + public static boolean isNumeric(@Nullable String string) { + // check for null + if (string == null) { + return false; + } + // match string with regex return NUMERIC_REGEX.matcher(string).matches(); } From a9da72f1e6fd77e7015ba11ceb0d11fd3933f455 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:31:04 +0100 Subject: [PATCH 33/40] remove dependency on stickyapi:common --- math/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/math/build.gradle b/math/build.gradle index 7d4f56a5..8e7d76d5 100644 --- a/math/build.gradle +++ b/math/build.gradle @@ -1,5 +1,4 @@ dependencies { - implementation project(":common") // TODO: Investigate why this isn't provided by common. implementation "com.google.guava:guava:30.1.1-jre" } From 6779650f51f8169e6965992a536c53d3a75326e1 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:50:22 +0100 Subject: [PATCH 34/40] wip :sparkles: cubic bezier implementation --- .../stickyapi/math/Interpolation.java | 47 ++++--------- .../stickyapi/math/function/CubicBezier.java | 66 +++++++++++++++++++ .../stickyapi/math/vector/Vector2.java | 9 ++- 3 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java index 60427f41..5f8b24bb 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/Interpolation.java @@ -13,40 +13,6 @@ public final class Interpolation { private Interpolation() {} - /** - * Represents a cubic bezier curve. - */ - public class CubicBezier { - private double a; - private double b; - private double c; - private double d; - - /** - * Construct a new cubic bezier with the specified parameters - * @param a The first parameter - * @param b The second parameter - * @param c The third parameter - * @param d The fourth parameter - */ - public CubicBezier(double a, double b, double c, double d) { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } - - /** - * Evaluate this cubic bezier for time t. - * @param t The time parameter - * @return A value based on the cubic bezier. - * TODO: Implement this - */ - public double evaluate(Number t) { - return t.doubleValue(); - } - } - /** * Clamp the value `t` between `min` and `max`. * @param max The minimum value @@ -87,4 +53,17 @@ public static double lerpUnclamped(Number a, Number b, Number t) { Preconditions.checkNotNull(t); return b.doubleValue() * t.doubleValue() + (1 - t.doubleValue()) * a.doubleValue(); } + + /** + * Sinusoidaly interpolate between a and b with time parameter t. + * @param a The first value + * @param b The second value + * @param t The time parameter + * @return The sinusoidaly interpolated value. + */ + public static double sinusoidal(Number a, Number b, Number t) { + // calculate sinusoidal param + double param = Math.pow(Math.sin(t.doubleValue() * Math.PI / 2), 2); + return lerp(a, b, param); + } } diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java new file mode 100644 index 00000000..51b7e74d --- /dev/null +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java @@ -0,0 +1,66 @@ +package com.dumbdogdiner.stickyapi.math.function; + +import com.dumbdogdiner.stickyapi.math.vector.Vector2; + +/** + * Represents a cubic bezier curve. + */ +public class CubicBezier { + private Vector2 a; + private Vector2 b; + private Vector2 c; + private Vector2 d; + + /** + * Construct a new cubic bezier with the specified parameters + * @param a The first parameter + * @param b The second parameter + * @param c The third parameter + * @param d The fourth parameter + */ + public CubicBezier(double a, double b, double c, double d) { + this(new Vector2(a, b), new Vector2(c, d)); + } + + /** + * Construct a new cubic bezier with the specified vector parameters. + * @param a The first vector + * @param b The second vector + */ + public CubicBezier(Vector2 a, Vector2 b) { + this(Vector2.zero(), a, b, Vector2.one()); + } + + /** + * Construct a raw cubic bezier with the specified vector parameters. + * @param a The first vector + * @param b The second vector + * @param c The third vector + * @param d The fourth vector + */ + public CubicBezier(Vector2 a, Vector2 b, Vector2 c, Vector2 d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + /** + * Evaluate this cubic bezier for time val. + * @param val The time parameter + * @return A value based on the cubic bezier. + */ + public Vector2 evaluateToVector(Number val) { + double t = val.doubleValue(); + double q = 1 - t; + + // B(t) = (1 - t)^3P0 + (1-t)^2*tP1 etc. + return this.a.scale(Math.pow(q, 3)).add( + this.b.scale(3 * Math.pow(q, 2) * t) + ).add( + this.c.scale(3 * q * Math.pow(t, 2)) + ).add( + this.c.scale(Math.pow(t, 3)) + ); + } +} diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java index 1f86e1af..705f9bce 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/vector/Vector2.java @@ -19,6 +19,13 @@ public static Vector2 zero() { return new Vector2(0, 0); } + /** + * @return A one vector. + */ + public static Vector2 one() { + return new Vector2(1, 1); + } + /** * Create a new Vector2 from polar co-ordinates. * @param r The radius of the vector @@ -100,7 +107,7 @@ Vector2 subtract(@NotNull Vector vector) { @Override @NotNull - Vector2 scale(@NotNull Number scalar) { + public Vector2 scale(@NotNull Number scalar) { Preconditions.checkNotNull(scalar); return new Vector2(this.x * scalar.doubleValue(), this.y * scalar.doubleValue()); } From 93465d83b79a4ed0f2ee155cdfdce4d6f7c8306b Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 12:55:10 +0100 Subject: [PATCH 35/40] amend parameter in bezier evaluation --- .../stickyapi/math/function/CubicBezier.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java index 51b7e74d..085668ce 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java @@ -6,9 +6,9 @@ * Represents a cubic bezier curve. */ public class CubicBezier { - private Vector2 a; - private Vector2 b; - private Vector2 c; + private final Vector2 a; + private final Vector2 b; + private final Vector2 c; private Vector2 d; /** @@ -24,11 +24,11 @@ public CubicBezier(double a, double b, double c, double d) { /** * Construct a new cubic bezier with the specified vector parameters. - * @param a The first vector - * @param b The second vector + * @param b The first vector + * @param c The second vector */ - public CubicBezier(Vector2 a, Vector2 b) { - this(Vector2.zero(), a, b, Vector2.one()); + public CubicBezier(Vector2 b, Vector2 c) { + this(Vector2.zero(), b, c, Vector2.one()); } /** From 46ab4a84fd1830433f2fda23193e5cfd84a540ca Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 18:47:01 +0100 Subject: [PATCH 36/40] add LocationUtil to bukkit --- .../stickyapi/bukkit/util/LocationUtil.java | 32 +++++++++++++++++++ common/build.gradle | 1 + config/README.md | 0 .../stickyapi/math/function/CubicBezier.java | 4 +-- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java create mode 100644 config/README.md diff --git a/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java new file mode 100644 index 00000000..2dae8273 --- /dev/null +++ b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java @@ -0,0 +1,32 @@ +package com.dumbdogdiner.stickyapi.bukkit.util; + +import com.dumbdogdiner.stickyapi.math.vector.Vector3; +import org.bukkit.Location; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +/** + * Utilities for converting {@link Vector3}s into Bukkit's {@link Location} object. + */ +public final class LocationUtil { + private LocationUtil() {} + + /** + * Convert the target {@link Vector3} into a Bukkit {@link Location}. + * @param world The world this location should be contained in + * @param vector The target vector + * @return A new {@link Location} in the target world. + */ + public static @NotNull Location toLocation(@NotNull World world, @NotNull Vector3 vector) { + return new Location(world, vector.getX(), vector.getY(), vector.getZ()); + } + + /** + * Convert the target {@link Location} into a {@link Vector3}. + * @param location The target location + * @return A new {@link Location} in the target world. + */ + public static @NotNull Vector3 toVector3(@NotNull Location location) { + return new Vector3(location.getX(), location.getY(), location.getZ()); + } +} diff --git a/common/build.gradle b/common/build.gradle index 0462850a..473b51a4 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -9,6 +9,7 @@ plugins { dependencies { // Depend on the config project api project(":config") // api - transistively expose config dependency when implementing :common + api project(":math") // api - transistively expose api dependency when implementing :common testImplementation project(":config").sourceSets.test.output // LocaleProviderTest - add snakeyaml so YamlProvider (from the :config project) can work properly diff --git a/config/README.md b/config/README.md new file mode 100644 index 00000000..e69de29b diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java index 085668ce..d774ff74 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java @@ -9,7 +9,7 @@ public class CubicBezier { private final Vector2 a; private final Vector2 b; private final Vector2 c; - private Vector2 d; + private final Vector2 d; /** * Construct a new cubic bezier with the specified parameters @@ -60,7 +60,7 @@ public Vector2 evaluateToVector(Number val) { ).add( this.c.scale(3 * q * Math.pow(t, 2)) ).add( - this.c.scale(Math.pow(t, 3)) + this.d.scale(Math.pow(t, 3)) ); } } From 16bda31a37cb0f4e73a868dd0b10c52a0df4e523 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 19:18:30 +0100 Subject: [PATCH 37/40] deprecate old MathUtils and RandomUtils --- .../stickyapi/bukkit/util/PlayerSelector.java | 4 ++-- .../stickyapi/common/arguments/Arguments.java | 6 +++--- .../stickyapi/common/util/MathUtil.java | 4 ++-- .../stickyapi/common/util/NumberUtil.java | 4 +++- .../stickyapi/common/util/StringUtil.java | 7 ++++--- .../stickyapi/common/util/TimeUtil.java | 3 ++- .../common/nbt/NbtNumberTagTest.java | 7 +++---- .../stickyapi/math/RandomUtil.java | 19 +++++++++++++++++++ 8 files changed, 38 insertions(+), 16 deletions(-) diff --git a/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/PlayerSelector.java b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/PlayerSelector.java index f71aa801..76c14515 100644 --- a/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/PlayerSelector.java +++ b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/PlayerSelector.java @@ -4,7 +4,7 @@ */ package com.dumbdogdiner.stickyapi.bukkit.util; -import com.dumbdogdiner.stickyapi.common.util.MathUtil; +import com.dumbdogdiner.stickyapi.math.RandomUtil; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; @@ -101,7 +101,7 @@ public static Predicate inRange(Location center, double radius) { */ public static Player selectRandom(Predicate condition) { List list = selectPlayers(condition); - return list.get(MathUtil.randomInt(list.size())); + return RandomUtil.randomElement(list); } /** diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java index e442bd55..a4c72ca3 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java @@ -12,9 +12,9 @@ import java.util.List; import com.dumbdogdiner.stickyapi.common.util.Debugger; -import com.dumbdogdiner.stickyapi.common.util.NumberUtil; import com.dumbdogdiner.stickyapi.common.util.TimeUtil; +import com.dumbdogdiner.stickyapi.math.NumberUtil; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -356,7 +356,7 @@ public Arguments requiredTimeString(@NotNull String name) { private Arguments optionalIntImplementation(@NotNull String name, @NotNull Integer fallback) { debug.print("Looking for optional integer " + name + "..."); - if (unparsedArgs.size() > position && NumberUtil.isNumeric(unparsedArgs.get(position))) { + if (unparsedArgs.size() > position && NumberUtil.isInteger(unparsedArgs.get(position), false)) { parsedArgs.put(name, unparsedArgs.get(position)); position++; debug.print("Found int at position " + String.valueOf(position) + " - new args size = " @@ -405,7 +405,7 @@ public Arguments optionalInt(@NotNull String name, @NotNull Integer fallback) { public Arguments requiredInt(@NotNull String name) { debug.print("Looking for optional required " + name + "..."); - if (unparsedArgs.size() > position && NumberUtil.isNumeric(unparsedArgs.get(position))) { + if (unparsedArgs.size() > position && NumberUtil.isInteger(unparsedArgs.get(position, false))) { parsedArgs.put(name, unparsedArgs.get(position)); position++; debug.print("Found int at position " + String.valueOf(position) + " - new args size = " diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/MathUtil.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/MathUtil.java index b20c6cb3..eecf1513 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/MathUtil.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/MathUtil.java @@ -15,10 +15,10 @@ import java.util.Random; /** - *

* Util class for commonly used math operations. - *

+ * @deprecated in favor of the stickyapi:math module. */ +@Deprecated(since = "3.1") public class MathUtil { private MathUtil() { } diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java index 1515543e..09579b4b 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/NumberUtil.java @@ -10,7 +10,9 @@ *

* Provides extra functionality for Java Number classes. *

- */ + * @deprecated in favor of the stickyapi:math module. +*/ +@Deprecated(since = "3.1") public final class NumberUtil { private NumberUtil() { } diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java index 6e5922ce..067649c9 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/StringUtil.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.UUID; +import com.dumbdogdiner.stickyapi.math.RandomUtil; import com.google.common.base.Preconditions; import net.md_5.bungee.api.ChatColor; @@ -333,16 +334,16 @@ public static String randomObfuscatedString(int min, int max, int minRunBeforeSp }; StringBuilder obfuscated = new StringBuilder(); - int len = MathUtil.randomInt(min, max); + int len = RandomUtil.randomInt(min, max); int charsSinceSpace = 0; while (obfuscated.length() < len) { if (minRunBeforeSpace > 0 && charsSinceSpace > minRunBeforeSpace && // Set a 5% probability of the character being a space - MathUtil.randomInt(1, 100) <= 5) { + RandomUtil.probability(0.05)) { obfuscated.append(' '); charsSinceSpace = 0; } else { - obfuscated.append(MathUtil.randomElement(choices)); + obfuscated.append(RandomUtil.randomElement(choices)); charsSinceSpace++; } } diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java index a13faa49..d5f8f266 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/util/TimeUtil.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Optional; +import com.dumbdogdiner.stickyapi.math.NumberUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -286,7 +287,7 @@ public static Timestamp toTimestamp(@NotNull String timePeriod) { return null; // If it's numeric, lets do some extra checks! - if (NumberUtil.isNumeric(timePeriod)) { + if (NumberUtil.isInteger(timePeriod, false)) { // Return null if it's greater 12 characters long if (timePeriod.length() > 12) return null; diff --git a/common/src/test/java/com/dumbdogdiner/stickyapi/common/nbt/NbtNumberTagTest.java b/common/src/test/java/com/dumbdogdiner/stickyapi/common/nbt/NbtNumberTagTest.java index 9f84a4f0..91f077c4 100644 --- a/common/src/test/java/com/dumbdogdiner/stickyapi/common/nbt/NbtNumberTagTest.java +++ b/common/src/test/java/com/dumbdogdiner/stickyapi/common/nbt/NbtNumberTagTest.java @@ -4,6 +4,7 @@ */ package com.dumbdogdiner.stickyapi.common.nbt; +import com.dumbdogdiner.stickyapi.math.RandomUtil; import com.google.gson.JsonPrimitive; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; @@ -11,8 +12,6 @@ import java.text.MessageFormat; -import static com.dumbdogdiner.stickyapi.common.util.MathUtil.randomDouble; -import static com.dumbdogdiner.stickyapi.common.util.MathUtil.randomInt; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.*; @@ -23,8 +22,8 @@ class NbtNumberTagTest { @BeforeEach public void setUp(){ - i = randomInt(-256, 4096); - f = (float) randomDouble(-256, 8192); + i = RandomUtil.randomInt(-256, 4096); + f = (float) RandomUtil.randomDouble(-256, 8192); } @RepeatedTest(REPEAT) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java index e592c547..5b5737a8 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/RandomUtil.java @@ -62,6 +62,16 @@ public static int randomInt(int min, int max) { return min + randomInt(max - min); } + /** + * Get a random number between 0 and 1. This method is exclusive, as doubles are + * (approximately) continuous. While it could generate a value equal to the maximum, + * this is really unlikely + * @return A random double between 0 and 1. + */ + public static double randomDouble() { + return random.nextDouble(); + } + /** * Get a random number between 0 and the specified maximum value. This method * is exclusive, as doubles are (approximately) continuous. While it could generate @@ -137,6 +147,15 @@ public static int randomElement(int [] choices) { return Objects.requireNonNull(randomElement(Ints.asList(choices))); } + /** + * Return a random boolean with probability p of being true. + * @param probability The probability of returning + * @return A random boolean with probability p of being true. + */ + public static boolean probability(double probability) { + return randomDouble() > probability; + } + /** * @return A random angle between 0 and 2pi. */ From 3f4aa73f0a7e8935e4dae0b963a738d464e6481e Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 19:20:35 +0100 Subject: [PATCH 38/40] fix RandomUtil call --- .../com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java | 4 ++++ .../dumbdogdiner/stickyapi/common/arguments/Arguments.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java index 2dae8273..e630135e 100644 --- a/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java +++ b/bukkit/src/main/java/com/dumbdogdiner/stickyapi/bukkit/util/LocationUtil.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ package com.dumbdogdiner.stickyapi.bukkit.util; import com.dumbdogdiner.stickyapi.math.vector.Vector3; diff --git a/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java b/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java index a4c72ca3..bef460b9 100644 --- a/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java +++ b/common/src/main/java/com/dumbdogdiner/stickyapi/common/arguments/Arguments.java @@ -405,7 +405,7 @@ public Arguments optionalInt(@NotNull String name, @NotNull Integer fallback) { public Arguments requiredInt(@NotNull String name) { debug.print("Looking for optional required " + name + "..."); - if (unparsedArgs.size() > position && NumberUtil.isInteger(unparsedArgs.get(position, false))) { + if (unparsedArgs.size() > position && NumberUtil.isInteger(unparsedArgs.get(position), false)) { parsedArgs.put(name, unparsedArgs.get(position)); position++; debug.print("Found int at position " + String.valueOf(position) + " - new args size = " From 8e97b310a13546e4f74713761c97c351d8503359 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 19:20:50 +0100 Subject: [PATCH 39/40] add license header to CubicBezier --- .../com/dumbdogdiner/stickyapi/math/function/CubicBezier.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java index d774ff74..5af2c393 100644 --- a/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java +++ b/math/src/main/java/com/dumbdogdiner/stickyapi/math/function/CubicBezier.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2020-2021 DumbDogDiner . All rights reserved. + * Licensed under the MIT license, see LICENSE for more information... + */ package com.dumbdogdiner.stickyapi.math.function; import com.dumbdogdiner.stickyapi.math.vector.Vector2; From 1446c6407ebc1ca8f3923693b5d96b47657ae5f0 Mon Sep 17 00:00:00 2001 From: SkyezerFox Date: Sun, 25 Apr 2021 19:47:50 +0100 Subject: [PATCH 40/40] bump gradle version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 829256f1..fd1ab2ae 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ plugins { allprojects { group = "com.dumbdogdiner" - version = "3.0.2" + version = "3.1.0" // java plugin is applied in subprojects apply plugin: "jacoco"