From 1c0030d66ef999660adc6476a8145cc3b4f0b22c Mon Sep 17 00:00:00 2001 From: Michael Fross Date: Thu, 11 May 2023 09:36:22 -0500 Subject: [PATCH] Added sort, median, and kg/lbs conversion - 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 --- .gitignore | 1 + graphics/ScreenShot2.jpg | Bin 278494 -> 272985 bytes mdbook/src/Chapters/CalculatorCommands.md | 12 +- mdbook/src/Chapters/Introduction.md | 3 +- pom.xml | 2 +- snap/snapcraft.yaml | 2 +- .../java/org/fross/rpncalc/CommandParser.java | 11 ++ src/main/java/org/fross/rpncalc/Help.java | 7 +- src/main/java/org/fross/rpncalc/Math.java | 41 +++- .../java/org/fross/rpncalc/StackCommands.java | 71 ++++++- src/main/java/org/fross/rpncalc/StackObj.java | 41 ++++ .../org/fross/rpncalc/StackOperations.java | 12 +- src/test/java/org/fross/rpncalc/MathTest.java | 4 +- .../org/fross/rpncalc/StackCommandsTest.java | 177 +++++++++++++++++- 14 files changed, 361 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 92ece5c..cbc0df8 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/graphics/ScreenShot2.jpg b/graphics/ScreenShot2.jpg index 485cdefef30dca297d30725ef3fe4821f473ab31..2c7057083a911d0ab7ec85146dfa493d32ab03cb 100644 GIT binary patch delta 267 zcmccjU*P5yfeoR|Ow3<4hch!fGO|y8?q|+8VY9sdp+KfT#*@D-uwb4rY13xIg>#u0 zFKs@*c(NE{=jP;HjKWM`Oed$GwP&8NaM9+KXRk0bp4?n_QAv^U_vVG44hk`SF`1sq z${4~pp?w!C4YKW;Ok61n&a(jZ G-2?zKq+K8Y delta 589 zcmcb4Md03lfeoR|Osso0hch!fPF~==i)8$PUxVp{!Q_YhdTh4A+@ES1fJ|Lx##fb- zCpz~q+E0#lF=srmd9ur)K+(UIGoH79zOnygU;XQ!?{5Ez_|Nb>e{%RV3+73i4s4z@ zZ7vhzq|F91CyOy=Za%V}QJDGXiTcUM57{#>Ty$VF=iw{Nj5{~qJEo+__-ixci-STy z>G;V|H)!u^m>tDQ-YV7}%|3AW@C&0+S#KgqN!pO|R%m@Sw zjDm^`hK_-W0)>qe7b+NRbO<>3A>rb~j|B|}w#N%H_0N_6&k&a}#jyR)7oG<7JjOd$ z6TCkDXRwQKD!Kok;oN_Q?H|`Mb%;-YaFl5#HHm=rOk9kd(+xK>=`qcN(2PyfHy&l` znO=2_NuTk+_EpE2-nhv8?aV3v&+vUa$a8`a&oMJIvx5A_IDHK_vlJ81BgeU!HT^CB zcDg+W`lh-b5)h0G%s};wOf2kR#f*XsilBf1hJ%1Xz(NIwgpCI;et-moVl=bKd7ZzN t^V2M*SC#w#hR9TH(**gvT$aw|Yb`%doj?88k5f6jR;bp+gTv(iO#sn~2EPCR diff --git a/mdbook/src/Chapters/CalculatorCommands.md b/mdbook/src/Chapters/CalculatorCommands.md index efc76df..ac3cff6 100644 --- a/mdbook/src/Chapters/CalculatorCommands.md +++ b/mdbook/src/Chapters/CalculatorCommands.md @@ -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. -|
Command
|Description| +|
Command
|Description| |-------|-----------| |%| **PERCENT**
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**
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**
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**
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**
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
clear| **CLEAR**
Clear the current stack and the screen. Memory data is retained and you can undo the clear with the undo `u` command| |cl
clean| **CLEAN SCREEN**
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 [#]
dup [#]|**COPY**
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
d [#]
d [#-#]| **DELETE**
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**
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
roll XdY| **DICE ROLL**
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
flip | **FLIP SIGN**
Flip the sign on the top stack item (`line1`). This is effectively multiplying `line1` by -1| |int| **INTEGER**
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**
[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.

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**
Calculates the [natural logarithm (base e)](https://en.wikipedia.org/wiki/Natural_logarithm)| |log10|**LOGARITHM BASE 10**
Calculates the [base10 logarithm](https://en.wikipedia.org/wiki/Common_logarithm)| -|Min|**MINIMUM VALUE**
Copies the small value in the current stack to `line1`| -|Max|**MAXIMUM VALUE**
Copies the largest value in the current stack to `line1`| +|max|**MAXIMUM VALUE**
Copies the largest value in the current stack to `line1`| +|median [keep]|**MEDIAN**
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**
Copies the small value in the current stack to `line1`| |mod| **MODULUS**
[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**
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**
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 [#] \[#]
swap [#] \[#]| **SWAP LINES**
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**
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**
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**
Perform a [square root](https://en.wikipedia.org/wiki/Square_root) of the top stack item (`line1`)| |u [#]
undo [#]| **UNDO**
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| diff --git a/mdbook/src/Chapters/Introduction.md b/mdbook/src/Chapters/Introduction.md index 1c004c2..e483a17 100644 --- a/mdbook/src/Chapters/Introduction.md +++ b/mdbook/src/Chapters/Introduction.md @@ -39,4 +39,5 @@ From within the program, entering `h`, `help` or `?` will show the program help The current RPNCalc help screen:
- \ No newline at end of file + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 3403651..6d5001a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.fross rpncalc - 4.7.8 + 4.8.0 jar rpncalc diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 468157c..1879fe9 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -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 diff --git a/src/main/java/org/fross/rpncalc/CommandParser.java b/src/main/java/org/fross/rpncalc/CommandParser.java index 8e1a0be..6f921aa 100644 --- a/src/main/java/org/fross/rpncalc/CommandParser.java +++ b/src/main/java/org/fross/rpncalc/CommandParser.java @@ -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); @@ -173,6 +183,7 @@ public static void Parse(StackObj calcStack, StackObj calcStack2, String cmdInpu // Dice case "dice": + case "roll": StackCommands.cmdDice(calcStack, cmdInputParam); break; diff --git a/src/main/java/org/fross/rpncalc/Help.java b/src/main/java/org/fross/rpncalc/Help.java index 46d7cee..02bc948 100644 --- a/src/main/java/org/fross/rpncalc/Help.java +++ b/src/main/java/org/fross/rpncalc/Help.java @@ -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) + "+"); @@ -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"); @@ -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"); diff --git a/src/main/java/org/fross/rpncalc/Math.java b/src/main/java/org/fross/rpncalc/Math.java index a2d6aef..2addd85 100644 --- a/src/main/java/org/fross/rpncalc/Math.java +++ b/src/main/java/org/fross/rpncalc/Math.java @@ -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(); @@ -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; } /** diff --git a/src/main/java/org/fross/rpncalc/StackCommands.java b/src/main/java/org/fross/rpncalc/StackCommands.java index 0c0cd3c..b7f6592 100644 --- a/src/main/java/org/fross/rpncalc/StackCommands.java +++ b/src/main/java/org/fross/rpncalc/StackCommands.java @@ -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) @@ -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 */ @@ -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 * @@ -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 diff --git a/src/main/java/org/fross/rpncalc/StackObj.java b/src/main/java/org/fross/rpncalc/StackObj.java index 67d892d..19b8e82 100644 --- a/src/main/java/org/fross/rpncalc/StackObj.java +++ b/src/main/java/org/fross/rpncalc/StackObj.java @@ -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 * diff --git a/src/main/java/org/fross/rpncalc/StackOperations.java b/src/main/java/org/fross/rpncalc/StackOperations.java index c9ebc54..4ebb2af 100644 --- a/src/main/java/org/fross/rpncalc/StackOperations.java +++ b/src/main/java/org/fross/rpncalc/StackOperations.java @@ -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 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))); @@ -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(); } /** diff --git a/src/test/java/org/fross/rpncalc/MathTest.java b/src/test/java/org/fross/rpncalc/MathTest.java index 6851443..9ce4d75 100644 --- a/src/test/java/org/fross/rpncalc/MathTest.java +++ b/src/test/java/org/fross/rpncalc/MathTest.java @@ -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() { @@ -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)); } /** diff --git a/src/test/java/org/fross/rpncalc/StackCommandsTest.java b/src/test/java/org/fross/rpncalc/StackCommandsTest.java index 3bac01c..7a7372e 100644 --- a/src/test/java/org/fross/rpncalc/StackCommandsTest.java +++ b/src/test/java/org/fross/rpncalc/StackCommandsTest.java @@ -83,7 +83,7 @@ void testCmdAbsoluteValue() { @Test void testCmdAverage() { StackObj stk = new StackObj(); - + // Test with Keep stk.push(1.1); stk.push(2.2); @@ -95,7 +95,7 @@ void testCmdAverage() { StackCommands.cmdAverage(stk, "keep"); assertEquals(-1.2571428571428573, stk.peek()); assertEquals(8, stk.size()); - + // Test without keep stk.push(1.23); stk.push(2.34); @@ -106,6 +106,17 @@ void testCmdAverage() { assertEquals(0.5532967032967032, stk.peek()); assertEquals(1, stk.size()); + // One last test just to be sure + stk.clear(); + Double[] testValues = { -5.0, 1.2, 3.34, 3.44, 3.45, 7.3, 8.76, 33.2, 42.44, 1000.01 }; + for (int i = 0; i < testValues.length; i++) { + stk.push(testValues[i]); + } + assertEquals(10, stk.size()); + StackCommands.cmdAverage(stk, ""); + assertEquals(109.814, stk.peek()); + assertEquals(1, stk.size()); + } /** @@ -416,7 +427,7 @@ void testCmdLinearRegression() { StackCommands.cmdRound(stk, "6"); assertEquals(11, stk.size()); assertEquals(16.671333, stk.pop()); - + // Test #3 stk.clear(); stk.push(29.11); @@ -500,6 +511,74 @@ void testCmdMaximum() { assertEquals(5.68, stk.get(stk.size() - 1)); } + /** + * Test median command with an odd number of stack items + */ + @Test + void testMedianOddNumberOfValues() { + StackObj stk = new StackObj(); + + // Test odd numbers on the stack with keep flag + stk.push(3.0); + stk.push(2.0); + stk.push(5.0); + stk.push(4.0); + stk.push(1.0); + + StackCommands.cmdMedian(stk, "keep"); + assertEquals(3, stk.pop()); + assertEquals(5, stk.size()); + + // Ensure the order is back to normal after the median sort + assertEquals(3, stk.get(0)); + assertEquals(2, stk.get(1)); + assertEquals(5, stk.get(2)); + assertEquals(4, stk.get(3)); + assertEquals(1, stk.get(4)); + + } + + /** + * Test median command with an even number of stack items + */ + @Test + void testMedianEvenNumberOfValues() { + StackObj stk = new StackObj(); + + // Test even numbers on the stack + stk.push(4.56); + stk.push(-1.23); + stk.push(5.67); + stk.push(2.34); + stk.push(4.56); + stk.push(-3.45); + + StackCommands.cmdMedian(stk, "keep"); + StackCommands.cmdRound(stk, "2"); + + assertEquals(3.45, stk.pop()); + assertEquals(6, stk.size()); + + // Ensure the order is back to normal after the median sort + assertEquals(4.56, stk.get(0)); + assertEquals(-1.23, stk.get(1)); + assertEquals(5.67, stk.get(2)); + assertEquals(2.34, stk.get(3)); + assertEquals(4.56, stk.get(4)); + assertEquals(-3.45, stk.get(5)); + + // One last test just to be sure + stk.clear(); + Double[] testValues = { -5.0, 1.2, 3.34, 3.44, 3.45, 7.3, 8.76, 33.2, 42.44, 1000.01 }; + for (int i = 0; i < testValues.length; i++) { + stk.push(testValues[i]); + } + assertEquals(10, stk.size()); + StackCommands.cmdMedian(stk, ""); + assertEquals(5.375, stk.peek()); + assertEquals(1, stk.size()); + } + /** * Test method for {@link org.fross.rpncalc.StackCommands#cmdMinimum(org.fross.rpncalc.StackObj)}. */ @@ -599,6 +678,22 @@ void testCmdRound() { stk.push(2.34567); StackCommands.cmdRound(stk, "4"); assertEquals(2.3457, stk.pop()); + + stk.push(-65.4321); + StackCommands.cmdRound(stk, "1"); + assertEquals(-65.4, stk.pop()); + + stk.push(-65.4329); + StackCommands.cmdRound(stk, "1"); + assertEquals(-65.4, stk.pop()); + + stk.push(-65.4329); + StackCommands.cmdRound(stk, "3"); + assertEquals(-65.433, stk.pop()); + + stk.push(-65.4329); + StackCommands.cmdRound(stk, "12"); + assertEquals(-65.4329, stk.pop()); } /** @@ -626,6 +721,82 @@ void testCmdSwapElements() { assertEquals(1, stk.pop()); } + /** + * Test stack sorting descending + */ + @Test + void testSortingDescending() { + StackObj stk = new StackObj(); + + stk.push(1.0); + stk.push(-2.0); + stk.push(6.882); + stk.push(9.0); + stk.push(3.0); + stk.push(2.0); + stk.push(7.0); + stk.push(6.881); + stk.push(-1.0); + stk.push(4.0); + stk.push(3.9); + stk.push(-1.01); + StackCommands.cmdSort(stk, "descending"); + + assertEquals(12, stk.size()); + + assertEquals(-2, stk.get(0)); + assertEquals(-1.01, stk.get(1)); + assertEquals(-1, stk.get(2)); + assertEquals(1, stk.get(3)); + assertEquals(2, stk.get(4)); + assertEquals(3, stk.get(5)); + assertEquals(3.9, stk.get(6)); + assertEquals(4, stk.get(7)); + assertEquals(6.881, stk.get(8)); + assertEquals(6.882, stk.get(9)); + assertEquals(7, stk.get(10)); + assertEquals(9, stk.get(11)); + + } + + /** + * Test stack sorting ascending + */ + @Test + void testSortingAscending() { + StackObj stk = new StackObj(); + + stk.push(1.0); + stk.push(-2.0); + stk.push(6.882); + stk.push(9.0); + stk.push(3.0); + stk.push(2.0); + stk.push(7.0); + stk.push(6.881); + stk.push(-1.0); + stk.push(4.0); + stk.push(3.9); + stk.push(-1.01); + StackCommands.cmdSort(stk, "ascending"); + + assertEquals(12, stk.size()); + + assertEquals(-2, stk.get(11)); + assertEquals(-1.01, stk.get(10)); + assertEquals(-1, stk.get(9)); + assertEquals(1, stk.get(8)); + assertEquals(2, stk.get(7)); + assertEquals(3, stk.get(6)); + assertEquals(3.9, stk.get(5)); + assertEquals(4, stk.get(4)); + assertEquals(6.881, stk.get(3)); + assertEquals(6.882, stk.get(2)); + assertEquals(7, stk.get(1)); + assertEquals(9, stk.get(0)); + + } + /** * Test method for {@link org.fross.rpncalc.StackCommands#cmdSqrt(org.fross.rpncalc.StackObj)}. */