diff --git a/CHANGELOG.md b/CHANGELOG.md index 71f8f7e11300..39715b401ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,12 @@ - [Added Statistic.Product][10122] - [Added Encoding.Default that tries to detect UTF-8 or UTF-16 encoding based on BOM][10130] +- [Added `Decimal` column to the in-memory database, with some arithmetic + operations.][9950] [debug-shortcuts]: +[9950]: https://github.com/enso-org/enso/pull/9950 [10122]: https://github.com/enso-org/enso/pull/10122 [10130]: https://github.com/enso-org/enso/pull/10130 diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso index 8795d1421672..77fafbdd2e33 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Decimal.enso @@ -499,7 +499,8 @@ type Decimal # => Decimal.new 45.7 divide : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error divide self (that : Decimal) (math_context : Math_Context | Nothing = Nothing) -> Decimal ! Arithmetic_Error = - handle_java_exception <| + extra_message = " Please use `.divide` with an explicit `Math_Context` to limit the numeric precision." + handle_java_exception extra_message=extra_message <| case math_context of Nothing -> Decimal.Value (self.big_decimal.divide that.big_decimal) _ -> Decimal.Value (self.big_decimal.divide that.big_decimal math_context.math_context) @@ -1042,9 +1043,9 @@ attach_loss_of_numeric_precision x value = Warning.attach (Loss_Of_Numeric_Precision.Warning x value) value ## PRIVATE -handle_java_exception ~action = +handle_java_exception ~action (extra_message : Text = "") = Panic.catch ArithmeticException action caught_panic-> - Error.throw (Arithmetic_Error.Error caught_panic.payload.getMessage) + Error.throw (Arithmetic_Error.Error caught_panic.payload.getMessage+extra_message) ## PRIVATE handle_unsupported_argument_types ~action = diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Column.enso index 58dd631b5a3b..6d784fb9e09d 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Column.enso @@ -31,6 +31,7 @@ import project.Value_Type.Value_Type from project.Errors import Conversion_Failure, Floating_Point_Equality, Inexact_Type_Coercion, Invalid_Column_Names, Invalid_Value_Type, No_Index_Set_Error from project.Internal.Column_Format import all from project.Internal.Java_Exports import make_date_builder_adapter, make_string_builder +from project.Internal.Storage import enso_to_java, java_to_enso polyglot java import org.enso.base.Time_Utils polyglot java import org.enso.table.data.column.operation.cast.CastProblemAggregator @@ -89,6 +90,12 @@ type Column Value_Type.Boolean -> False Value_Type.Byte -> False _ -> True + needs_enso_to_java_conversion = case value_type of + Value_Type.Decimal _ _ -> True + Auto -> True + _ -> False + enso_to_java_maybe items = if needs_enso_to_java_conversion.not then items else + items.map enso_to_java expected_storage_type = case value_type of Auto -> Nothing _ -> Storage.from_value_type value_type on_problems=Problem_Behavior.Report_Warning @@ -102,7 +109,7 @@ type Column Invalid_Column_Names.handle_java_exception <| Polyglot_Helpers.handle_polyglot_dataflow_errors <| handle_invalid_value_type <| java_column = Java_Problems.with_problem_aggregator Problem_Behavior.Report_Warning java_problem_aggregator-> case needs_polyglot_conversion of - True -> Java_Column.fromItems name items expected_storage_type java_problem_aggregator + True -> Java_Column.fromItems name (enso_to_java_maybe items) expected_storage_type java_problem_aggregator False -> Java_Column.fromItemsNoDateConversion name items expected_storage_type java_problem_aggregator result = Column.Value java_column . throw_on_warning Conversion_Failure result.catch Conversion_Failure error-> @@ -684,8 +691,9 @@ type Column example_div = Examples.decimal_column ^ Examples.integer_column ^ : Column | Any -> Column ^ self other = - Value_Type_Helpers.check_binary_numeric_op self other <| - run_vectorized_binary_op self Java_Storage.Maps.POWER other + Illegal_Argument.handle_java_exception <| + Value_Type_Helpers.check_binary_numeric_op self other <| + run_vectorized_binary_op self Java_Storage.Maps.POWER other ## ALIAS and GROUP Standard.Base.Operators @@ -2187,7 +2195,7 @@ type Column if valid_index.not then default else storage = self.java_column.getStorage if storage.isNothing index then Nothing else - storage.getItem index + java_to_enso <| storage.getItem index ## ICON data_input Returns a column containing rows of this column. @@ -2211,7 +2219,7 @@ type Column example_to_vector = Examples.integer_column.to_vector to_vector : Vector - to_vector self = Vector.from_polyglot_array self.java_column.getStorage.toList + to_vector self = Vector.from_polyglot_array self.java_column.getStorage.toList . map java_to_enso ## GROUP Standard.Base.Metadata ICON metadata @@ -2533,7 +2541,7 @@ run_vectorized_binary_op column name operand new_name=Nothing fallback_fn=Nothin _ -> s1 = column.java_column.getStorage rs = Polyglot_Helpers.handle_polyglot_dataflow_errors <| - s1.vectorizedOrFallbackBinaryMap name problem_builder fallback_fn operand skip_nulls storage_type + s1.vectorizedOrFallbackBinaryMap name problem_builder fallback_fn (enso_to_java operand) skip_nulls storage_type Column.Value (Java_Column.new effective_new_name rs) ## PRIVATE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Storage.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Storage.enso index 8608ebc839b0..f21059e9d378 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Storage.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Storage.enso @@ -8,8 +8,11 @@ import project.Value_Type.Bits import project.Value_Type.Value_Type from project.Errors import Inexact_Type_Coercion +polyglot java import java.math.BigDecimal + polyglot java import org.enso.table.data.column.builder.Builder as Java_Builder polyglot java import org.enso.table.data.column.storage.type.AnyObjectType +polyglot java import org.enso.table.data.column.storage.type.BigDecimalType polyglot java import org.enso.table.data.column.storage.type.BigIntegerType polyglot java import org.enso.table.data.column.storage.type.Bits as Java_Bits polyglot java import org.enso.table.data.column.storage.type.BooleanType @@ -40,6 +43,7 @@ to_value_type storage_type = case storage_type of _ : DateType -> Value_Type.Date _ : DateTimeType -> Value_Type.Date_Time with_timezone=True _ : TimeOfDayType -> Value_Type.Time + _ : BigDecimalType -> Value_Type.Decimal _ : BigIntegerType -> Value_Type.Decimal scale=0 _ : AnyObjectType -> Value_Type.Mixed @@ -64,11 +68,29 @@ closest_storage_type value_type = case value_type of Value_Type.Mixed -> AnyObjectType.INSTANCE Value_Type.Decimal _ scale -> is_integer = scale.is_nothing || scale <= 0 - if is_integer then BigIntegerType.INSTANCE else - Error.throw (Illegal_Argument.Error "Columns of type "+value_type.to_display_text+" are currently not supported in the in-memory backend - only Decimal of integer type (scale <= 0) is supported. You may cast the column to Float first (lossy conversion).") + if is_integer then BigIntegerType.INSTANCE else BigDecimalType.INSTANCE _ -> Error.throw (Illegal_Argument.Error "Columns of type "+value_type.to_display_text+" are currently not supported in the in-memory backend.") +## PRIVATE + + Convert an Enso value to a Java value before storing it in a Java `Column`. + This step is unnecessary for primitive and builtin values, but necessary for + values such as `Decimal`/`BigDecimal`. +enso_to_java x = case x of + Decimal.Value big_decimal -> big_decimal + _ -> x + +## PRIVATE + + Convert a Java value to an Enso value before returning it to Enso from a Java + `Column`. This step is unnecessary for primitive and builtin values, but + necessary for values such as `Decimal`/`BigDecimal`. +java_to_enso x = case x of + _ : Nothing -> Nothing + _ : BigDecimal -> Decimal.Value x + _ -> x + ## PRIVATE Converts a value type to an in-memory storage type, possibly approximating it to the closest supported type. diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Value_Type_Helpers.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Value_Type_Helpers.enso index 34148b97639d..92a030e128d3 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Value_Type_Helpers.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Value_Type_Helpers.enso @@ -27,6 +27,7 @@ most_specific_value_type : Any -> Boolean -> Value_Type most_specific_value_type value use_smallest=False = case value of _ : Float -> Value_Type.Float Bits.Bits_64 + _ : Decimal -> Value_Type.Decimal _ : Boolean -> Value_Type.Boolean _ : Date -> Value_Type.Date _ : Time_Of_Day -> Value_Type.Time diff --git a/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java b/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java index d7b860200e78..8e4f5d38333c 100644 --- a/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java +++ b/std-bits/base/src/main/java/org/enso/base/polyglot/NumericConverter.java @@ -59,6 +59,25 @@ public static BigInteger coerceToBigInteger(Object o) { } } + /** + * Coerces a number to a BigDecimal. + * + *

