Skip to content

Commit

Permalink
Added fact / factorial command
Browse files Browse the repository at this point in the history
Also updated help, manual, and unit tests
  • Loading branch information
frossm committed May 29, 2023
1 parent 27b67a1 commit 8a0561b
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 37 deletions.
1 change: 1 addition & 0 deletions mdbook/src/Chapters/CalculatorCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ These commands, like the others you'll read about later, are executed by typing
|copy [#] <br> dup [#]|**COPY**<br>With no number provided, copy will duplicate the top stack item / `line1` and place it on the stack. If the optional line number is given, the value at the line will be duplicated to the top of the stack|
|d<br>d [#]<br>d [#-#]| **DELETE**<br>If `d` is given without any parameters, it will delete `line1`. If a line number is given after the `d`, that line number will be deleted. A range can be given as well, such as `d 1-3` and RPNCalc will delete those lines and everything in between. `del` can also be used|
|dice XdY<br>roll XdY| **DICE ROLL**<br>Roll a Y sided die X times and add the results to the stack. Default is 1d6 which roll a six sided dice one time. This is a more specific version of `random`. While not a normal calculator function, it's fun! (Especially if you're an old school gamer)|
|fact <br>factorial| **FACTORIAL**<br>Takes the factorial of the target item (`line1`). Note 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`. `4.999` will also result in `4`. If rounding is desired, execute the `round` command prior to `int`|
|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|
Expand Down
2 changes: 1 addition & 1 deletion 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.0.2</version>
<version>5.0.3</version>
<packaging>jar</packaging>

<name>rpncalc</name>
Expand Down
2 changes: 1 addition & 1 deletion snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: rpncalc
version: '5.0.2'
version: '5.0.3'
summary: The command line Reverse Polish Notation (RPN) calculator
description: |
RPNCalc is an easy to use command line based Reverse Polish
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/org/fross/rpncalc/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
switch (cmdInputCmd) {

/*******************************************************************************
* Stack Commands
* Stack Calculator Commands
******************************************************************************/
// Undo
case "undo":
Expand Down Expand Up @@ -80,6 +80,12 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
StackCommands.cmdDelete(calcStack, cmdInputParam);
break;

// Factorial
case "fact":
case "factorial":
StackCommands.cmdFactorial(calcStack);
break;

// Percent
case "%":
StackCommands.cmdPercent(calcStack);
Expand Down Expand Up @@ -425,7 +431,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
// Check for a fraction. If number entered contains a '/' but it's not at the end, then it must be a fraction.
} else if (cmdInput.contains("/") && !cmdInput.substring(cmdInput.length() - 1).matches("/")) {
Output.debugPrint("Fraction has been entered");

try {
BigDecimal fracInteger = BigDecimal.ZERO;
BigDecimal fracDecimalEquiv = BigDecimal.ZERO;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/fross/rpncalc/Help.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public static void Display() {
Output.printColorln(Ansi.Color.WHITE, " copy [#] Copy line1 or the provided line number and add it to the stack");
Output.printColorln(Ansi.Color.WHITE, " d [#] [#-#] Delete line1, the line number provided, or a range of lines provided");
Output.printColorln(Ansi.Color.WHITE, " dice XdY Roll a Y sided die X times. Default is 1d6");
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");
Expand Down
32 changes: 16 additions & 16 deletions src/main/java/org/fross/rpncalc/Math.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,22 @@ public static StackObj Power(StackObj stk) {
return stk;
}

/**
* factorial(): Return the factorial of the provided integer
*
* @param num
* @return
*/
public static BigDecimal Factorial(Long num) {
BigDecimal result = BigDecimal.ONE;

for (long factor = 2; factor <= num; factor++) {
result = result.multiply(new BigDecimal(String.valueOf(factor)), MathContext.UNLIMITED);
}

return result;
}

/**
* GreatestCommonDivisor(): Return the largest common number divisible into both numbers. Used in rpncalc for fraction
* reduction.
Expand Down Expand Up @@ -300,20 +316,4 @@ public static BigDecimal median(StackObj stk) {
return result;
}

/**
* factorial(): Return the factorial of the provided integer
*
* @param num
* @return
*/
public static BigDecimal factorial(int num) {
BigDecimal result = BigDecimal.ONE;

for (int factor = 2; factor <= num; factor++) {
result = result.multiply(new BigDecimal(factor), MathContext.UNLIMITED);
}

return result;
}

}
24 changes: 24 additions & 0 deletions src/main/java/org/fross/rpncalc/StackCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,31 @@ public static void cmdDice(StackObj calcStack, String param) {
long randomNum = new java.util.Random().nextInt(die) + 1;
calcStack.push(new BigDecimal(String.valueOf(randomNum)));
}
}

/**
* cmdFactorial(): Take the factorial of the top of stack item dropping decimals if present
*
* @param calcStack
*/
public static void cmdFactorial(StackObj calcStack) {
// Ensure we have an item on the stack
if (calcStack.size() < 1) {
Output.printColorln(Ansi.Color.RED, "Error: There must be at least one item on the stack to perform a factorial");
return;
}

// Ensure the provided number is not zero or negative
if (calcStack.peek().compareTo(BigDecimal.ZERO) < 1) {
Output.printColorln(Ansi.Color.RED, "ERROR: Factorial requires a number greater than zero");
return;
}

// Save current calcStack to the undoStack
calcStack.saveUndo();

BigDecimal result = Math.Factorial(calcStack.pop().toBigInteger().longValue());
calcStack.push(result);
}

/**
Expand Down
34 changes: 17 additions & 17 deletions src/test/java/org/fross/rpncalc/MathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -363,23 +363,23 @@ void testMedian() {
*/
@Test
void testFactorial() {
assertEquals(new BigDecimal("24"), Math.factorial(4));
assertEquals(new BigDecimal("120"), Math.factorial(5));
assertEquals(new BigDecimal("720"), Math.factorial(6));
assertEquals(new BigDecimal("5040"), Math.factorial(7));
assertEquals(new BigDecimal("40320"), Math.factorial(8));
assertEquals(new BigDecimal("362880"), Math.factorial(9));
assertEquals(new BigDecimal("3628800"), Math.factorial(10));
assertEquals(new BigDecimal("39916800"), Math.factorial(11));
assertEquals(new BigDecimal("479001600"), Math.factorial(12));
assertEquals(new BigDecimal("6227020800"), Math.factorial(13));
assertEquals(new BigDecimal("87178291200"), Math.factorial(14));
assertEquals(new BigDecimal("1307674368000"), Math.factorial(15));
assertEquals(new BigDecimal("20922789888000"), Math.factorial(16));
assertEquals(new BigDecimal("355687428096000"), Math.factorial(17));
assertEquals(new BigDecimal("6402373705728000"), Math.factorial(18));
assertEquals(new BigDecimal("121645100408832000"), Math.factorial(19));
assertEquals(new BigDecimal("2432902008176640000"), Math.factorial(20));
assertEquals(new BigDecimal("24"), Math.Factorial(4L));
assertEquals(new BigDecimal("120"), Math.Factorial(5L));
assertEquals(new BigDecimal("720"), Math.Factorial(6L));
assertEquals(new BigDecimal("5040"), Math.Factorial(7L));
assertEquals(new BigDecimal("40320"), Math.Factorial(8L));
assertEquals(new BigDecimal("362880"), Math.Factorial(9L));
assertEquals(new BigDecimal("3628800"), Math.Factorial(10L));
assertEquals(new BigDecimal("39916800"), Math.Factorial(11L));
assertEquals(new BigDecimal("479001600"), Math.Factorial(12L));
assertEquals(new BigDecimal("6227020800"), Math.Factorial(13L));
assertEquals(new BigDecimal("87178291200"), Math.Factorial(14L));
assertEquals(new BigDecimal("1307674368000"), Math.Factorial(15L));
assertEquals(new BigDecimal("20922789888000"), Math.Factorial(16L));
assertEquals(new BigDecimal("355687428096000"), Math.Factorial(17L));
assertEquals(new BigDecimal("6402373705728000"), Math.Factorial(18L));
assertEquals(new BigDecimal("121645100408832000"), Math.Factorial(19L));
assertEquals(new BigDecimal("2432902008176640000"), Math.Factorial(20L));
}

}
51 changes: 51 additions & 0 deletions src/test/java/org/fross/rpncalc/StackCommandsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,57 @@ void testCmdCopy() {
assertEquals("123E+42", stk.get(stk.size() - 1).toEngineeringString());
assertEquals(1.23, stk.get(0).doubleValue());
}

/**
* Factorial test. MathTest takes care of most of this, just make sure
* it can be called with StackCommands.cmdFactorial()
*/
@Test
void testFactorial() {
StackObj stk = new StackObj();

// Test #1
stk.push(12);
assertEquals(1, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("479001600", stk.pop().toEngineeringString());
assertEquals(0, stk.size());

// Test #2
stk.push(12.123);
assertEquals(1, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("479001600", stk.pop().toEngineeringString());
assertEquals(0, stk.size());

// Test #3
stk.push(12.999);
assertEquals(1, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("479001600", stk.pop().toEngineeringString());
assertEquals(0, stk.size());

// Test #4
stk.push(13.0000001);
assertEquals(1, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("6227020800", stk.pop().toEngineeringString());
assertEquals(0, stk.size());

// Test #5
stk.push(-6.1);
assertEquals(1, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("-6.1", stk.peek().toEngineeringString());
assertEquals(1, stk.size());

// Test #6
stk.push(0);
assertEquals(2, stk.size());
StackCommands.cmdFactorial(stk);
assertEquals("0", stk.peek().toEngineeringString());
assertEquals(2, stk.size());
}

/**
* Test delete command no parameter. Should delete line1
Expand Down

0 comments on commit 8a0561b

Please sign in to comment.