diff --git a/README.md b/README.md index 802ad9a..3b6f893 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,13 @@ The following Data Types are supported: 5. Boolean 6. Semantic Version +--- +**NOTE** + +Decimal will internally use BigDecimal for storage. + +--- + Usage examples: Simple Numerical Comparison diff --git a/build.gradle b/build.gradle index acf6974..78560a4 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,6 @@ dependencies { implementation 'io.vavr:vavr:0.10.4' implementation 'com.github.ben-manes.caffeine:caffeine:2.9.3' implementation 'org.projectlombok:lombok:1.18.26' - implementation 'org.apache.commons:commons-math3:3.6.1' annotationProcessor 'org.projectlombok:lombok:1.18.26' testAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:1.36' diff --git a/src/main/java/com/github/sidhant92/boolparser/datatype/DecimalDataType.java b/src/main/java/com/github/sidhant92/boolparser/datatype/DecimalDataType.java index 532b577..0ef884c 100644 --- a/src/main/java/com/github/sidhant92/boolparser/datatype/DecimalDataType.java +++ b/src/main/java/com/github/sidhant92/boolparser/datatype/DecimalDataType.java @@ -1,5 +1,6 @@ package com.github.sidhant92.boolparser.datatype; +import java.math.BigDecimal; import java.util.Optional; import com.github.sidhant92.boolparser.constant.DataType; @@ -7,9 +8,9 @@ * @author sidhant.aggarwal * @since 05/03/2023 */ -public class DecimalDataType extends AbstractDataType { +public class DecimalDataType extends AbstractDataType { public DecimalDataType() { - super(Double.class); + super(BigDecimal.class); } @Override @@ -22,7 +23,7 @@ public boolean isValid(final Object value) { boolean isValid = super.defaultIsValid(value); if (!isValid) { try { - Double.parseDouble(value.toString()); + new BigDecimal(value.toString()); return true; } catch (Exception ex) { return false; @@ -40,13 +41,13 @@ public boolean isValid(final Object value, final boolean useStrictValidation) { } @Override - public Optional getValue(Object value) { - final Optional result = defaultGetValue(value); + public Optional getValue(Object value) { + final Optional result = defaultGetValue(value); if (result.isPresent()) { return result; } try { - return Optional.of(Double.parseDouble(value.toString())); + return Optional.of(new BigDecimal(value.toString())); } catch (final Exception ignored) { } return Optional.empty(); diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/AvgFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/AvgFunction.java index 7b9eeb2..909c11e 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/AvgFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/AvgFunction.java @@ -1,12 +1,18 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; +import java.util.Optional; +import java.util.stream.Collectors; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; import com.github.sidhant92.boolparser.util.ValueUtils; @@ -19,16 +25,20 @@ public class AvgFunction extends AbstractFunction { public Object evaluate(final List items) { if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.DECIMAL))) { - return ValueUtils.caseDouble(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).average().getAsDouble()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + final List itemsConverted = items + .stream() + .map(item -> decimalDataType.getValue(item.getValue())) + .map(Optional::get) + .collect(Collectors.toList()); + final BigDecimal sum = itemsConverted + .stream().reduce(BigDecimal.ZERO, BigDecimal::add); + return ValueUtils.castDecimal(sum.divide(new BigDecimal(itemsConverted.size()), 3, RoundingMode.DOWN)); } - if (items - .stream().anyMatch(a -> a.getDataType().equals(DataType.LONG))) { - return ValueUtils.caseDouble(items - .stream().mapToLong(a -> Long.parseLong(a.getValue().toString())).average().getAsDouble()); - } - return ValueUtils.caseDouble(items - .stream().mapToInt(a -> Integer.parseInt(a.getValue().toString())).average().getAsDouble()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + final long sum = items + .stream().mapToLong(a -> longDataType.getValue(a.getValue()).get()).sum(); + return ValueUtils.castDecimal(new BigDecimal(sum).divide(new BigDecimal(items.size()), 2, RoundingMode.UNNECESSARY)); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/IntFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/IntFunction.java index ea032c8..d3a28de 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/IntFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/IntFunction.java @@ -1,12 +1,13 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; import com.github.sidhant92.boolparser.domain.EvaluatedNode; /** @@ -18,7 +19,7 @@ public class IntFunction extends AbstractFunction { public Object evaluate(final List items) { final EvaluatedNode item = items.get(0); if (item.getDataType() == DataType.DECIMAL) { - return ((Double) item.getValue()).intValue(); + return ((BigDecimal) DataTypeFactory.getDataType(DataType.DECIMAL).getValue(item.getValue()).get()).intValue(); } if (item.getDataType() == DataType.LONG) { return ((Long) item.getValue()).intValue(); diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/LenFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/LenFunction.java index b23729f..dda46f6 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/LenFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/LenFunction.java @@ -2,7 +2,6 @@ import java.util.Arrays; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MaxFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MaxFunction.java index 8d02e87..5f3b8a8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MaxFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MaxFunction.java @@ -1,12 +1,19 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; +import java.util.Optional; +import java.util.stream.Collectors; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.IntegerDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; import com.github.sidhant92.boolparser.util.ValueUtils; @@ -19,16 +26,25 @@ public class MaxFunction extends AbstractFunction { public Object evaluate(final List items) { if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.DECIMAL))) { - return ValueUtils.caseDouble(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).max().getAsDouble()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + final List itemsConverted = items + .stream() + .map(item -> decimalDataType.getValue(item.getValue())) + .map(Optional::get) + .collect(Collectors.toList()); + final BigDecimal max = itemsConverted + .stream().max(Comparator.naturalOrder()).get(); + return ValueUtils.castDecimal(max); } if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.LONG))) { - return ValueUtils.caseDouble(items - .stream().mapToLong(a -> Long.parseLong(a.getValue().toString())).max().getAsLong()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return ValueUtils.castDecimal(items + .stream().mapToLong(a -> longDataType.getValue(a.getValue()).get()).max().getAsLong()); } - return ValueUtils.caseDouble(items - .stream().mapToInt(a -> Integer.parseInt(a.getValue().toString())).max().getAsInt()); + final IntegerDataType integerDataType = (IntegerDataType) DataTypeFactory.getDataType(DataType.INTEGER); + return ValueUtils.castDecimal(items + .stream().mapToInt(a -> integerDataType.getValue(a.getValue()).get()).max().getAsInt()); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MeanFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MeanFunction.java index e3ae6ac..36ba128 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MeanFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MeanFunction.java @@ -3,14 +3,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.math3.stat.StatUtils; -import org.apache.commons.math3.stat.descriptive.moment.Mean; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; -import com.github.sidhant92.boolparser.util.ValueUtils; +import com.github.sidhant92.boolparser.function.FunctionFactory; /** * @author sidhant.aggarwal @@ -19,9 +16,7 @@ public class MeanFunction extends AbstractFunction { @Override public Object evaluate(final List items) { - final double mean = StatUtils.mean(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).toArray()); - return ValueUtils.caseDouble(mean); + return FunctionFactory.getArithmeticFunction(FunctionType.AVG).evaluate(items); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MedianFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MedianFunction.java index 9394e0c..90f0d12 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MedianFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MedianFunction.java @@ -1,13 +1,16 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.math3.stat.descriptive.rank.Median; +import java.util.Optional; +import java.util.stream.Collectors; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; import com.github.sidhant92.boolparser.util.ValueUtils; @@ -16,17 +19,25 @@ * @since 21/05/2024 */ public class MedianFunction extends AbstractFunction { - private final Median median; - public MedianFunction() { - this.median = new Median(); } @Override public Object evaluate(final List items) { - final double res = median.evaluate(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).toArray()); - return ValueUtils.caseDouble(res); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + final List itemsConverted = items + .stream() + .map(item -> decimalDataType.getValue(item.getValue())) + .map(Optional::get) + .collect(Collectors.toList()); + Collections.sort(itemsConverted); + if (items.size() % 2 == 0) { + final BigDecimal res = itemsConverted.get(items.size() / 2).add(itemsConverted.get(items.size() / 2 - 1)) + .divide(new BigDecimal("2"), 2, BigDecimal.ROUND_DOWN); + return ValueUtils.castDecimal(res); + } else { + return ValueUtils.castDecimal(itemsConverted.get(items.size() / 2)); + } } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MinFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MinFunction.java index 30a95c8..b18bcb8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MinFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/MinFunction.java @@ -1,12 +1,19 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; +import java.util.Optional; +import java.util.stream.Collectors; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.IntegerDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; import com.github.sidhant92.boolparser.util.ValueUtils; @@ -19,16 +26,25 @@ public class MinFunction extends AbstractFunction { public Object evaluate(final List items) { if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.DECIMAL))) { - return ValueUtils.caseDouble(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).min().getAsDouble()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + final List itemsConverted = items + .stream() + .map(item -> decimalDataType.getValue(item.getValue())) + .map(Optional::get) + .collect(Collectors.toList()); + final BigDecimal min = itemsConverted + .stream().min(Comparator.naturalOrder()).get(); + return ValueUtils.castDecimal(min); } if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.LONG))) { - return ValueUtils.caseDouble(items - .stream().mapToLong(a -> Long.parseLong(a.getValue().toString())).min().getAsLong()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return ValueUtils.castDecimal(items + .stream().mapToLong(a -> longDataType.getValue(a.getValue()).get()).min().getAsLong()); } - return ValueUtils.caseDouble(items - .stream().mapToInt(a -> Integer.parseInt(a.getValue().toString())).min().getAsInt()); + final IntegerDataType integerDataType = (IntegerDataType) DataTypeFactory.getDataType(DataType.INTEGER); + return ValueUtils.castDecimal(items + .stream().mapToInt(a -> integerDataType.getValue(a.getValue()).get()).min().getAsInt()); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/ModeFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/ModeFunction.java index 6539efd..01f9945 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/ModeFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/ModeFunction.java @@ -1,11 +1,11 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.math3.stat.StatUtils; -import org.apache.commons.math3.stat.descriptive.moment.Mean; +import java.util.Map; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; @@ -19,9 +19,34 @@ public class ModeFunction extends AbstractFunction { @Override public Object evaluate(final List items) { - final double mode = StatUtils.mode(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).toArray())[0]; - return ValueUtils.caseDouble(mode); + Map hm = new HashMap(); + int max = 1; + Object temp = items.get(0).getValue(); + + for (int i = 0; i < items.size(); i++) { + final Object value = items.get(i).getValue(); + + if (hm.get(value) != null) { + + int count = hm.get(value); + count++; + hm.put(value, count); + + if (count > max) { + max = count; + temp = value; + } + } else { + hm.put(value, 1); + } + } + if (temp instanceof BigDecimal) { + return ValueUtils.castDecimal((BigDecimal) temp); + } + if (temp instanceof Long) { + return ValueUtils.castLong((Long) temp); + } + return temp; } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/SumFunction.java b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/SumFunction.java index 8d8e54f..455339c 100644 --- a/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/SumFunction.java +++ b/src/main/java/com/github/sidhant92/boolparser/function/arithmetic/SumFunction.java @@ -1,12 +1,17 @@ package com.github.sidhant92.boolparser.function.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.tuple.Pair; +import java.util.Optional; +import java.util.stream.Collectors; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.FunctionType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; import com.github.sidhant92.boolparser.domain.EvaluatedNode; import com.github.sidhant92.boolparser.util.ValueUtils; @@ -19,16 +24,20 @@ public class SumFunction extends AbstractFunction { public Object evaluate(final List items) { if (items .stream().anyMatch(a -> a.getDataType().equals(DataType.DECIMAL))) { - return ValueUtils.caseDouble(items - .stream().mapToDouble(a -> Double.parseDouble(a.getValue().toString())).sum()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + final List itemsConverted = items + .stream() + .map(item -> decimalDataType.getValue(item.getValue())) + .map(Optional::get) + .collect(Collectors.toList()); + final BigDecimal sum = itemsConverted + .stream().reduce(BigDecimal.ZERO, BigDecimal::add); + return ValueUtils.castDecimal(sum); } - if (items - .stream().anyMatch(a -> a.getDataType().equals(DataType.LONG))) { - return ValueUtils.caseDouble(items - .stream().mapToLong(a -> Long.parseLong(a.getValue().toString())).sum()); - } - return ValueUtils.caseDouble(items - .stream().mapToInt(a -> Integer.parseInt(a.getValue().toString())).sum()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + final long sum = items + .stream().mapToLong(a -> longDataType.getValue(a.getValue()).get()).sum(); + return ValueUtils.castDecimal(sum); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/AddOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/AddOperator.java index 80dd2d4..d26a62b 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/AddOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/AddOperator.java @@ -1,13 +1,15 @@ package com.github.sidhant92.boolparser.operator.arithmetic; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; -import com.github.sidhant92.boolparser.exception.InvalidDataType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; +import com.github.sidhant92.boolparser.util.ValueUtils; /** * @author sidhant.aggarwal @@ -22,15 +24,11 @@ public Object evaluate(final Object leftOperand, final DataType leftOperandDataT return leftOperand + String.valueOf(rightOperand); } if (leftOperandDataType.equals(DataType.DECIMAL) || rightOperandDataType.equals(DataType.DECIMAL)) { - return Double.parseDouble(leftOperand.toString()) + Double.parseDouble(rightOperand.toString()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + return decimalDataType.getValue(leftOperand).get().add(decimalDataType.getValue(rightOperand).get()); } - if (leftOperandDataType.equals(DataType.LONG) || rightOperandDataType.equals(DataType.LONG)) { - return Long.parseLong(leftOperand.toString()) + Long.parseLong(rightOperand.toString()); - } - if (leftOperandDataType.equals(DataType.INTEGER) || rightOperandDataType.equals(DataType.INTEGER)) { - return Integer.parseInt(leftOperand.toString()) + Integer.parseInt(rightOperand.toString()); - } - return leftOperand + String.valueOf(rightOperand); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return ValueUtils.castLong(longDataType.getValue(leftOperand).get() + longDataType.getValue(rightOperand).get()); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/DivideOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/DivideOperator.java index 781d58d..e179f1b 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/DivideOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/DivideOperator.java @@ -1,12 +1,16 @@ package com.github.sidhant92.boolparser.operator.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; import com.github.sidhant92.boolparser.exception.InvalidDataType; +import com.github.sidhant92.boolparser.util.ValueUtils; /** * @author sidhant.aggarwal @@ -16,11 +20,9 @@ public class DivideOperator extends AbstractOperator { @Override public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { - final double res = Double.parseDouble(leftOperand.toString()) / Double.parseDouble(rightOperand.toString()); - if ((int) res == res) { - return (int) res; - } - return res; + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + return ValueUtils.castDecimal( + decimalDataType.getValue(leftOperand).get().divide(decimalDataType.getValue(rightOperand).get(), 3, BigDecimal.ROUND_DOWN)); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ExponentOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ExponentOperator.java index db4640b..b4c5239 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ExponentOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ExponentOperator.java @@ -6,7 +6,10 @@ import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; -import com.github.sidhant92.boolparser.exception.InvalidDataType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; +import com.github.sidhant92.boolparser.util.ValueUtils; /** * @author sidhant.aggarwal @@ -16,13 +19,13 @@ public class ExponentOperator extends AbstractOperator { @Override public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { - if (leftOperandDataType.equals(DataType.LONG) || rightOperandDataType.equals(DataType.LONG)) { - return (long) Math.pow(Long.parseLong(leftOperand.toString()), Long.parseLong(rightOperand.toString())); + if (leftOperandDataType.equals(DataType.DECIMAL) || rightOperandDataType.equals(DataType.DECIMAL)) { + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + return ValueUtils.castDecimal( + Math.pow(decimalDataType.getValue(leftOperand).get().doubleValue(), decimalDataType.getValue(rightOperand).get().doubleValue())); } - if (leftOperandDataType.equals(DataType.INTEGER) || rightOperandDataType.equals(DataType.INTEGER)) { - return (int) Math.pow(Long.parseLong(leftOperand.toString()), Long.parseLong(rightOperand.toString())); - } - throw new InvalidDataType(); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return ValueUtils.castDecimal(Math.pow(longDataType.getValue(leftOperand).get(), longDataType.getValue(rightOperand).get())); } @Override @@ -42,6 +45,6 @@ public List getAllowedContainerTypes() { @Override public List getAllowedDataTypes() { - return Arrays.asList(DataType.INTEGER, DataType.LONG); + return Arrays.asList(DataType.INTEGER, DataType.LONG, DataType.DECIMAL); } } diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ModulusOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ModulusOperator.java index 26c3d37..f79dd32 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ModulusOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/ModulusOperator.java @@ -6,6 +6,9 @@ import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.IntegerDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; import com.github.sidhant92.boolparser.exception.InvalidDataType; /** @@ -17,12 +20,11 @@ public class ModulusOperator extends AbstractOperator { public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { if (leftOperandDataType.equals(DataType.LONG) || rightOperandDataType.equals(DataType.LONG)) { - return Long.parseLong(leftOperand.toString()) % Long.parseLong(rightOperand.toString()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return longDataType.getValue(leftOperand).get() % longDataType.getValue(rightOperand).get(); } - if (leftOperandDataType.equals(DataType.INTEGER) || rightOperandDataType.equals(DataType.INTEGER)) { - return Integer.parseInt(leftOperand.toString()) % Integer.parseInt(rightOperand.toString()); - } - throw new InvalidDataType(); + final IntegerDataType integerDataType = (IntegerDataType) DataTypeFactory.getDataType(DataType.INTEGER); + return integerDataType.getValue(leftOperand).get() % integerDataType.getValue(rightOperand).get(); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/MultiplyOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/MultiplyOperator.java index 9cfa41d..931a3ff 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/MultiplyOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/MultiplyOperator.java @@ -6,7 +6,10 @@ import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; -import com.github.sidhant92.boolparser.exception.InvalidDataType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; +import com.github.sidhant92.boolparser.util.ValueUtils; /** * @author sidhant.aggarwal @@ -17,15 +20,11 @@ public class MultiplyOperator extends AbstractOperator { public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { if (leftOperandDataType.equals(DataType.DECIMAL) || rightOperandDataType.equals(DataType.DECIMAL)) { - return Double.parseDouble(leftOperand.toString()) * Double.parseDouble(rightOperand.toString()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + return decimalDataType.getValue(leftOperand).get().multiply(decimalDataType.getValue(rightOperand).get()); } - if (leftOperandDataType.equals(DataType.LONG) || rightOperandDataType.equals(DataType.LONG)) { - return Long.parseLong(leftOperand.toString()) * Long.parseLong(rightOperand.toString()); - } - if (leftOperandDataType.equals(DataType.INTEGER) || rightOperandDataType.equals(DataType.INTEGER)) { - return Integer.parseInt(leftOperand.toString()) * Integer.parseInt(rightOperand.toString()); - } - throw new InvalidDataType(); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return ValueUtils.castLong(longDataType.getValue(leftOperand).get() * longDataType.getValue(rightOperand).get()); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/SubtractOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/SubtractOperator.java index dab87b7..fc4917c 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/SubtractOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/SubtractOperator.java @@ -6,7 +6,10 @@ import com.github.sidhant92.boolparser.constant.ContainerDataType; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.Operator; -import com.github.sidhant92.boolparser.exception.InvalidDataType; +import com.github.sidhant92.boolparser.datatype.DataTypeFactory; +import com.github.sidhant92.boolparser.datatype.DecimalDataType; +import com.github.sidhant92.boolparser.datatype.IntegerDataType; +import com.github.sidhant92.boolparser.datatype.LongDataType; /** * @author sidhant.aggarwal @@ -17,15 +20,15 @@ public class SubtractOperator extends AbstractOperator { public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { if (leftOperandDataType.equals(DataType.DECIMAL) || rightOperandDataType.equals(DataType.DECIMAL)) { - return Double.parseDouble(leftOperand.toString()) - Double.parseDouble(rightOperand.toString()); + final DecimalDataType decimalDataType = (DecimalDataType) DataTypeFactory.getDataType(DataType.DECIMAL); + return decimalDataType.getValue(leftOperand).get().subtract(decimalDataType.getValue(rightOperand).get()); } if (leftOperandDataType.equals(DataType.LONG) || rightOperandDataType.equals(DataType.LONG)) { - return Long.parseLong(leftOperand.toString()) - Long.parseLong(rightOperand.toString()); + final LongDataType longDataType = (LongDataType) DataTypeFactory.getDataType(DataType.LONG); + return longDataType.getValue(leftOperand).get() - longDataType.getValue(rightOperand).get(); } - if (leftOperandDataType.equals(DataType.INTEGER) || rightOperandDataType.equals(DataType.INTEGER)) { - return Integer.parseInt(leftOperand.toString()) - Integer.parseInt(rightOperand.toString()); - } - throw new InvalidDataType(); + final IntegerDataType integerDataType = (IntegerDataType) DataTypeFactory.getDataType(DataType.INTEGER); + return integerDataType.getValue(leftOperand).get() - integerDataType.getValue(rightOperand).get(); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/UnaryOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/UnaryOperator.java index a6f2e6d..8c0675c 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/UnaryOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/arithmetic/UnaryOperator.java @@ -1,5 +1,6 @@ package com.github.sidhant92.boolparser.operator.arithmetic; +import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -16,7 +17,7 @@ public class UnaryOperator extends AbstractOperator { public Object evaluate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, final DataType rightOperandDataType) { if (leftOperandDataType.equals(DataType.DECIMAL)) { - return -(double) leftOperand; + return ((BigDecimal) leftOperand).negate(); } if (leftOperandDataType.equals(DataType.LONG)) { return -(long) leftOperand; diff --git a/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java b/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java index 144f5f5..6366e73 100644 --- a/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java +++ b/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java @@ -1,5 +1,6 @@ package com.github.sidhant92.boolparser.util; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -61,7 +62,7 @@ public static Object convertValue(final String value, final DataType dataType) { case LONG: return Long.parseLong(value); case DECIMAL: - return Double.parseDouble(value); + return new BigDecimal(value); case BOOLEAN: return Boolean.parseBoolean(value); case VERSION: @@ -77,10 +78,24 @@ public static Object convertValue(final String value, final DataType dataType) { } } - public static Object caseDouble(final double value) { + public static Object castDecimal(final double value) { if ((int) value == value) { return (int) value; } + return new BigDecimal(value); + } + + public static Object castLong(final long value) { + if ((int) value == value) { + return (int) value; + } + return value; + } + + public static Object castDecimal(final BigDecimal value) { + if (value.signum() == 0 || value.scale() <= 0 || value.stripTrailingZeros().scale() <= 0) { + return value.intValueExact(); + } return value; } @@ -101,7 +116,7 @@ public static DataType getDataType(final Object value) { if (value instanceof Boolean) { return DataType.BOOLEAN; } - if (value instanceof Float || value instanceof Double) { + if (value instanceof Float || value instanceof Double || value instanceof BigDecimal) { return DataType.DECIMAL; } if (value instanceof Integer) { diff --git a/src/test/java/com/github/sidhant92/boolparser/application/ArithmeticExpressionEvaluatorTest.java b/src/test/java/com/github/sidhant92/boolparser/application/ArithmeticExpressionEvaluatorTest.java index d36315b..7f2ff5c 100644 --- a/src/test/java/com/github/sidhant92/boolparser/application/ArithmeticExpressionEvaluatorTest.java +++ b/src/test/java/com/github/sidhant92/boolparser/application/ArithmeticExpressionEvaluatorTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -60,7 +61,7 @@ public void testSimpleDivideOperationWithoutVariable1() { final Map data = new HashMap<>(); final Try resultOptional = arithmeticExpressionEvaluator.evaluate("5 / 4", data); assertTrue(resultOptional.isSuccess()); - assertEquals(resultOptional.get(), 1.25); + assertTrue(((BigDecimal)resultOptional.get()).compareTo(new BigDecimal("1.25")) == 0); } @Test @@ -268,7 +269,7 @@ public void testMaxArithmeticFunctionDouble() { data.put("a", 10); final Try resultOptional = arithmeticExpressionEvaluator.evaluate("max (1,2,3,4.5)", data); assertTrue(resultOptional.isSuccess()); - assertEquals(resultOptional.get(), 4.5d); + assertTrue(((BigDecimal)resultOptional.get()).compareTo(new BigDecimal("4.5")) == 0); } @Test @@ -286,7 +287,7 @@ public void testAvgArithmeticFunctionDouble() { data.put("a", 10); final Try resultOptional = arithmeticExpressionEvaluator.evaluate("avg (1,2,3,4.5)", data); assertTrue(resultOptional.isSuccess()); - assertEquals(resultOptional.get(), 2.625); + assertTrue(((BigDecimal) resultOptional.get()).compareTo(new BigDecimal("2.625")) == 0); } @Test @@ -304,7 +305,7 @@ public void testSumArithmeticFunctionDouble() { data.put("a", 10); final Try resultOptional = arithmeticExpressionEvaluator.evaluate("sum (1,2,3,4.5)", data); assertTrue(resultOptional.isSuccess()); - assertEquals(resultOptional.get(), 10.5); + assertTrue(((BigDecimal)resultOptional.get()).compareTo(new BigDecimal("10.5")) == 0); } @Test @@ -322,7 +323,7 @@ public void testMeanArithmeticFunctionDouble() { data.put("a", 10); final Try resultOptional = arithmeticExpressionEvaluator.evaluate("mean (1,2,3,4.5)", data); assertTrue(resultOptional.isSuccess()); - assertEquals(resultOptional.get(), 2.625); + assertTrue(((BigDecimal)resultOptional.get()).compareTo(new BigDecimal("2.625")) == 0); } @Test diff --git a/src/test/java/com/github/sidhant92/boolparser/parser/antlr/BooleanFilterBoolParserTest.java b/src/test/java/com/github/sidhant92/boolparser/parser/antlr/BooleanFilterBoolParserTest.java index 20ec9a6..2fb4988 100644 --- a/src/test/java/com/github/sidhant92/boolparser/parser/antlr/BooleanFilterBoolParserTest.java +++ b/src/test/java/com/github/sidhant92/boolparser/parser/antlr/BooleanFilterBoolParserTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.math.BigDecimal; import org.junit.jupiter.api.Test; import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.LogicalOperationType; @@ -101,7 +102,7 @@ public void testSingleDecimalToken() { final Try nodeOptional = boolExpressionBoolParser.parseExpression("age=44.34"); assertTrue(nodeOptional.isSuccess()); verifyComparisonToken(nodeOptional.get(), "age", Operator.EQUALS); - verifyUnaryToken(((ComparisonNode) nodeOptional.get()).getValue(), 44.34, DataType.DECIMAL); + verifyUnaryToken(((ComparisonNode) nodeOptional.get()).getValue(), new BigDecimal("44.34"), DataType.DECIMAL); } @Test @@ -133,7 +134,7 @@ public void testSingleDecimalRangeToken() { final Try nodeOptional = boolExpressionBoolParser.parseExpression("age 18.4 TO 44.2"); assertTrue(nodeOptional.isSuccess()); assertEquals(nodeOptional.get().getTokenType().name(), NodeType.NUMERIC_RANGE.name()); - verifyNumericRangeToken((NumericRangeNode) nodeOptional.get(), "age", 18.4, 44.2); + verifyNumericRangeToken((NumericRangeNode) nodeOptional.get(), "age", new BigDecimal("18.4"), new BigDecimal("44.2")); } @Test @@ -401,7 +402,7 @@ public void testAddOperatorDecimal() { final Try nodeOptional = boolExpressionBoolParser.parseExpression("20.5 + 5"); assertTrue(nodeOptional.isSuccess()); assertEquals(nodeOptional.get().getTokenType(), NodeType.ARITHMETIC); - assertEquals(((UnaryNode) ((ArithmeticNode) nodeOptional.get()).getLeft()).getValue(), 20.5); + assertEquals(((UnaryNode) ((ArithmeticNode) nodeOptional.get()).getLeft()).getValue(), new BigDecimal("20.5")); assertEquals(((UnaryNode) ((ArithmeticNode) nodeOptional.get()).getLeft()).getDataType(), DataType.DECIMAL); assertEquals(((UnaryNode) ((ArithmeticNode) nodeOptional.get()).getRight()).getValue(), 5); assertEquals(((UnaryNode) ((ArithmeticNode) nodeOptional.get()).getRight()).getDataType(), DataType.INTEGER);