项目作者: bartlomiej-gora

项目描述 :
Java Reverse Polish Notation Library
高级语言: Java
项目地址: git://github.com/bartlomiej-gora/RPNLibrary.git
创建时间: 2015-01-25T16:54:36Z
项目社区:https://github.com/bartlomiej-gora/RPNLibrary

开源协议:GNU General Public License v3.0

下载


RPNLibrary

Simple RPN (Reverse Polish Notation) Library for Java.

It is based on Dijkstra Algorithm. (https://en.wikipedia.org/wiki/Reverse_Polish_notation)

Quality Gate Status Bugs Code Smells Duplicated Lines (%)

Codacy Badge

Maven Central

javadoc

Story

Couple years ago I read Joshua Bloch’s “Java. Effective Programming”.
I wanted to practice what I’ve learned.
I didn’t want to create another CRUD like application, so I found that Dijkstra’s algorithm would be good
to learn design patterns, and effective programming.
First version’s were available on Sourceforge.
Couple years later I manage to publish my library on maven cetral repo.
Over the years I built a small ecosystem around this library.
Feel free to check my other projects that use this one.

Available functions:

+,-,*,/ with (), power(^)
Sin, cos, tg, ctg,
min, max, fib

example:

  1. Calculator calc = Calculator.createCalulator();
  2. BigDecimal result = calc.calculate("2^3*(12/6)+18/3+5.0/2");
  3. BigDecimal result2 = calc.calculate("3.678^2");
  4. BigDecimal resultSin = calc.calculate("sin(2)");
  5. BigDecimal resultSin2 = calc.calculate("sin(1+1)");
  6. BigDecimal resultCtg = calc.calculate("ctg(-1.65091)");
  7. BigDecimal min = calc.calculate("min(10, 8) + 10");

Maven:

  1. <dependency>
  2. <groupId>com.github.bartlomiej-gora</groupId>
  3. <artifactId>RPNLibrary</artifactId>
  4. <version>5.1.0</version>
  5. </dependency>

Changelog:

Version 5.1.0:

  • Removed Tests in Kotlin.
  • Moved to Java 17 (var, and Map.of)
  • Changed Interfaces names from RPNChecking -> RPNChecker, and RPNExecuting -> RPNExecutioner
  • Added new Factories for RPNChecker, and RPNExecutioner.

    If you want to customize the calculator by adding your own operators and functions to existing one, you can use those
  • Factories. Below there is an example of adding % (moduli) operator to the calculator.

    1. var rpnChecker = RPNCheckerFactory.createRPNCheckerWithDefaults(Map.of("%", 1), Map.of());
    2. AbstractOperatorStrategy modulo = new AbstractOperatorStrategy("%") {
    3. @Override
    4. public BigDecimal execute(final String first, final String second, final MathContext mathContext) {
    5. var firstDec = new BigDecimal(first, mathContext);
    6. var secondDec = new BigDecimal(second, mathContext);
    7. return firstDec.remainder(secondDec);
    8. }
    9. };
    10. var rpnExecutioner = RPNExecutionerFactory.createRPNExecutionerWithDefaults(Map.of("%", modulo), Map.of());
    11. var calc = Calculator.createCalculator(rpnChecker,rpnExecutioner, MathContext.DECIMAL64, 2);
    12. BigDecimal result = calc.calculate("4%17");
    13. assertThat(result).isEqualTo(new BigDecimal("4.00"));

Version 5.0.0:

  • Moved to java 8
  • Refactoring, split Calculator class into smaller pieces, using java 8 functional interfaces
  • Added tests written in Kotest:

example:

  1. class RPNFactoryTest : FreeSpec({
  2. val tested = RPNFactory(RPNChecker(DefaultStrategyProvider()))
  3. "should Return RPN" - {
  4. val text = "( 2 + 3 ) * 5"
  5. val result = tested.apply(text)
  6. result shouldBe "2 3 + 5 *"
  7. }
  8. "should Return RPN for Function call" - {
  9. val text = "sin ( 1 )"
  10. val result = tested.apply(text)
  11. result shouldBe "1 sin"
  12. }
  13. "should Return RPN for Function and equation" - {
  14. val text = "sin ( 1 ) + 27 * 8"
  15. val result = tested.apply(text)
  16. result shouldBe "1 sin 27 8 * +"
  17. }
  18. "should Return RPN for two Functions call" - {
  19. val text = "sin ( 1 ) + ctg ( 0 )"
  20. val result = tested.apply(text)
  21. result shouldBe "1 sin 0 ctg +"
  22. }
  23. })

Version 4.0.0:

Version 3.2.2:

  • Changed internal calculation from BigDecimal to Double in DefaultCalculator implementation

Version 3.2.1:

  • Fixed bug in divide operator, that caused:
    ex: “10/4 = 2, and not 2.5”,
    “5/2 = 2, and not 2.5”
  • Changed RoundinMode from HALF_UP, to HALF_EVEN
  • Changed internal calculation type from BigDecimal to Double

Version 3.2.1-SNAPSHOT:

  • Fixed bug in divide operator, that caused:
    ex: “10/4 = 2, and not 2.5”,
    “5/2 = 2, and not 2.5”
  • Changed RoundinMode from HALF_UP, to HALF_EVEN
  • Changed internal calculation type from BigDecimal to Double

Version 3.2.0:

IMPORTANT:

Changed package names from

  1. pl.bgora.rpn

to

  1. com.github.bgora.rpnlibrary

Fixed bug, that prevented from exucuting functions with multiple parameters.

New functions:

max() - takes two parameters, returns greater one

min() - take two parameters, returns less one

fib() - Fibonacci number

Refactor:

Changed createCalulator, and getDefaultEngine to use CalculationEngine interface

  1. /**
  2. * Creates AdvanceCalculator with given CalculatorEngine
  3. *
  4. * @param engine CalculationEngine implementation
  5. * @return AdvanceCalculator
  6. */
  7. public CalculatorInterface createCalulator(CalculationEngine engine) {
  8. return new AdvancedCalculator(RoundingMode.HALF_UP, engine);
  9. }
  10. /**
  11. * Return default CalculationEngine implementation
  12. *
  13. * @return CalculatorEngine
  14. */
  15. public CalculationEngine getDefaultEngine() {
  16. return new CalculatorEngine(StrategiesUtil.DEFAULT_OPERATORS, StrategiesUtil.DEFAULT_FUNCTIONS);
  17. }

Version 3.1.0:

  • Added package pl.bgora.rpn.advanced
  • Added AdvancedCalculatorFactory

The advanced Calculator works with CalculationEngine, which uses strategy pattern to run.
please see:

pl.bgora.rpn.AbstractOperatorStrategy

pl.bgora.rpn.AbstractFunctionStrategy

Example 1:

  1. AdvancedCalculatorFactory advancedCalculatorFactory = new AdvancedCalculatorFactory();
  2. calc = advancedCalculatorFactory.createCalulator();

Example 2:

Assume that you want to add a function sqrt(number), which will return The square root , You will have to extend
AbstractFunctionStrategy like this:

  1. public class SqrtFunctionStrategy extends AbstractFunctionStrategy {
  2. public SqrtFunctionStrategy() {
  3. super("sqrt", 1, RoundingMode.HALF_EVEN);
  4. }
  5. @Override
  6. public BigDecimal execute(String... params) {
  7. return java.math.BigDecimal.valueOf(Math.sqrt(x));
  8. }
  9. }

And then you can add your function like that:

  1. CalculatorInterface calc;
  2. AdvancedCalculatorFactory advancedCalculatorFactory = new AdvancedCalculatorFactory();
  3. CalculatorEngine engine = advancedCalculatorFactory.getDefaultEngine();
  4. engine.addFunctionStartegy(new SqrtFunctionStrategy());
  5. calc = advancedCalculatorFactory.createCalulator(engine);

Example 3:

Assume that you want to add a function max(number, number), which will return greater value, You will have to extend
AbstractFunctionStrategy like this:

  1. public class MaxFunctionStrategy extends AbstractFunctionStrategy {
  2. public MaxFunctionStrategy() {
  3. super("max", 2, RoundingMode.HALF_EVEN);
  4. }
  5. @Override
  6. public BigDecimal execute(String... params) {
  7. String first = params[0];
  8. String second = params[1];
  9. BigDecimal result = //do you calculation here
  10. return result; //result;
  11. }
  12. }

And then you can add your function like that:

  1. CalculatorInterface calc;
  2. AdvancedCalculatorFactory advancedCalculatorFactory = new AdvancedCalculatorFactory();
  3. CalculatorEngine engine = advancedCalculatorFactory.getDefaultEngine();
  4. engine.addFunctionStartegy(new MaxFunctionStrategy());
  5. calc = advancedCalculatorFactory.createCalulator(engine);