diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..bfa0703 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: publish +on: + push: + branches: [ master ] +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'temurin' + - name: Build with Gradle + run: cmd /r gradlew.bat build + - name: Publish package + uses: gradle/gradle-build-action@749f47bda3e44aa060e82d7b3ef7e40d953bd629 + with: + arguments: publish + env: + MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/publish.yml similarity index 100% rename from .github/workflows/github-actions-demo.yml rename to .github/workflows/publish.yml diff --git a/lib/build.gradle b/lib/build.gradle new file mode 100644 index 0000000..81aac85 --- /dev/null +++ b/lib/build.gradle @@ -0,0 +1,88 @@ +import org.gradle.internal.os.OperatingSystem +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java library project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.5.1/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the java-library plugin for API and implementation separation. + id 'java-library' + id 'maven-publish' + id "com.palantir.git-version" version "3.0.0" +} + +group = "mil.army.usace.hec" +version = "1.0.0" + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit test framework. + testImplementation(libs.junit) + + // This dependency is exported to consumers, that is to say found on their compile classpath. + api(libs.commons.math3) + + // This dependency is used internally, and not exposed to consumers on their own compile classpath. + implementation(libs.guava) +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } +} + +def versionLabel(gitInfo) { + // A release is published by tagging the master branch with "vX.Y" (e.g. v1.1). + // The same tag versions all jars uniformly; the leading "v" is stripped so the + // artifact version is just the number (e.g. 1.1). + // + // The release version is used ONLY when HEAD is exactly on the tagged commit with a + // clean tree (isCleanTag). Any commit past the tag — including later branch merges — + // falls back to -SNAPSHOT. Without the isCleanTag guard, lastTag returns the nearest + // reachable tag, so every build after a vX.Y tag would keep re-publishing that release + // version from unreleased code until a newer tag superseded it. + def tag = gitInfo.lastTag?.trim() + println(tag) + if (gitInfo.isCleanTag && tag && tag ==~ /v\d+(\.\d+)*/) { + return tag.substring(1) + } + return "-SNAPSHOT" +} + +publishing { + version = versionLabel(versionDetails()) + println("selected tag: " + version) + publications { + mavenJava(MavenPublication) { + from components.java + groupId = 'mil.army.usace.hec' + artifactId = 'expressions' + } + } + repositories { + maven { + name = "hecNexus" + credentials { + username = System.getenv("MAVEN_USERNAME") + password = System.getenv("MAVEN_PASSWORD") + } + def releasesRepoUrl = "https://www.hec.usace.army.mil/nexus/repository/maven-releases/" + def snapshotsRepoUrl = "https://www.hec.usace.army.mil/nexus/repository/maven-snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + } + } +} + +publish.dependsOn(build) + +build { + dependsOn jar +} \ No newline at end of file diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts deleted file mode 100644 index 54fa4a8..0000000 --- a/lib/build.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * This generated file contains a sample Java library project to get you started. - * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.5.1/userguide/building_java_projects.html in the Gradle documentation. - */ - -plugins { - // Apply the java-library plugin for API and implementation separation. - `java-library` -} - -repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() -} - -dependencies { - // Use JUnit test framework. - testImplementation(libs.junit) - - // This dependency is exported to consumers, that is to say found on their compile classpath. - api(libs.commons.math3) - - // This dependency is used internally, and not exposed to consumers on their own compile classpath. - implementation(libs.guava) -} - -// Apply a specific Java toolchain to ease working on different environments. -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} diff --git a/lib/src/main/java/usace/hec/expressions/ExpressionOperator.java b/lib/src/main/java/usace/hec/expressions/ExpressionOperator.java index 502b7e8..f54bc9c 100644 --- a/lib/src/main/java/usace/hec/expressions/ExpressionOperator.java +++ b/lib/src/main/java/usace/hec/expressions/ExpressionOperator.java @@ -21,7 +21,9 @@ public enum ExpressionOperator { AFTER("is after?"), BEFORE("is before?"), TODAY("TODAY()"), - DAY("Day"); + DOY("Day"), + LAG("Lagged"), + RAND("Random"); private final String op; ExpressionOperator(){ //used for time nodes diff --git a/lib/src/main/java/usace/hec/expressions/misc/LagNode.java b/lib/src/main/java/usace/hec/expressions/misc/LagNode.java new file mode 100644 index 0000000..3b50309 --- /dev/null +++ b/lib/src/main/java/usace/hec/expressions/misc/LagNode.java @@ -0,0 +1,58 @@ +package usace.hec.expressions.misc; + +import usace.hec.expressions.BinaryExpressionNode; +import usace.hec.expressions.ExpressionNode; +import usace.hec.expressions.ExpressionOperator; + +import java.util.ArrayDeque; +import java.util.Queue; + +public class LagNode extends BinaryExpressionNode { + private Queue lagQueue; + private Integer lag; + public LagNode(ExpressionNode left, ExpressionNode right) { + super(left, right); + } + + @Override + public String OpName() { + return Operator().getPrefixName(); + } + + @Override + public String InfixOpName() { + return Operator().getInfixName(); + } + + @Override + public ExpressionOperator Operator() { + return ExpressionOperator.LAG; + } + + @Override + public void excelAppend(StringBuilder sb){ + sb.append(InfixOpName()); + sb.append('('); + leftnode.excelAppend(sb); + sb.append(','); + rightnode.excelAppend(sb); + sb.append(')'); + } + + @Override + public T evaluate() { + //TODO: initialize Queue with values that come lag steps before simulation start + if (lagQueue == null) { + lag = rightnode.evaluate().intValue(); + lagQueue = new ArrayDeque<>(); + T dummyVal = leftnode.evaluate(); + for (int i = 0; i < lag; i++) { + lagQueue.add(dummyVal); + } + } + else { + lagQueue.add(leftnode.evaluate()); + } + return lagQueue.poll(); + } +} diff --git a/lib/src/main/java/usace/hec/expressions/misc/RandNode.java b/lib/src/main/java/usace/hec/expressions/misc/RandNode.java new file mode 100644 index 0000000..5396dd5 --- /dev/null +++ b/lib/src/main/java/usace/hec/expressions/misc/RandNode.java @@ -0,0 +1,38 @@ +package usace.hec.expressions.misc; + +import usace.hec.expressions.ExpressionNode; +import usace.hec.expressions.ExpressionOperator; +import usace.hec.expressions.UnaryExpressionNode; + +import java.util.Random; +import java.util.random.RandomGenerator; + +public class RandNode extends UnaryExpressionNode { + private final int SEED; + private final Random RAND; + public RandNode(ExpressionNode child) { + super(child); + SEED = child.evaluate(); //safe because it uses a constant leaf node, which should already have an initial value. + RAND = new Random(SEED); + } + + @Override + public String OpName() { + return Operator().getPrefixName(); + } + + @Override + public String InfixOpName() { + return Operator().getInfixName(); + } + + @Override + public ExpressionOperator Operator() { + return ExpressionOperator.RAND; + } + + @Override + public Double evaluate() { + return RAND.nextDouble(); + } +} diff --git a/lib/src/main/java/usace/hec/expressions/time/DayOfYearNode.java b/lib/src/main/java/usace/hec/expressions/time/DayOfYearNode.java index 4abd65f..97575f2 100644 --- a/lib/src/main/java/usace/hec/expressions/time/DayOfYearNode.java +++ b/lib/src/main/java/usace/hec/expressions/time/DayOfYearNode.java @@ -28,7 +28,7 @@ public String InfixOpName() { } @Override public ExpressionOperator Operator() { - return ExpressionOperator.DAY; + return ExpressionOperator.DOY; } @Override diff --git a/lib/src/test/java/usace/hec/expressions/misc/LagNodeTest.java b/lib/src/test/java/usace/hec/expressions/misc/LagNodeTest.java new file mode 100644 index 0000000..2c1b101 --- /dev/null +++ b/lib/src/test/java/usace/hec/expressions/misc/LagNodeTest.java @@ -0,0 +1,48 @@ +package usace.hec.expressions.misc; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import usace.hec.expressions.*; +import usace.hec.expressions.math.AddNode; +import usace.hec.expressions.math.MinusNode; + +public class LagNodeTest { + @Test + public void basicLagTest(){ + ExpressionNode X = new UpdateableLeafNode<>("X"); + ExpressionNode lag = new ConstantLeafNode<>(4); + ExpressionNode lagNode = new LagNode<>(X, lag); + + BaseDataUpdater adu = new BaseDataUpdater(); + + List> list = lagNode.fetchListeners(); + for(DataListener d : list) { + adu.register(d); + } + adu.publish("X", 1.0); + Double result = lagNode.evaluate(); + assertEquals(1.0, result, 0.0); + adu.publish("X", 2.0); + result = lagNode.evaluate(); + assertEquals(1.0, result, 0.0); + adu.publish("X", 3.0); + result = lagNode.evaluate(); + assertEquals(1.0, result, 0.0); + adu.publish("X", 4.0); + result = lagNode.evaluate(); + assertEquals(1.0, result, 0.0); + adu.publish("X", 5.0); + result = lagNode.evaluate(); + assertEquals(2.0, result, 0.0); + + String expression = lagNode.PreFixSyntax(); + System.out.print(expression + "\n"); + String expressionInfix = lagNode.ExcelSyntax(); + System.out.print(expressionInfix+ "\n"); + } + +} diff --git a/lib/src/test/java/usace/hec/expressions/misc/RandNodeTest.java b/lib/src/test/java/usace/hec/expressions/misc/RandNodeTest.java new file mode 100644 index 0000000..2f9534f --- /dev/null +++ b/lib/src/test/java/usace/hec/expressions/misc/RandNodeTest.java @@ -0,0 +1,43 @@ +package usace.hec.expressions.misc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Random; + +import org.junit.Test; + +import usace.hec.expressions.*; +import usace.hec.expressions.math.AddNode; +import usace.hec.expressions.math.MinusNode; + +public class RandNodeTest { + @Test + public void basicRandTest(){ + ExpressionNode seed = new ConstantLeafNode<>(4); + ExpressionNode randNode = new RandNode(seed); + + BaseDataUpdater adu = new BaseDataUpdater(); + + List> list = randNode.fetchListeners(); + for(DataListener d : list) { + adu.register(d); + } + + Random check = new Random(4); + + for (int i = 0; i < 33550336; i++) { + Double result = randNode.evaluate(); + assertEquals(check.nextDouble(), result, 0.0); + assertTrue(result < 1); + assertTrue(result >= 0); + } + + String expression = randNode.PreFixSyntax(); + System.out.print(expression + "\n"); + String expressionInfix = randNode.ExcelSyntax(); + System.out.print(expressionInfix+ "\n"); + } + +}