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 extends Number> 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 extends Number> 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 extends Number> 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 extends Number> runBinaryMap(
public Storage extends Number> 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 extends Number> 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 super T>>
+ extends NumericBinaryOpImplementation {
+ public NumericBinaryOpReturningBigDecimal(String name) {
+ super(name);
+ }
+
+ @Override
+ public Storage extends Number> 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 extends Number> 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]