Skip to content

Commit

Permalink
Enhanced linear regression and maven updates
Browse files Browse the repository at this point in the history
Added the ability to predict a result from a selected X value
Adding next value to stack is no longer the default - you need to use
the `add` parameter
Updated the following plugins:
  maven-compiler-plugin
  maven-surefire-plugin
  maven-shade-plugin
  exec-maven-plugin
Updated the following dependencies:
  fross libray
  jline-reader
  jline-terminal-jansi

Updated UserGuide for the Linear Regression command
Added enhanced lr tests
  • Loading branch information
frossm committed Jan 29, 2024
1 parent 7dbe120 commit 36f5230
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 38 deletions.
3 changes: 1 addition & 2 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="module" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
Expand Down
2 changes: 1 addition & 1 deletion mdbook/src/Chapters/CalculatorCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ These commands, like the others you'll read about later, are executed by typing
|fact <br>factorial| **FACTORIAL**<br>Takes the factorial of `line1`. The factorial target number must be an integer, so if there is a decimal component, it will be dropped (not rounded) as with the `int` command|
|f <br> flip | **FLIP SIGN**<br>Flip the sign on the top stack item (`line1`). This is effectively multiplying `line1` by `-1`|
|int| **INTEGER**<br>Converts the top stack item (`line1`) to it's integer value. This will discard the decimal portion regardless of it's value. For example: `4.34` will result in `4` and `4.999` will also result in `4`. If rounding is desired, execute the `round` command prior to `int` (or create a user defined function)|
|lr| **SIMPLE LINEAR REGRESSION**<br>[Linear regression](https://www.graphpad.com/guides/the-ultimate-guide-to-linear-regression) is used to model the relationship between two variables and create a line that can be used to estimate other values using a line-of-best-fit method. This implementation is for simple linear regression and will show you the formula, slope, y-intercept as well as add the next expected value to the top of the stack.<br><br>The values will be plotted from the bottom of the stack to the top (`line1`)(which is probably want you want). If you need it the other way around, simply reverse the stack with the `reverse` or `rev` command prior to executing `lr`|
|lr [add]<br>lr [x]| **SIMPLE LINEAR REGRESSION**<br>[Linear regression](https://www.graphpad.com/guides/the-ultimate-guide-to-linear-regression) is used to model the relationship between two variables and create a line that can be used to estimate other values using a line-of-best-fit method. This implementation is for simple linear regression, and will display the formula, slope, y-intercept as well as add the next expected value to the top of the stack.<br><br>If `add` (or just `a`) is entered, the next predicted value will be added to the top of the stack (`line1`). If a number is provided (`x`), the predicted `y` value will be displayed. The `y` value is the result of the linear extrapolation at the `x` value. If both are added, the y value at the x location will be both calculated and added to the top of the stack (`line1`)<br><br>The values will be plotted from the bottom of the stack to the top (`line1`)(which is probably want you want). If you need it the other way around, simply reverse the stack with the `reverse` or `rev` command prior to executing `lr`|
|log|**LOGARITHM BASE e**<br>Calculates the [natural logarithm (base e)](https://en.wikipedia.org/wiki/Natural_logarithm). Please note that these are calculated as a `double` and therefore do not have unlimited precision|
|log10|**LOGARITHM BASE 10**<br>Calculates the [base10 logarithm](https://en.wikipedia.org/wiki/Common_logarithm). Please note that these are calculated as a `double` and therefore do not have unlimited precision|
|max|**MAXIMUM VALUE**<br>Copies the largest value in the top of the stack (`line1`)|
Expand Down
16 changes: 8 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.fross</groupId>
<artifactId>rpncalc</artifactId>
<version>5.1.7</version>
<version>5.1.8</version>
<packaging>jar</packaging>

<name>rpncalc</name>
Expand Down Expand Up @@ -64,7 +64,7 @@
<!-- ========================================================================================== -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.12.1</version>
<configuration>
<release>${maven.compiler.release}</release>
</configuration>
Expand All @@ -77,7 +77,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.5</version>
</plugin>

<!-- ========================================================================================== -->
Expand All @@ -87,7 +87,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
Expand Down Expand Up @@ -182,7 +182,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<version>3.1.1</version>
<executions>
<execution>
<id>chmod</id>
Expand Down Expand Up @@ -240,7 +240,7 @@
<dependency>
<groupId>org.fross</groupId>
<artifactId>library</artifactId>
<version>2023.12.03</version>
<version>2024.01.22</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
Expand All @@ -255,15 +255,15 @@
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-reader</artifactId>
<version>3.25.0</version>
<version>3.25.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.jline/jline-terminal-jansi -->
<!-- Needed to remove jline warning when app starts -->
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline-terminal-jansi</artifactId>
<version>3.25.0</version>
<version>3.25.1</version>
</dependency>

</dependencies>
Expand Down
4 changes: 2 additions & 2 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: rpncalc
version: '5.1.7'
version: '5.1.8'
summary: The command line Reverse Polish Notation (RPN) calculator
description: |
RPNCalc is an easy to use command line based Reverse Polish
Expand Down Expand Up @@ -50,7 +50,7 @@ parts:
plugin: maven
source: https://github.com/frossm/library.git
source-type: git
source-tag: 'v2023.12.03'
source-tag: 'v2024.01.22'
maven-parameters:
- install

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/fross/rpncalc/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu

// Linear Regression
case "lr":
StackCommands.cmdLinearRegression(calcStack);
StackCommands.cmdLinearRegression(calcStack, cmdInputParam);
break;

// Absolute Value
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/fross/rpncalc/Help.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public static void Display() {
Output.printColorln(Ansi.Color.WHITE, " fact Take a factorial of line1. Decimals will be dropped");
Output.printColorln(Ansi.Color.WHITE, " f Flip the sign of the element at line1");
Output.printColorln(Ansi.Color.WHITE, " int Convert line1 to an integer. No rounding is performed");
Output.printColorln(Ansi.Color.WHITE, " lr Simple Linear regression. Calculate the next predicted value");
Output.printColorln(Ansi.Color.WHITE, " lr [add] [x] Linear Regress. 'add' will add next value. x adds the y value at that x");
Output.printColorln(Ansi.Color.WHITE, " log | log10 Calculate the natural (base e) or base10 logarithm");
Output.printColorln(Ansi.Color.WHITE, " min | max Add the minimum or maximum stack value to the stack");
Output.printColorln(Ansi.Color.WHITE, " median [keep] Replace stack with median value. 'keep' will retain stack");
Expand Down
62 changes: 46 additions & 16 deletions src/main/java/org/fross/rpncalc/StackCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,40 @@ public static void cmdInteger(StackObj calcStack) {
*
* @param calcStack
*/
public static void cmdLinearRegression(StackObj calcStack) {
public static void cmdLinearRegression(StackObj calcStack, String args) {
boolean argAdd = false;
BigDecimal argX = new BigDecimal(calcStack.size());

// Ensure we have at least 2 values on the stack
if (calcStack.size() < 2) {
Output.printColorln(Ansi.Color.RED, "Error: There must be at least two items on the stack to calculate a linear regression");
return;
}

// Process the arguments
try {
// If any arguments were supplied
if (!args.isEmpty()) {
// Loop through each option and process it
for (String a : args.split("\\s")) {
// Look for a string that starts with 'a'
if (a.toLowerCase().startsWith("a")) {
argAdd = true;
Output.debugPrintln("Setting LR ADD flag");
continue;
} else {
argX = new BigDecimal(a).subtract(BigDecimal.ONE);
Output.debugPrintln("Setting LR 'x' value to : " + argX.toString());
continue;
}
}
}

} catch (Exception ex) {
Output.printColorln(Ansi.Color.RED, "ERROR: Acceptable linear regression options are 'add' or a number");
return;
}

// X is the number of stack items
BigDecimal n = new BigDecimal(String.valueOf(calcStack.size()));
BigDecimal sumX = BigDecimal.ZERO; // X values are the stack numbers
Expand Down Expand Up @@ -464,31 +491,34 @@ public static void cmdLinearRegression(StackObj calcStack) {
BigDecimal b = b_top.divide(b_bottom, MathContext.DECIMAL128);

// Output details if debug is enabled
Output.debugPrintln("n: " + n.toPlainString());
Output.debugPrintln("sumX: " + sumX.toPlainString());
Output.debugPrintln("sumY: " + sumY.toPlainString());
Output.debugPrintln("sumXY: " + sumXY.toPlainString());
Output.debugPrintln("sumX2: " + sumX2.toPlainString());
Output.debugPrintln("sumY2: " + sumY2.toPlainString());
Output.debugPrintln("a: " + a.toPlainString());
Output.debugPrintln("b: " + b.toPlainString());
Output.debugPrintln("n: " + n.toPlainString());
Output.debugPrintln("sumX: " + sumX.toPlainString());
Output.debugPrintln("sumY: " + sumY.toPlainString());
Output.debugPrintln("sumXY: " + sumXY.toPlainString());
Output.debugPrintln("sumX2: " + sumX2.toPlainString());
Output.debugPrintln("sumY2: " + sumY2.toPlainString());
Output.debugPrintln("a: " + a.toPlainString());
Output.debugPrintln("b: " + b.toPlainString());
Output.debugPrintln("NextValue: " + argX.toPlainString() + " (one less than displayed)");

// Rounded values are just for the display
BigDecimal nextValue = a.add(b.multiply(n.add(BigDecimal.ONE)));
BigDecimal nextValue = a.add(b.multiply(argX.add(BigDecimal.ONE)));
BigDecimal aRounded = a.setScale(4, RoundingMode.HALF_UP);
BigDecimal bRounded = b.setScale(4, RoundingMode.HALF_UP);
BigDecimal nextValueRounded = nextValue.setScale(4, RoundingMode.HALF_UP);

// Display the LR formula
Output.printColorln(Ansi.Color.CYAN, "Slope Equation: y = " + bRounded + "x + " + aRounded);
Output.printColorln(Ansi.Color.CYAN, "Slope: " + bRounded + " Y-Intercept: " + aRounded);
Output.printColorln(Ansi.Color.CYAN, "Predicted next value (" + nextValueRounded + ") added to the top of the stack");

// Save current calcStack to the undoStack
calcStack.saveUndo();
Output.printColorln(Ansi.Color.CYAN, "Predicted value at x = " + argX.add(BigDecimal.ONE) + ": " + nextValueRounded);

// Add the next predicted value to the stack
calcStack.push(nextValue);
// Add the next predicted value to the stack if 'add' was selected
if (argAdd) {
// Save current calcStack to the undoStack
calcStack.saveUndo();

calcStack.push(nextValue);
}

}

Expand Down
49 changes: 42 additions & 7 deletions src/test/java/org/fross/rpncalc/StackCommandsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ void testCmdInteger() {

/**
* Testing the linear regression results
*
* Reference for testing: https://www.socscistatistics.com/tests/regression/default.aspx
*/
@Test
void testCmdLinearRegression() {
Expand All @@ -584,7 +584,7 @@ void testCmdLinearRegression() {
stk.push(20.00);
stk.push(10.00);
stk.push(35.00);
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "4");
assertEquals(8, stk.size());
assertEquals(27.7143, stk.pop().doubleValue());
Expand All @@ -601,7 +601,7 @@ void testCmdLinearRegression() {
stk.push(11.11);
stk.push(22.22);
stk.push(3.0);
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "6");
assertEquals(11, stk.size());
assertEquals(16.671333, stk.pop().doubleValue());
Expand All @@ -618,7 +618,7 @@ void testCmdLinearRegression() {
stk.push(-34.88);
stk.push(40.99);
stk.push(3.00);
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "6");
assertEquals(11, stk.size());
assertEquals(4.271333, stk.pop().doubleValue());
Expand All @@ -629,7 +629,7 @@ void testCmdLinearRegression() {
for (int i = 0; i < testValues.length; i++) {
stk.push(testValues[i]);
}
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "7");
assertEquals(48.9842105, stk.pop().doubleValue());

Expand All @@ -639,7 +639,7 @@ void testCmdLinearRegression() {
for (int i = 0; i < testValues1.length; i++) {
stk.push(testValues1[i]);
}
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "9");
assertEquals(17, stk.size());
assertEquals(-9.101980055, stk.pop().doubleValue());
Expand All @@ -652,10 +652,45 @@ void testCmdLinearRegression() {
stk.push(testValues2[i]);
}
assertEquals(12, stk.size());
StackCommands.cmdLinearRegression(stk);
StackCommands.cmdLinearRegression(stk, "a");
StackCommands.cmdRound(stk, "9");
assertEquals("47498562223075895424.242424242", stk.peek().toEngineeringString());
assertEquals(13, stk.size());

// Test #7 - Custom X values
stk.clear();
Double[] testValues3 = { 29.0, 41.0, 8.0, 18.0, 22.0, 99.0, 32.0, 15.0, 31.0, 3.0, 72.0, 12.0, 60.0, 32.0, 54.0, 45.0, 34.0, 76.0, 5.0, 67.0 };
for (int i = 0; i < testValues3.length; i++) {
stk.push(testValues3[i]);
}
StackCommands.cmdLinearRegression(stk, "30 a");
StackCommands.cmdRound(stk, "4");
assertEquals(58.6135, stk.pop().doubleValue());
assertEquals(20, stk.size());

// Test #8 - Custom X values
stk.clear();
Double[] testValues4 = { 19.0, 41.0, 8.0, 18.0, 12.0, -99.0, 32.0, 15.0, 31.0, 3.0, 72.0, 12.0, 60.0, 32.0, 54.0, 45.0, 34.0, 76.0, 5.0, 67.0 };
for (int i = 0; i < testValues4.length; i++) {
stk.push(testValues4[i]);
}
StackCommands.cmdLinearRegression(stk, "22 a");
StackCommands.cmdRound(stk, "4");
assertEquals(57.1564, stk.pop().doubleValue());
assertEquals(20, stk.size());

// Test #9 - Custom X values
stk.clear();
Double[] testValues5 = { 29.0, 41.0, 8.0, 18.0, 22.0, -99.0, 32.0, 15.0, 31.0, 3.0, 72.0, 12.0, -60.0, 32.0, 54.0, 45.0, 14.0, 76.0, 5.0, 44.0 };
for (int i = 0; i < testValues5.length; i++) {
stk.push(testValues5[i]);
}
StackCommands.cmdLinearRegression(stk, "45 a");
StackCommands.cmdRound(stk, "4");
assertEquals(69.1932, stk.pop().doubleValue());
assertEquals(20, stk.size());


}

/**
Expand Down

0 comments on commit 36f5230

Please sign in to comment.