Will throw an exception if the object is not a number. + */ + public static BigDecimal coerceToBigDecimal(Object o) { + return switch (o) { + case Double x -> BigDecimal.valueOf(x); + case BigDecimal x -> x; + case Float x -> BigDecimal.valueOf(x); + case BigInteger x -> new BigDecimal(x); + case Long x -> BigDecimal.valueOf(x); + case Integer x -> BigDecimal.valueOf(x); + case Short x -> BigDecimal.valueOf(x); + case Byte x -> BigDecimal.valueOf(x); + default -> throw new UnsupportedOperationException("Cannot coerce " + o + " to a BigDecimal."); + }; + } + /** Returns true if the object is any supported number. */ public static boolean isCoercibleToDouble(Object o) { return isFloatLike(o)|| isCoercibleToLong(o) || o instanceof BigInteger; @@ -66,7 +85,6 @@ public static boolean isCoercibleToDouble(Object o) { public static boolean isFloatLike(Object o) { return o instanceof Double - || o instanceof BigDecimal || o instanceof Float; } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigDecimalBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigDecimalBuilder.java new file mode 100644 index 000000000000..e6f56cde11f4 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigDecimalBuilder.java @@ -0,0 +1,49 @@ +package org.enso.table.data.column.builder; + +import java.math.BigDecimal; +import org.enso.table.data.column.storage.Storage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; +import org.enso.table.data.column.storage.type.BigDecimalType; +import org.enso.table.data.column.storage.type.StorageType; +import org.enso.table.error.ValueTypeMismatchException; + +/** A builder for BigDecimal columns. */ +public class BigDecimalBuilder extends TypedBuilderImpl { + @Override + protected BigDecimal[] newArray(int size) { + return new BigDecimal[size]; + } + + public BigDecimalBuilder(int size) { + super(size); + } + + @Override + public StorageType getType() { + return BigDecimalType.INSTANCE; + } + + @Override + public void appendNoGrow(Object o) { + try { + data[currentSize++] = (BigDecimal) o; + } catch (ClassCastException e) { + throw new ValueTypeMismatchException(getType(), o); + } + } + + @Override + public void append(Object o) { + appendNoGrow(o); + } + + @Override + public boolean accepts(Object o) { + return o instanceof BigDecimal; + } + + @Override + protected Storage doSeal() { + return new BigDecimalStorage(data, currentSize); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java index 9b6782e92f87..8fb6296d8d2a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/Builder.java @@ -2,6 +2,7 @@ import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.type.AnyObjectType; +import org.enso.table.data.column.storage.type.BigDecimalType; import org.enso.table.data.column.storage.type.BigIntegerType; import org.enso.table.data.column.storage.type.BooleanType; import org.enso.table.data.column.storage.type.DateTimeType; @@ -38,6 +39,7 @@ public static Builder getForType( case IntegerType integerType -> NumericBuilder.createLongBuilder( size, integerType, problemAggregator); case TextType textType -> new StringBuilder(size, textType); + case BigDecimalType x -> new BigDecimalBuilder(size); case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator); case null -> new InferredBuilder(size, problemAggregator); }; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateBuilder.java index 430bd35190b0..047dc74637e8 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateBuilder.java @@ -7,7 +7,7 @@ import org.enso.table.data.column.storage.type.StorageType; import org.enso.table.error.ValueTypeMismatchException; -/** A builder for string columns. */ +/** A builder for LocalDate columns. */ public class DateBuilder extends TypedBuilderImpl { @Override protected LocalDate[] newArray(int size) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateTimeBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateTimeBuilder.java index 35fe0672aa03..09a4a77e1856 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateTimeBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DateTimeBuilder.java @@ -12,7 +12,7 @@ import org.enso.table.error.ValueTypeMismatchException; import org.graalvm.polyglot.Context; -/** A builder for string columns. */ +/** A builder for ZonedDateTime columns. */ public class DateTimeBuilder extends TypedBuilderImpl { @Override protected ZonedDateTime[] newArray(int size) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/InferredBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/InferredBuilder.java index 55c3bcc80250..97d7214c1f89 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/InferredBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/InferredBuilder.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.builder; +import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalTime; @@ -115,6 +116,8 @@ private void initBuilderFor(Object o) { currentBuilder = new StringBuilder(initialCapacity, TextType.VARIABLE_LENGTH); } else if (o instanceof BigInteger) { currentBuilder = new BigIntegerBuilder(initialCapacity, problemAggregator); + } else if (o instanceof BigDecimal) { + currentBuilder = new BigDecimalBuilder(initialCapacity); } else if (o instanceof LocalDate) { currentBuilder = new DateBuilder(initialCapacity); } else if (o instanceof LocalTime) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/TimeOfDayBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/TimeOfDayBuilder.java index 21dd0b259462..09a04fd68062 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/TimeOfDayBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/TimeOfDayBuilder.java @@ -7,7 +7,7 @@ import org.enso.table.data.column.storage.type.TimeOfDayType; import org.enso.table.error.ValueTypeMismatchException; -/** A builder for string columns. */ +/** A builder for LocalTime columns. */ public class TimeOfDayBuilder extends TypedBuilderImpl { @Override protected LocalTime[] newArray(int size) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/StorageConverter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/StorageConverter.java index 2714f7f7ce95..4933d843b749 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/StorageConverter.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/StorageConverter.java @@ -2,6 +2,7 @@ import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.type.AnyObjectType; +import org.enso.table.data.column.storage.type.BigDecimalType; import org.enso.table.data.column.storage.type.BigIntegerType; import org.enso.table.data.column.storage.type.BooleanType; import org.enso.table.data.column.storage.type.DateTimeType; @@ -29,6 +30,8 @@ static StorageConverter fromStorageType(StorageType storageType) { case TextType textType -> new ToTextStorageConverter(textType); case TimeOfDayType timeOfDayType -> new ToTimeOfDayStorageConverter(); case BigIntegerType bigIntegerType -> new ToBigIntegerConverter(); + case BigDecimalType bigDecimalType -> throw new UnsupportedOperationException( + "Conversion to BigDecimal is not yet supported."); }; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/AddOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/AddOp.java index 7e5fc0026ed3..2306ef136ad9 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/AddOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/AddOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.storage.Storage; @@ -32,4 +33,10 @@ public BigInteger doBigInteger( BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { return a.add(b); } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + return a.add(b); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/BigDecimalDivideOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/BigDecimalDivideOp.java new file mode 100644 index 000000000000..e5e20b5b86fe --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/BigDecimalDivideOp.java @@ -0,0 +1,25 @@ +package org.enso.table.data.column.operation.map.numeric.arithmetic; + +import java.math.BigDecimal; +import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; +import org.enso.table.data.column.storage.Storage; + +public class BigDecimalDivideOp> + extends NumericBinaryOpReturningBigDecimal { + public BigDecimalDivideOp() { + super(Storage.Maps.DIV); + } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + try { + return a.divide(b); + } catch (ArithmeticException e) { + String extraMessage = + " Please use `.divide` with an explicit `Math_Context` to limit the numeric precision."; + problemAggregator.reportArithmeticError(e.getMessage() + extraMessage, ix); + return null; + } + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/ModOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/ModOp.java index 3a0c776ceaa9..fb544c993700 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/ModOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/ModOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.storage.Storage; @@ -40,4 +41,15 @@ public BigInteger doBigInteger( return a.mod(b); } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + if (b.equals(BigDecimal.ZERO)) { + problemAggregator.reportDivisionByZero(ix); + return null; + } + + return a.remainder(b); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/MulOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/MulOp.java index 4cf27e8dd06a..781c1c1c1093 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/MulOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/MulOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.storage.Storage; @@ -32,4 +33,10 @@ public BigInteger doBigInteger( BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { return a.multiply(b); } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + return a.multiply(b); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java index 879f7d6a8712..d9ff6db9028a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; @@ -10,4 +11,7 @@ public interface NumericBinaryOpDefinition { BigInteger doBigInteger( BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator); + + BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java index ede9667b08e6..d57f5c1bfac0 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java @@ -2,15 +2,18 @@ import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage; +import java.math.BigDecimal; import java.math.BigInteger; import java.util.BitSet; import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.operation.map.BinaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; +import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; import org.enso.table.data.column.operation.map.numeric.helpers.BigIntegerArrayAdapter; import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.numeric.AbstractLongStorage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; import org.enso.table.data.column.storage.numeric.BigIntegerStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage; import org.enso.table.data.column.storage.numeric.LongStorage; @@ -41,6 +44,8 @@ public Storage runBinaryMap( BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); case BigIntegerStorage s -> runBigIntegerMap( BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); + case BigDecimalStorage s -> runBigDecimalMap( + BigDecimalArrayAdapter.fromStorage(s), new BigDecimal(rhs), problemAggregator); case DoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemAggregator); default -> throw new IllegalStateException( "Unsupported storage: " + storage.getClass().getCanonicalName()); @@ -53,6 +58,10 @@ public Storage runBinaryMap( BigIntegerArrayAdapter.fromStorage(s), BigInteger.valueOf(argAsLong), problemAggregator); + case BigDecimalStorage s -> runBigDecimalMap( + BigDecimalArrayAdapter.fromStorage(s), + BigDecimal.valueOf(argAsLong), + problemAggregator); case DoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator); default -> throw new IllegalStateException( "Unsupported storage: " + storage.getClass().getCanonicalName()); @@ -64,10 +73,17 @@ public Storage runBinaryMap( DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator); case BigIntegerStorage s -> runDoubleMap( DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator); + case BigDecimalStorage s -> runBigDecimalMap( + BigDecimalArrayAdapter.fromStorage(s), + BigDecimal.valueOf(doubleArg), + problemAggregator); case DoubleStorage s -> runDoubleMap(s, doubleArg, problemAggregator); default -> throw new IllegalStateException( "Unsupported storage: " + storage.getClass().getCanonicalName()); }; + } else if (arg instanceof BigDecimal bd) { + return runBigDecimalMap( + BigDecimalArrayAdapter.fromAnyStorage(storage), bd, problemAggregator); } else { throw new UnexpectedTypeException("a Number."); } @@ -78,7 +94,14 @@ public Storage runBinaryMap( public Storage runZip( I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { return switch (storage) { - case DoubleStorage lhs -> runDoubleZip(lhs, fromAnyStorage(arg), problemAggregator); + case DoubleStorage lhs -> switch (arg) { + case BigDecimalStorage rhs -> { + BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); + BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromStorage(rhs); + yield runBigDecimalZip(left, right, problemAggregator); + } + default -> runDoubleZip(lhs, fromAnyStorage(arg), problemAggregator); + }; case AbstractLongStorage lhs -> switch (arg) { case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); @@ -89,28 +112,45 @@ public Storage runZip( } case DoubleStorage rhs -> runDoubleZip( DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); + case BigDecimalStorage rhs -> { + BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); + BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromStorage(rhs); + yield runBigDecimalZip(left, right, problemAggregator); + } default -> throw new IllegalStateException( "Unsupported storage: " + arg.getClass().getCanonicalName()); }; case BigIntegerStorage lhs -> { - BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); yield switch (arg) { case AbstractLongStorage rhs -> { + BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); yield runBigIntegerZip(left, right, problemAggregator); } case BigIntegerStorage rhs -> { + BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs); BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); yield runBigIntegerZip(left, right, problemAggregator); } case DoubleStorage rhs -> runDoubleZip( DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); + case BigDecimalStorage rhs -> { + BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); + BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromStorage(rhs); + yield runBigDecimalZip(left, right, problemAggregator); + } default -> throw new IllegalStateException( "Unsupported storage: " + arg.getClass().getCanonicalName()); }; } + case BigDecimalStorage lhs -> { + BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromStorage(lhs); + BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromAnyStorage(arg); + yield runBigDecimalZip(left, right, problemAggregator); + } + default -> throw new IllegalStateException( "Unsupported storage: " + storage.getClass().getCanonicalName()); }; @@ -276,4 +316,45 @@ protected BigIntegerStorage runBigIntegerMap( return new BigIntegerStorage(out, n); } + + protected BigDecimalStorage runBigDecimalZip( + BigDecimalArrayAdapter a, + BigDecimalArrayAdapter b, + MapOperationProblemAggregator problemAggregator) { + Context context = Context.getCurrent(); + int n = a.size(); + int m = Math.min(a.size(), b.size()); + BigDecimal[] out = new BigDecimal[n]; + for (int i = 0; i < m; i++) { + BigDecimal x = a.getItem(i); + BigDecimal y = b.getItem(i); + if (x != null && y != null) { + BigDecimal r = doBigDecimal(x, y, i, problemAggregator); + out[i] = r; + } + context.safepoint(); + } + + return new BigDecimalStorage(out, n); + } + + protected BigDecimalStorage runBigDecimalMap( + BigDecimalArrayAdapter a, BigDecimal b, MapOperationProblemAggregator problemAggregator) { + Context context = Context.getCurrent(); + int n = a.size(); + BigDecimal[] out = new BigDecimal[n]; + for (int i = 0; i < n; i++) { + BigDecimal x = a.getItem(i); + if (x == null || b == null) { + out[i] = null; + } else { + BigDecimal r = doBigDecimal(x, b, i, problemAggregator); + out[i] = r; + } + + context.safepoint(); + } + + return new BigDecimalStorage(out, n); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java new file mode 100644 index 000000000000..cec24ce3687d --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningBigDecimal.java @@ -0,0 +1,62 @@ +package org.enso.table.data.column.operation.map.numeric.arithmetic; + +import static org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter.fromAnyStorage; + +import java.math.BigDecimal; +import java.math.BigInteger; +import org.enso.base.polyglot.NumericConverter; +import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; +import org.enso.table.data.column.operation.map.numeric.helpers.BigDecimalArrayAdapter; +import org.enso.table.data.column.storage.Storage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; + +public abstract class NumericBinaryOpReturningBigDecimal< + T extends Number, I extends Storage> + extends NumericBinaryOpImplementation { + public NumericBinaryOpReturningBigDecimal(String name) { + super(name); + } + + @Override + public Storage runBinaryMap( + I storage, Object arg, MapOperationProblemAggregator problemAggregator) { + if (arg == null) { + return BigDecimalStorage.makeEmpty(storage.size()); + } + + BigDecimalArrayAdapter lhs = fromAnyStorage(storage); + BigDecimal rhs = NumericConverter.coerceToBigDecimal(arg); + return runBigDecimalMap(lhs, rhs, problemAggregator); + } + + @Override + public Storage runZip( + I storage, Storage arg, MapOperationProblemAggregator problemAggregator) { + BigDecimalArrayAdapter left = BigDecimalArrayAdapter.fromAnyStorage(storage); + BigDecimalArrayAdapter right = BigDecimalArrayAdapter.fromAnyStorage(arg); + return runBigDecimalZip(left, right, problemAggregator); + } + + @Override + public Long doLong(long a, long b, int ix, MapOperationProblemAggregator problemAggregator) { + throw new IllegalStateException( + "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" + + " doBigDecimal branch."); + } + + @Override + public BigInteger doBigInteger( + BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { + throw new IllegalStateException( + "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" + + " doBigDecimal branch."); + } + + @Override + public double doDouble( + double a, double b, int ix, MapOperationProblemAggregator problemAggregator) { + throw new IllegalStateException( + "Impossible: should not reach here - a NumericOpReturningBigDecimal should always use the" + + " doBigDecimal branch."); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java index 6a0c91bcfff1..d256a9a58a85 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java @@ -2,6 +2,7 @@ import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.base.polyglot.NumericConverter; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; @@ -52,4 +53,12 @@ public BigInteger doBigInteger( "Impossible: should not reach here - a NumericOpReturningDouble should always use the" + " doDouble branch."); } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + throw new IllegalStateException( + "Impossible: should not reach here - a NumericOpReturningDouble should always use the" + + " doDouble branch."); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/SubOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/SubOp.java index 1620e599a4cf..d00712e8a2c5 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/SubOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/SubOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.storage.Storage; @@ -32,4 +33,10 @@ public BigInteger doBigInteger( BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { return a.subtract(b); } + + @Override + public BigDecimal doBigDecimal( + BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) { + return a.subtract(b); + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java new file mode 100644 index 000000000000..88f8f6e1bd26 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigDecimalArrayAdapter.java @@ -0,0 +1,124 @@ +package org.enso.table.data.column.operation.map.numeric.helpers; + +import java.math.BigDecimal; +import org.enso.table.data.column.storage.SpecializedStorage; +import org.enso.table.data.column.storage.Storage; +import org.enso.table.data.column.storage.numeric.AbstractLongStorage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; +import org.enso.table.data.column.storage.numeric.BigIntegerStorage; +import org.enso.table.data.column.storage.numeric.DoubleStorage; + +public interface BigDecimalArrayAdapter { + BigDecimal getItem(int i); + + int size(); + + static BigDecimalArrayAdapter fromStorage(SpecializedStorage storage) { + return new BigDecimalStorageAsBigDecimal(storage); + } + + static BigDecimalArrayAdapter fromStorage(BigIntegerStorage storage) { + return new BigIntegerStorageAsBigDecimal(storage); + } + + static BigDecimalArrayAdapter fromStorage(AbstractLongStorage storage) { + return new LongStorageAsBigDecimal(storage); + } + + static BigDecimalArrayAdapter fromStorage(DoubleStorage storage) { + return new DoubleStorageAsBigDecimal(storage); + } + + static BigDecimalArrayAdapter fromAnyStorage(Storage storage) { + return switch (storage) { + case DoubleStorage s -> fromStorage(s); + case AbstractLongStorage s -> fromStorage(s); + case BigIntegerStorage s -> fromStorage(s); + case BigDecimalStorage s -> fromStorage(s); + default -> throw new IllegalStateException( + "Unsupported storage: " + storage.getClass().getCanonicalName()); + }; + } + + class BigDecimalStorageAsBigDecimal implements BigDecimalArrayAdapter { + private final SpecializedStorage storage; + + private BigDecimalStorageAsBigDecimal(SpecializedStorage storage) { + this.storage = storage; + } + + @Override + public BigDecimal getItem(int i) { + return storage.getItemBoxed(i); + } + + @Override + public int size() { + return storage.size(); + } + } + + class BigIntegerStorageAsBigDecimal implements BigDecimalArrayAdapter { + private final BigIntegerStorage storage; + + private BigIntegerStorageAsBigDecimal(BigIntegerStorage storage) { + this.storage = storage; + } + + @Override + public BigDecimal getItem(int i) { + return new BigDecimal(storage.getItemBoxed(i)); + } + + @Override + public int size() { + return storage.size(); + } + } + + class LongStorageAsBigDecimal implements BigDecimalArrayAdapter { + private final AbstractLongStorage storage; + + private LongStorageAsBigDecimal(AbstractLongStorage storage) { + this.storage = storage; + } + + @Override + public BigDecimal getItem(int i) { + if (storage.isNothing(i)) { + return null; + } else { + long x = storage.getItem(i); + return BigDecimal.valueOf(x); + } + } + + @Override + public int size() { + return storage.size(); + } + } + + class DoubleStorageAsBigDecimal implements BigDecimalArrayAdapter { + private final DoubleStorage storage; + + private DoubleStorageAsBigDecimal(DoubleStorage storage) { + this.storage = storage; + } + + @Override + public BigDecimal getItem(int i) { + if (storage.isNothing(i)) { + return null; + } else { + double x = storage.getItemAsDouble(i); + return BigDecimal.valueOf(x); + } + } + + @Override + public int size() { + return storage.size(); + } + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java index aa28de2128a9..1807659058bd 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java @@ -1,8 +1,10 @@ package org.enso.table.data.column.operation.map.numeric.helpers; +import java.math.BigDecimal; import java.math.BigInteger; import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.numeric.AbstractLongStorage; +import org.enso.table.data.column.storage.numeric.BigDecimalStorage; import org.enso.table.data.column.storage.numeric.BigIntegerStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage; @@ -17,6 +19,10 @@ static DoubleArrayAdapter fromStorage(BigIntegerStorage storage) { return new BigIntegerStorageAsDouble(storage); } + static DoubleArrayAdapter fromStorage(BigDecimalStorage storage) { + return new BigDecimalStorageAsDouble(storage); + } + static DoubleArrayAdapter fromStorage(AbstractLongStorage storage) { return new LongStorageAsDouble(storage); } @@ -30,6 +36,7 @@ static DoubleArrayAdapter fromAnyStorage(Storage storage) { case DoubleStorage s -> fromStorage(s); case AbstractLongStorage s -> fromStorage(s); case BigIntegerStorage s -> fromStorage(s); + case BigDecimalStorage s -> fromStorage(s); default -> throw new IllegalStateException( "Unsupported storage: " + storage.getClass().getCanonicalName()); }; @@ -82,4 +89,28 @@ public int size() { return storage.size(); } } + + class BigDecimalStorageAsDouble implements DoubleArrayAdapter { + private final BigDecimalStorage storage; + + private BigDecimalStorageAsDouble(BigDecimalStorage storage) { + this.storage = storage; + } + + @Override + public double getItemAsDouble(int i) { + BigDecimal x = storage.getItem(i); + return x.doubleValue(); + } + + @Override + public boolean isNothing(long i) { + return storage.getItem(i) == null; + } + + @Override + public int size() { + return storage.size(); + } + } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigDecimalStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigDecimalStorage.java new file mode 100644 index 000000000000..3f56d0b42420 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/BigDecimalStorage.java @@ -0,0 +1,64 @@ +package org.enso.table.data.column.storage.numeric; + +import java.math.BigDecimal; +import org.enso.table.data.column.operation.map.MapOperationStorage; +import org.enso.table.data.column.operation.map.numeric.arithmetic.AddOp; +import org.enso.table.data.column.operation.map.numeric.arithmetic.BigDecimalDivideOp; +import org.enso.table.data.column.operation.map.numeric.arithmetic.ModOp; +import org.enso.table.data.column.operation.map.numeric.arithmetic.MulOp; +import org.enso.table.data.column.operation.map.numeric.arithmetic.PowerOp; +import org.enso.table.data.column.operation.map.numeric.arithmetic.SubOp; +import org.enso.table.data.column.operation.map.numeric.comparisons.EqualsComparison; +import org.enso.table.data.column.operation.map.numeric.comparisons.GreaterComparison; +import org.enso.table.data.column.operation.map.numeric.comparisons.GreaterOrEqualComparison; +import org.enso.table.data.column.operation.map.numeric.comparisons.LessComparison; +import org.enso.table.data.column.operation.map.numeric.comparisons.LessOrEqualComparison; +import org.enso.table.data.column.storage.ObjectStorage; +import org.enso.table.data.column.storage.SpecializedStorage; +import org.enso.table.data.column.storage.type.BigDecimalType; +import org.enso.table.data.column.storage.type.StorageType; + +public final class BigDecimalStorage extends SpecializedStorage { + /** + * @param data the underlying data + * @param size the number of items stored + */ + public BigDecimalStorage(BigDecimal[] data, int size) { + super(data, size, buildOps()); + } + + public static BigDecimalStorage makeEmpty(int size) { + return new BigDecimalStorage(new BigDecimal[size], size); + } + + private static MapOperationStorage> buildOps() { + MapOperationStorage> ops = + ObjectStorage.buildObjectOps(); + return ops.add(new AddOp<>()) + .add(new SubOp<>()) + .add(new MulOp<>()) + .add(new BigDecimalDivideOp<>()) + .add(new PowerOp<>()) + .add(new ModOp<>()) + .add(new LessComparison<>()) + .add(new LessOrEqualComparison<>()) + .add(new EqualsComparison<>()) + .add(new GreaterOrEqualComparison<>()) + .add(new GreaterComparison<>()); + } + + @Override + protected SpecializedStorage newInstance(BigDecimal[] data, int size) { + return new BigDecimalStorage(data, size); + } + + @Override + protected BigDecimal[] newUnderlyingArray(int size) { + return new BigDecimal[size]; + } + + @Override + public StorageType getType() { + return BigDecimalType.INSTANCE; + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/BigDecimalType.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/BigDecimalType.java new file mode 100644 index 000000000000..84de26444c51 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/BigDecimalType.java @@ -0,0 +1,20 @@ +package org.enso.table.data.column.storage.type; + +public record BigDecimalType() implements StorageType { + public static final BigDecimalType INSTANCE = new BigDecimalType(); + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean hasDate() { + return false; + } + + @Override + public boolean hasTime() { + return false; + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/StorageType.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/StorageType.java index 69ef0e264146..35224aa4b2f8 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/StorageType.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/type/StorageType.java @@ -13,6 +13,7 @@ */ public sealed interface StorageType permits AnyObjectType, + BigDecimalType, BigIntegerType, BooleanType, DateTimeType, diff --git a/test/Base_Tests/src/Data/Decimal_Spec.enso b/test/Base_Tests/src/Data/Decimal_Spec.enso index 5d31eeeb205f..f71277f936f8 100644 --- a/test/Base_Tests/src/Data/Decimal_Spec.enso +++ b/test/Base_Tests/src/Data/Decimal_Spec.enso @@ -651,6 +651,9 @@ add_specs suite_builder = Decimal.new "12" . div (Decimal.new "0") . should_fail_with Arithmetic_Error + nt_error = Arithmetic_Error.Error "Non-terminating decimal expansion; no exact representable decimal result. Please use `.divide` with an explicit `Math_Context` to limit the numeric precision." + ((Decimal.new "1") / (Decimal.new "3")) . should_fail_with nt_error + suite_builder.group "pow" group_builder-> group_builder.specify "should define pow" <| Decimal.new "10" . pow 3 . should_equal 1000 diff --git a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso index 8931cd1429bc..8ab285c458bf 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso @@ -16,7 +16,6 @@ import Standard.Database.DB_Table.DB_Table from Standard.Test import all - import enso_dev.Base_Tests.Data.Round_Spec from project.Common_Table_Operations.Util import run_default_backend @@ -1789,6 +1788,91 @@ add_specs suite_builder setup = c.value_type . should_equal Value_Type.Mixed (empty.set c).at c.name . value_type . should_equal Value_Type.Mixed + decimal_db_pending = if setup.is_database then "Decimals are currently not implemented for the Database backend." + suite_builder.group prefix+"Decimal" pending=decimal_db_pending group_builder-> + data = Data.setup create_connection_fn + + group_builder.teardown <| + data.teardown + + table_builder cols = + setup.table_builder cols connection=data.connection + + group_builder.specify "can store and retrieve values" <| + t = table_builder [["x", [Decimal.new "23257245345.345345345"]]] + t.at "x" . at 0 . should_be_a Decimal + t.at "x" . get 0 . should_be_a Decimal + + group_builder.specify "arithmetic (decimal column and decimal column)" <| + t = table_builder [["x", [Decimal.new "23257245345.345345345"]], ["y", [Decimal.new "123e50"]], ["z", [Decimal.new "125e50"]], ["w", [Decimal.new 7513]], ["v", [Decimal.new "3.7"]]] + (t.at "x" + t.at "y").to_vector . should_equal [Decimal.new "12300000000000000000000000000000000000000023257245345.345345345"] + (t.at "x" - t.at "y").to_vector . should_equal [Decimal.new "-12299999999999999999999999999999999999999976742754654.654654655"] + (t.at "x" * t.at "y").to_vector . should_equal [Decimal.new "2.860641177477477477435E+62"] + (t.at "x" / t.at "z").to_vector . should_equal [Decimal.new "1.8605796276276276276E-42"] + (t.at "x" % t.at "w").to_vector . should_equal [Decimal.new "2545.345345345"] + (t.at "x" ^ t.at "v").to_vector . should_equal [Decimal.new "2.27125352907938E38" . to_float] + + group_builder.specify "arithmetic (decimal column and non-decimal column)" <| + t = table_builder [["x", [Decimal.new "101.25"]], ["y", [30]], ["z", [40.5]], ["w", [2]], ["wf", [2.0]], ["wf2", [2.1]]] + + (t.at "x" + t.at "y").to_vector . should_equal [Decimal.new "131.25"] + (t.at "x" - t.at "y").to_vector . should_equal [Decimal.new "71.25"] + (t.at "x" * t.at "y").to_vector . should_equal [Decimal.new "3037.5"] + (t.at "x" / t.at "y").to_vector . should_equal [Decimal.new "3.375"] + (t.at "x" % t.at "y").to_vector . should_equal [Decimal.new "11.25"] + + (t.at "x" + t.at "z").to_vector . should_equal [Decimal.new "141.75"] + (t.at "x" - t.at "z").to_vector . should_equal [Decimal.new "60.75"] + (t.at "x" * t.at "z").to_vector . should_equal [Decimal.new "4100.625"] + (t.at "x" / t.at "z").to_vector . should_equal [Decimal.new "2.5"] + (t.at "x" % t.at "z").to_vector . should_equal [Decimal.new "20.25"] + + (t.at "x" ^ t.at "w").to_vector . should_equal [Decimal.new "10251.5625"] + (t.at "x" ^ t.at "wf").to_vector . should_equal [Decimal.new "10251.5625"] + (t.at "x" ^ t.at "wf2").to_vector . should_equal [16267.827812994828] + + group_builder.specify "arithmetic (column and scalar)" <| + t = table_builder [["x", [Decimal.new "23257245345.345345345"]], ["y", [Decimal.new "944548245.68648775"]]] + + (t.at "x" + 10) . to_vector . should_equal [Decimal.new "23257245355.345345345"] + (t.at "x" - 10) . to_vector . should_equal [Decimal.new "23257245335.345345345"] + (t.at "x" * 10) . to_vector . should_equal [Decimal.new "232572453453.45345345"] + (t.at "x" / 10) . to_vector . should_equal [Decimal.new "2325724534.5345345345"] + (t.at "x" ^ 2) . to_vector . should_equal [Decimal.new "5.408994610535877E20" . to_float] + + (t.at "x" + 10.1) . to_vector . should_equal [Decimal.new "23257245355.445345345"] + (t.at "x" - 10.1) . to_vector . should_equal [Decimal.new "23257245335.245345345"] + (t.at "x" * 10.1) . to_vector . should_equal [Decimal.new "234898177987.9879879845"] + (t.at "y" / 16.5) . to_vector . should_equal [Decimal.new "57245348.2234235"] + (t.at "x" ^ 2.0) . to_vector . should_equal [Decimal.new "5.408994610535877E20" . to_float] + + (t.at "x" + 2^80) . to_vector . should_equal [Decimal.new "1208925819614652431951521.345345345"] + (t.at "x" - 2^80) . to_vector . should_equal [Decimal.new "-1208925819614605917460830.654654655"] + (t.at "x" * 2^80) . to_vector . should_equal [Decimal.new "28116284391100140971590625398136689.624350720"] + (t.at "y" / 2^80) . to_vector . should_equal [Decimal.new "0.0000000000000007813119964528366172913694049826337229003314632791443727910518646240234375"] + + (t.at "x" + (Decimal.new "10.1")) . to_vector . should_equal [Decimal.new "23257245355.445345345"] + (t.at "x" - (Decimal.new "10.1")) . to_vector . should_equal [Decimal.new "23257245335.245345345"] + (t.at "x" * (Decimal.new "10.1")) . to_vector . should_equal [Decimal.new "234898177987.9879879845"] + (t.at "y" / (Decimal.new "16.5")) . to_vector . should_equal [Decimal.new "57245348.2234235"] + (t.at "x" ^ (Decimal.new "2.0")) . to_vector . should_equal [Decimal.new "5.408994610535877E20" . to_float] + + group_builder.specify "arithmetic errors" <| + t = table_builder [["x", [Decimal.new "1"]], ["y", [Decimal.new "3"]], ["z", [Decimal.new "0"]]] + + r0 = t.at "x" / t.at "y" + r0 . to_vector . should_equal [Nothing] + nt_error = Arithmetic_Error.Error "Non-terminating decimal expansion; no exact representable decimal result. Please use `.divide` with an explicit `Math_Context` to limit the numeric precision. (at rows [0])." + Problems.expect_only_warning nt_error r0 + + r1 = t.at "x" / t.at "z" + r1 . to_vector . should_equal [Nothing] + Problems.expect_only_warning Arithmetic_Error r1 + + r2 = t.at "x" % t.at "z" + r2 . to_vector . should_equal [Nothing] + Problems.expect_only_warning Arithmetic_Error r2 + # A dummy value used to force the in-memory backend to trigger a infer a mixed type for the given column. type Mixed_Type_Object diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 08ae64eb3c34..23838d89ff7b 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -2,6 +2,7 @@ from Standard.Base import all import project.Util +import Standard.Base.Data.Vector.Map_Error import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Common.Type_Error @@ -95,7 +96,7 @@ add_specs suite_builder = if x == 1 then Error.throw "X" else x col = Column.from_vector "Test" [foo 0, foo 1, foo 2] col . should_fail_with Text - col.catch . should_equal "X" + col.catch . should_equal (Map_Error.Error 1 'X') group_builder.specify "should not allow invalid column names" <| c1 = Column.from_vector "" [1, 2, 3]