Skip to content

Commit

Permalink
Added sort, median, and kg/lbs conversion
Browse files Browse the repository at this point in the history
- Added ability to convert from kg to/from lbs
- Updates to UserGuide to support these changes
- Correct bug in import.  Working now.
- `roll` can not be used in addition to the `dice` command
- Stacks can be sorted in ascending or descending order
- Median can be calculated with even and odd numbers of stack items
- Added testing for new commands
- Updated help page
- Updated graphics to support new screenshots. Had to break this into 2
  • Loading branch information
frossm committed May 11, 2023
1 parent b8291ea commit 1c0030d
Show file tree
Hide file tree
Showing 14 changed files with 361 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
*.md.backup
release.properties
dependency-reduced-pom.xml
buildNumber.properties
Expand Down
Binary file modified graphics/ScreenShot2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 7 additions & 5 deletions mdbook/src/Chapters/CalculatorCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,30 @@ In Calculator Commands, these are the basic commands that operations on the numb

These commands, like the others you'll read about later, are executed by typing the command name, and any needed arguments into RPNCalc. Often there are several different names and abreviations for the same command. This is just to make it easier to remember. Brackets, `[]` around an option denotes it is optional.

|<div style="width:90px">Command</div>|Description|
|<div style="width:110px">Command</div>|Description|
|-------|-----------|
|%| **PERCENT**<br>Assumes `line1` contains a percent. This converts that into a number by simply dividing that value by 100. For example, if you want to take 50.123% of a number, you could just enter in `50.123 [ENTER] % [ENTER] *`|
|aa [keep]| **ADD ALL**<br>Add all stack items together and replace the numbers on the stack with the result. If the optional `keep` parameter is sent, the elements added will be retained on the stack and the total will be added to the top of the stack. The entire `keep` command is not necessary, anything that starts with `k` will work|
|abs| **ABSOLUTE VALUE**<br>Takes the [absolute value](https://en.wikipedia.org/wiki/Absolute_value#:~:text=In%20mathematics%2C%20the%20absolute%20value,and%20%7C0%7C%20%3D%200) of line 1. The returns the positive value of the number. Not the most useful command on it's own as you can always `f` flip the sign. However it could be useful as part of a User Defined Function|
|avg [keep]|**AVERAGE**<br>Calculate the average of all of the numbers on the stack. The stack will be replaced with the result. If `keep` is provided, the stack will be retained and the average will be added on top. `average` or `mean` can also be used|
|avg [keep]|**AVERAGE**<br>Calculate the [average](https://en.wikipedia.org/wiki/Average) of all of the numbers on the stack. The stack will be replaced with the result. If `keep` is provided, the stack will be retained and the average will be added on top. `average` or `mean` can also be used|
|c <br> clear| **CLEAR**<br>Clear the current stack and the screen. Memory data is retained and you can undo the clear with the undo `u` command|
|cl <br> clean| **CLEAN SCREEN**<br>Clear the current screen, but keep the stack. After cleaning, the stack will be displayed at the top of the screen. Used to remove the clutter|
|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| **DICE ROLL**<br>Roll a Y sided die X times and add the results to the stack. Default is 1d6. While not a normal calculator function, it's fun!|
|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)|
|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|
|log|**LOGARITHM BASE e**<br>Calculates the [natural logarithm (base e)](https://en.wikipedia.org/wiki/Natural_logarithm)|
|log10|**LOGARITHM BASE 10**<br>Calculates the [base10 logarithm](https://en.wikipedia.org/wiki/Common_logarithm)|
|Min|**MINIMUM VALUE**<br>Copies the small value in the current stack to `line1`|
|Max|**MAXIMUM VALUE**<br>Copies the largest value in the current stack to `line1`|
|max|**MAXIMUM VALUE**<br>Copies the largest value in the current stack to `line1`|
|median [keep]|**MEDIAN**<br>Replaces the current stack with the [median](https://en.wikipedia.org/wiki/Median) value. For an odd number of stack items, the "middle" value will be used. With an even number of items there is no "middle" value so the two center most values will be averaged. If the `keep` flag is used, the median value will be added on top of the stack retaining the current stack values|
|min|**MINIMUM VALUE**<br>Copies the small value in the current stack to `line1`|
|mod| **MODULUS**<br>[Modulus](en.wikipedia.org/wiki/Modular_arithmetic) is the remainder after a division. This command will perform a division of the top two stack items and return the remainder only back to the stack|
|rand [low] \[high] | **RANDOM NUMBER GENERATION**<br>Generate a random integer number between the provided `[l]ow` and `[h]igh` numbers inclusive to both. If no numbers are provided, then the random number will be between 1 and 100 inclusive|
|round [n]| **ROUND**<br>Round `line1` to `[n]` decimal places. If `[n]` is not given, round to the nearest integer (zero decimal places). **Example 1:** `3.14159` `round` would round to `3`. **Example 2:** `3.14159` `round 4` would round to `3.1416`|
|s [#] \[#] <br> swap [#] \[#]| **SWAP LINES**<br>Without an argument, swap the top two stack items (`line1 & line2`). You can swap any two line numbers in your stack by providing the two line numbers|
|sd [keep]|**STANDARD DEVIATION**<br>Calculate the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of the items in the stack. The stack items will be replaced by the result. If `keep` is provided, the numbers on the stack will not be removed and the standard deviation will simply be added to the top of the stack on `line1`|
|sort a\|d|**SORT**<br>Sort the stack in an ascending or descending way. An `a` or `d` is required. You can provide `ascending` or `descending` if you like, but only the first letter is looked at (just like `keep` in other commands. Note that this might look reversed at first since the top of the stack is `line1` which is the bottom of the display. If you get it wrong just `undo` and sort the other way|
|sqrt| **SQUARE ROOT**<br>Perform a [square root](https://en.wikipedia.org/wiki/Square_root) of the top stack item (`line1`)|
|u [#]<br> undo [#]| **UNDO**<br>By default, undo the last operation. However, if an undo stack line number is given, as displayed with the `list undo` command, undo will restore the stack back to that point. Please keep in mind that if you restore back to a previous undo state, later undo states will be discarded. You can't go back. Typically, however, `u` undo is used to simply undo the previous action|
3 changes: 2 additions & 1 deletion mdbook/src/Chapters/Introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ From within the program, entering `h`, `help` or `?` will show the program help

The current RPNCalc help screen:<br>

<img align="center" width="100%" src="https://github.com/frossm/rpncalc/raw/master/graphics/ScreenShot.jpg">
<img align="center" width="100%" src="https://github.com/frossm/rpncalc/raw/master/graphics/ScreenShot1.jpg">
<img align="center" width="100%" src="https://github.com/frossm/rpncalc/raw/master/graphics/ScreenShot2.jpg">
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>4.7.8</version>
<version>4.8.0</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: '4.7.8'
version: '4.8.0'
summary: The command line Reverse Polish Notation (RPN) calculator
description: |
RPNCalc is an easy to use command line based Reverse Polish
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/fross/rpncalc/CommandParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu
StackCommands.cmdAverage(calcStack, cmdInputParam);
break;

// Sort
case "sort":
StackCommands.cmdSort(calcStack, cmdInputParam);
break;

// Median
case "median":
StackCommands.cmdMedian(calcStack, cmdInputParam);
break;

// Standard Deviation
case "sd":
StackCommands.cmdStdDeviation(calcStack, cmdInputParam);
Expand Down Expand Up @@ -173,6 +183,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu

// Dice
case "dice":
case "roll":
StackCommands.cmdDice(calcStack, cmdInputParam);
break;

Expand Down
7 changes: 5 additions & 2 deletions src/main/java/org/fross/rpncalc/Help.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ public class Help {
public static void Display() {
int helpWidth = 80;

// Display help header
Output.printColor(Ansi.Color.CYAN, "\n+" + "-".repeat(helpWidth) + "+\n+");
Output.printColor(Ansi.Color.WHITE, Format.CenterText(helpWidth, ("RPN Calculator v" + Main.VERSION)));
Output.printColor(Ansi.Color.WHITE, Format.CenterText(helpWidth, ("RPN Calculator v" + Main.VERSION)));
Output.printColor(Ansi.Color.CYAN, "+\n+");
Output.printColor(Ansi.Color.WHITE, Format.CenterText(helpWidth, Main.COPYRIGHT));
Output.printColorln(Ansi.Color.CYAN, "+\n+" + "-".repeat(helpWidth) + "+");
Expand All @@ -71,7 +72,7 @@ public static void Display() {
Output.printColorln(Ansi.Color.YELLOW, "\nCalculator Commands:");
Output.printColorln(Ansi.Color.WHITE, " aa [keep] Add all stack items. Adding 'keep' will retain existing items");
Output.printColorln(Ansi.Color.WHITE, " abs Take the absolute value of line1");
Output.printColorln(Ansi.Color.WHITE, " avg [keep] Replace stack items with average of values. 'keep' will retain items");
Output.printColorln(Ansi.Color.WHITE, " avg [keep] Replace stack items with average of values. 'keep' will retain stack");
Output.printColorln(Ansi.Color.WHITE, " c Clear the screen and empty current stack");
Output.printColorln(Ansi.Color.WHITE, " cl[ean] Clear screen and keep the stack values");
Output.printColorln(Ansi.Color.WHITE, " copy [#] Copy line1 or the provided line number and add it to the stack");
Expand All @@ -82,7 +83,9 @@ public static void Display() {
Output.printColorln(Ansi.Color.WHITE, " lr Simple Linear regression. Calculate the next predicted value");
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");
Output.printColorln(Ansi.Color.WHITE, " mod Modulus. Perform a division and return the remainder");
Output.printColorln(Ansi.Color.WHITE, " sort a|d Sorts the stack in an ascending or descending manner");
Output.printColorln(Ansi.Color.WHITE, " sqrt Perform a square root of the line1 value");
Output.printColorln(Ansi.Color.WHITE, " rand [L] [H] Random integer between L and H inclusive. Default is 1-100");
Output.printColorln(Ansi.Color.WHITE, " round [n] Round to n decimal places. Default is 0 decimals");
Expand Down
41 changes: 39 additions & 2 deletions src/main/java/org/fross/rpncalc/Math.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public static boolean isNumeric(String str) {
* @param stk
* @return
*/
public static Double Mean(StackObj stk) {
public static Double mean(StackObj stk) {
Double totalCounter = 0.0;
int size = stk.size();

Expand Down Expand Up @@ -215,7 +215,44 @@ public static Double mean(Double[] arry) {
stk.push(arry[i]);
}

return (Mean(stk));
return (mean(stk));
}

/**
* median(): Return the median value of the stack items
*/
public static Double median(StackObj stk) {
Double result = 0.0;

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

// sort the stack
stk.sort("descending");

try {
if (stk.size() % 2 == 0) {
// Even number of items
int upperIndex = Integer.valueOf(stk.size() / 2);
int lowerIndex = Integer.valueOf(stk.size() / 2 + 1);

Output.debugPrint("Median: LowerIndex=" + lowerIndex + " | UpperIndex=" + upperIndex);

result = (stk.get(upperIndex - 1) + stk.get(lowerIndex - 1)) / 2;

} else {
// Odd number of items
result = stk.get(stk.size() / 2);
}
} catch (Exception ex) {
Output.printColorln(Ansi.Color.RED, "ERROR: Could not calculate the median");
return 0.0;
}

// Undo the sort to get back to the original stack order
StackCommands.cmdUndo(stk, String.valueOf(stk.undoSize()));

return result;
}

/**
Expand Down
71 changes: 69 additions & 2 deletions src/main/java/org/fross/rpncalc/StackCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static void cmdAverage(StackObj calcStack, String arg) {
}

// Calculate the mean
Double mean = Math.Mean(calcStack);
Double mean = Math.mean(calcStack);

// If we are not going to keep the stack (the default) clear it
if (keepFlag == false)
Expand Down Expand Up @@ -500,6 +500,42 @@ public static boolean cmdMaximum(StackObj calcStack) {
return true;
}

/**
* cmdMedian(): Replace the stack with the median value. If `keep` is specified, retain the stack
*/
public static void cmdMedian(StackObj calcStack, String arg) {
// Ensure we have enough numbers on the stack
if (calcStack.size() < 2) {
Output.printColorln(Ansi.Color.RED, "ERROR: Median requires at least two items on the stack");
return;
}

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

// Determine if we should keep or clear the stack
boolean keepFlag = false;
try {
// Just check if the provided command starts with 'k'. That should be enough
if (arg.toLowerCase().charAt(0) == 'k') {
keepFlag = true;
}
} catch (StringIndexOutOfBoundsException ex) {
keepFlag = false;
}

// Calculate the median
Double median = Math.median(calcStack);

// If we are not going to keep the stack (the default) clear it
if (keepFlag == false)
calcStack.clear();

// Add the average to the stack
calcStack.push(median);

}

/**
* cmdMinimum(): Add minimum value in the stack to the top of the stack
*/
Expand Down Expand Up @@ -673,6 +709,37 @@ public static void cmdRound(StackObj calcStack, String arg) {
calcStack.push(bd.doubleValue());
}

/**
* cmdSort(): Sort the stack in ascending or descending order
*
* @param param
*/
public static void cmdSort(StackObj calcStack, String param) {
// Ensure we have enough numbers on the stack
if (calcStack.size() < 2) {
Output.printColorln(Ansi.Color.RED, "ERROR: Sort requires at least two items on the stack");
return;
}

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

// Determine if we are sorting in ascending or descender mode
try {
if (param.toLowerCase().charAt(0) == 'a') {
calcStack.sort("ascending");
} else if (param.toLowerCase().charAt(0) == 'd') {
calcStack.sort("descending");
} else {
throw new IllegalArgumentException();
}
} catch (Exception ex) {
Output.printColorln(Ansi.Color.RED, "ERROR: Sort requires an (a)scending or (d)escending argument");
return;
}

}

/**
* cmdSwapElements(): Swap the provided elements within the stack
*
Expand Down Expand Up @@ -764,7 +831,7 @@ public static void cmdStdDeviation(StackObj calcStack, String arg) {
}

// Step1: Get the mean
Double mean1 = Math.Mean(calcStack);
Double mean1 = Math.mean(calcStack);
Output.debugPrint("Initial mean of the numbers: " + mean1);

// Step2: For each number: subtract the mean from the number and square the result
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/org/fross/rpncalc/StackObj.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,47 @@ public int size() {
return calcStack.size();
}

/**
* sort(): Sort the stack. Requires the parameter "ascending" or "descending"
*
*/
public void sort(String mode) {
if (!mode.equalsIgnoreCase("ascending") && !mode.equalsIgnoreCase("descending")) {
Output.printColorln(Ansi.Color.RED, "ERROR: sort requires a 'ascending' or 'descending' parameter");
return;
}

StackObj sortedStack = new StackObj();

while (!calcStack.isEmpty()) {
// Pull out a value from the stack
double tmpValue = calcStack.pop();

// While temporary stack is not empty and top of stack is greater than the tempValue
while (!sortedStack.isEmpty() && sortedStack.peek() > tmpValue) {
calcStack.push(sortedStack.pop());
}

sortedStack.push(tmpValue);
}

// Clear the calcStack and replace the values with the sorted stack
calcStack.clear();

if (mode.equalsIgnoreCase("ascending")) {
// Ascending
for (int i = sortedStack.size() - 1; i >= 0; i--) {
calcStack.push(sortedStack.get(i));
}
} else {
// Descending
for (int i = 0; i < sortedStack.size(); i++) {
calcStack.push(sortedStack.get(i));
}
}

}

/**
* undoGet(): Return the element of the undo stack at the index provided
*
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/org/fross/rpncalc/StackOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,20 @@ public static void cmdSwapStack(StackObj calcStack, StackObj calcStack2) {
public static void importStackFromDisk(StackObj calcStack, String arg) {
String fileName = arg.trim();

Output.debugPrint("Import filename as entered: " + fileName);

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

try {
// Verify the filename provided is a file and can be read
if (new File(fileName).canRead() && new File(fileName).isFile()) {
// Read lines from the file into the ArrayList
ArrayList<String> linesRead = new ArrayList<>(Files.readAllLines(Paths.get(fileName)));

// Clear the stack before importing the new values
calcStack.clear();

// Convert the strings to double values
for (int i = 0; i < linesRead.size(); i++) {
calcStack.push(Double.parseDouble(linesRead.get(i)));
Expand All @@ -215,10 +223,6 @@ public static void importStackFromDisk(StackObj calcStack, String arg) {
"The data in '" + fileName + "' can't be read as it is not in the correct format.\nThe import file format is simply one number per line");
}

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

calcStack.clear();
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/fross/rpncalc/MathTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ void testIsNumeric() {
}

/**
* Test method for {@link org.fross.rpncalc.Math#Mean(org.fross.rpncalc.StackObj)}.
* Test method for {@link org.fross.rpncalc.Math#mean(org.fross.rpncalc.StackObj)}.
*/
@Test
void testMeanIfStackObjIsProvided() {
Expand All @@ -195,7 +195,7 @@ void testMeanIfStackObjIsProvided() {
stk.push(12.1354);
stk.push(-1.23);

assertEquals(5.38837, Math.Mean(stk));
assertEquals(5.38837, Math.mean(stk));
}

/**
Expand Down
Loading

0 comments on commit 1c0030d

Please sign in to comment.