BigDecimalBuilder and arithmetic operations. (#9950)

* hack

* make a column

* add

* no scale=0 on BD type

* a test

* wip

* 3 arithmetic ops

* /

* wip

* BigDecimalPowerOp

* wip

* mod test

* NumericBinaryOpReturningBigDecimal

* with scalar

* misc arithmetic tests

* fix integralBigDecimalToInteger

* mixed columns

* bigdecimal pow via double

* cleanup

* j2e on get

* arithmetic exception

* mod 0

* cleanup

* fmt

* changelog

* check type first

* merge

* mc error message

* add BD case to Builder.java

* fmt

* changelog

* add BD case to StorageConverter.java

* fmt

* fix test
This commit is contained in:
GregoryTravis 2024-06-04 13:59:31 -04:00 committed by GitHub
parent 106007cb89
commit 5fad3558a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 671 additions and 19 deletions

View File

@ -5,9 +5,12 @@
- [Added Statistic.Product][10122] - [Added Statistic.Product][10122]
- [Added Encoding.Default that tries to detect UTF-8 or UTF-16 encoding based on - [Added Encoding.Default that tries to detect UTF-8 or UTF-16 encoding based on
BOM][10130] BOM][10130]
- [Added `Decimal` column to the in-memory database, with some arithmetic
operations.][9950]
[debug-shortcuts]: [debug-shortcuts]:
[9950]: https://github.com/enso-org/enso/pull/9950
[10122]: https://github.com/enso-org/enso/pull/10122 [10122]: https://github.com/enso-org/enso/pull/10122
[10130]: https://github.com/enso-org/enso/pull/10130 [10130]: https://github.com/enso-org/enso/pull/10130

View File

@ -499,7 +499,8 @@ type Decimal
# => Decimal.new 45.7 # => Decimal.new 45.7
divide : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error divide : Decimal -> Math_Context | Nothing -> Decimal ! Arithmetic_Error
divide self (that : Decimal) (math_context : Math_Context | Nothing = 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 case math_context of
Nothing -> Decimal.Value (self.big_decimal.divide that.big_decimal) Nothing -> Decimal.Value (self.big_decimal.divide that.big_decimal)
_ -> Decimal.Value (self.big_decimal.divide that.big_decimal math_context.math_context) _ -> 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 Warning.attach (Loss_Of_Numeric_Precision.Warning x value) value
## PRIVATE ## PRIVATE
handle_java_exception ~action = handle_java_exception ~action (extra_message : Text = "") =
Panic.catch ArithmeticException action caught_panic-> 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 ## PRIVATE
handle_unsupported_argument_types ~action = handle_unsupported_argument_types ~action =

View File

@ -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.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.Column_Format import all
from project.Internal.Java_Exports import make_date_builder_adapter, make_string_builder 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.base.Time_Utils
polyglot java import org.enso.table.data.column.operation.cast.CastProblemAggregator polyglot java import org.enso.table.data.column.operation.cast.CastProblemAggregator
@ -89,6 +90,12 @@ type Column
Value_Type.Boolean -> False Value_Type.Boolean -> False
Value_Type.Byte -> False Value_Type.Byte -> False
_ -> True _ -> 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 expected_storage_type = case value_type of
Auto -> Nothing Auto -> Nothing
_ -> Storage.from_value_type value_type on_problems=Problem_Behavior.Report_Warning _ -> 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 <| 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-> java_column = Java_Problems.with_problem_aggregator Problem_Behavior.Report_Warning java_problem_aggregator->
case needs_polyglot_conversion of 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 False -> Java_Column.fromItemsNoDateConversion name items expected_storage_type java_problem_aggregator
result = Column.Value java_column . throw_on_warning Conversion_Failure result = Column.Value java_column . throw_on_warning Conversion_Failure
result.catch Conversion_Failure error-> result.catch Conversion_Failure error->
@ -684,8 +691,9 @@ type Column
example_div = Examples.decimal_column ^ Examples.integer_column example_div = Examples.decimal_column ^ Examples.integer_column
^ : Column | Any -> Column ^ : Column | Any -> Column
^ self other = ^ self other =
Value_Type_Helpers.check_binary_numeric_op self other <| Illegal_Argument.handle_java_exception <|
run_vectorized_binary_op self Java_Storage.Maps.POWER other Value_Type_Helpers.check_binary_numeric_op self other <|
run_vectorized_binary_op self Java_Storage.Maps.POWER other
## ALIAS and ## ALIAS and
GROUP Standard.Base.Operators GROUP Standard.Base.Operators
@ -2187,7 +2195,7 @@ type Column
if valid_index.not then default else if valid_index.not then default else
storage = self.java_column.getStorage storage = self.java_column.getStorage
if storage.isNothing index then Nothing else if storage.isNothing index then Nothing else
storage.getItem index java_to_enso <| storage.getItem index
## ICON data_input ## ICON data_input
Returns a column containing rows of this column. Returns a column containing rows of this column.
@ -2211,7 +2219,7 @@ type Column
example_to_vector = Examples.integer_column.to_vector example_to_vector = Examples.integer_column.to_vector
to_vector : 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 ## GROUP Standard.Base.Metadata
ICON 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 s1 = column.java_column.getStorage
rs = Polyglot_Helpers.handle_polyglot_dataflow_errors <| 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) Column.Value (Java_Column.new effective_new_name rs)
## PRIVATE ## PRIVATE

View File

@ -8,8 +8,11 @@ import project.Value_Type.Bits
import project.Value_Type.Value_Type import project.Value_Type.Value_Type
from project.Errors import Inexact_Type_Coercion 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.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.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.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.Bits as Java_Bits
polyglot java import org.enso.table.data.column.storage.type.BooleanType 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 _ : DateType -> Value_Type.Date
_ : DateTimeType -> Value_Type.Date_Time with_timezone=True _ : DateTimeType -> Value_Type.Date_Time with_timezone=True
_ : TimeOfDayType -> Value_Type.Time _ : TimeOfDayType -> Value_Type.Time
_ : BigDecimalType -> Value_Type.Decimal
_ : BigIntegerType -> Value_Type.Decimal scale=0 _ : BigIntegerType -> Value_Type.Decimal scale=0
_ : AnyObjectType -> Value_Type.Mixed _ : AnyObjectType -> Value_Type.Mixed
@ -64,11 +68,29 @@ closest_storage_type value_type = case value_type of
Value_Type.Mixed -> AnyObjectType.INSTANCE Value_Type.Mixed -> AnyObjectType.INSTANCE
Value_Type.Decimal _ scale -> Value_Type.Decimal _ scale ->
is_integer = scale.is_nothing || scale <= 0 is_integer = scale.is_nothing || scale <= 0
if is_integer then BigIntegerType.INSTANCE else 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 - only Decimal of integer type (scale <= 0) is supported. You may cast the column to Float first (lossy conversion).")
_ -> _ ->
Error.throw (Illegal_Argument.Error "Columns of type "+value_type.to_display_text+" are currently not supported in the in-memory backend.") 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 ## PRIVATE
Converts a value type to an in-memory storage type, possibly approximating it Converts a value type to an in-memory storage type, possibly approximating it
to the closest supported type. to the closest supported type.

View File

@ -27,6 +27,7 @@ most_specific_value_type : Any -> Boolean -> Value_Type
most_specific_value_type value use_smallest=False = most_specific_value_type value use_smallest=False =
case value of case value of
_ : Float -> Value_Type.Float Bits.Bits_64 _ : Float -> Value_Type.Float Bits.Bits_64
_ : Decimal -> Value_Type.Decimal
_ : Boolean -> Value_Type.Boolean _ : Boolean -> Value_Type.Boolean
_ : Date -> Value_Type.Date _ : Date -> Value_Type.Date
_ : Time_Of_Day -> Value_Type.Time _ : Time_Of_Day -> Value_Type.Time

View File

@ -59,6 +59,25 @@ public class NumericConverter {
} }
} }
/**
* Coerces a number to a BigDecimal.
*
* <p>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. */ /** Returns true if the object is any supported number. */
public static boolean isCoercibleToDouble(Object o) { public static boolean isCoercibleToDouble(Object o) {
return isFloatLike(o)|| isCoercibleToLong(o) || o instanceof BigInteger; return isFloatLike(o)|| isCoercibleToLong(o) || o instanceof BigInteger;
@ -66,7 +85,6 @@ public class NumericConverter {
public static boolean isFloatLike(Object o) { public static boolean isFloatLike(Object o) {
return o instanceof Double return o instanceof Double
|| o instanceof BigDecimal
|| o instanceof Float; || o instanceof Float;
} }

View File

@ -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<BigDecimal> {
@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<BigDecimal> doSeal() {
return new BigDecimalStorage(data, currentSize);
}
}

View File

@ -2,6 +2,7 @@ package org.enso.table.data.column.builder;
import org.enso.table.data.column.storage.Storage; 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.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.BigIntegerType;
import org.enso.table.data.column.storage.type.BooleanType; import org.enso.table.data.column.storage.type.BooleanType;
import org.enso.table.data.column.storage.type.DateTimeType; import org.enso.table.data.column.storage.type.DateTimeType;
@ -38,6 +39,7 @@ public abstract class Builder {
case IntegerType integerType -> NumericBuilder.createLongBuilder( case IntegerType integerType -> NumericBuilder.createLongBuilder(
size, integerType, problemAggregator); size, integerType, problemAggregator);
case TextType textType -> new StringBuilder(size, textType); case TextType textType -> new StringBuilder(size, textType);
case BigDecimalType x -> new BigDecimalBuilder(size);
case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator); case BigIntegerType x -> new BigIntegerBuilder(size, problemAggregator);
case null -> new InferredBuilder(size, problemAggregator); case null -> new InferredBuilder(size, problemAggregator);
}; };

View File

@ -7,7 +7,7 @@ import org.enso.table.data.column.storage.type.DateType;
import org.enso.table.data.column.storage.type.StorageType; import org.enso.table.data.column.storage.type.StorageType;
import org.enso.table.error.ValueTypeMismatchException; import org.enso.table.error.ValueTypeMismatchException;
/** A builder for string columns. */ /** A builder for LocalDate columns. */
public class DateBuilder extends TypedBuilderImpl<LocalDate> { public class DateBuilder extends TypedBuilderImpl<LocalDate> {
@Override @Override
protected LocalDate[] newArray(int size) { protected LocalDate[] newArray(int size) {

View File

@ -12,7 +12,7 @@ import org.enso.table.data.column.storage.type.StorageType;
import org.enso.table.error.ValueTypeMismatchException; import org.enso.table.error.ValueTypeMismatchException;
import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Context;
/** A builder for string columns. */ /** A builder for ZonedDateTime columns. */
public class DateTimeBuilder extends TypedBuilderImpl<ZonedDateTime> { public class DateTimeBuilder extends TypedBuilderImpl<ZonedDateTime> {
@Override @Override
protected ZonedDateTime[] newArray(int size) { protected ZonedDateTime[] newArray(int size) {

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.builder; package org.enso.table.data.column.builder;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
@ -115,6 +116,8 @@ public class InferredBuilder extends Builder {
currentBuilder = new StringBuilder(initialCapacity, TextType.VARIABLE_LENGTH); currentBuilder = new StringBuilder(initialCapacity, TextType.VARIABLE_LENGTH);
} else if (o instanceof BigInteger) { } else if (o instanceof BigInteger) {
currentBuilder = new BigIntegerBuilder(initialCapacity, problemAggregator); currentBuilder = new BigIntegerBuilder(initialCapacity, problemAggregator);
} else if (o instanceof BigDecimal) {
currentBuilder = new BigDecimalBuilder(initialCapacity);
} else if (o instanceof LocalDate) { } else if (o instanceof LocalDate) {
currentBuilder = new DateBuilder(initialCapacity); currentBuilder = new DateBuilder(initialCapacity);
} else if (o instanceof LocalTime) { } else if (o instanceof LocalTime) {

View File

@ -7,7 +7,7 @@ import org.enso.table.data.column.storage.type.StorageType;
import org.enso.table.data.column.storage.type.TimeOfDayType; import org.enso.table.data.column.storage.type.TimeOfDayType;
import org.enso.table.error.ValueTypeMismatchException; import org.enso.table.error.ValueTypeMismatchException;
/** A builder for string columns. */ /** A builder for LocalTime columns. */
public class TimeOfDayBuilder extends TypedBuilderImpl<LocalTime> { public class TimeOfDayBuilder extends TypedBuilderImpl<LocalTime> {
@Override @Override
protected LocalTime[] newArray(int size) { protected LocalTime[] newArray(int size) {

View File

@ -2,6 +2,7 @@ package org.enso.table.data.column.operation.cast;
import org.enso.table.data.column.storage.Storage; 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.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.BigIntegerType;
import org.enso.table.data.column.storage.type.BooleanType; import org.enso.table.data.column.storage.type.BooleanType;
import org.enso.table.data.column.storage.type.DateTimeType; import org.enso.table.data.column.storage.type.DateTimeType;
@ -29,6 +30,8 @@ public interface StorageConverter<T> {
case TextType textType -> new ToTextStorageConverter(textType); case TextType textType -> new ToTextStorageConverter(textType);
case TimeOfDayType timeOfDayType -> new ToTimeOfDayStorageConverter(); case TimeOfDayType timeOfDayType -> new ToTimeOfDayStorageConverter();
case BigIntegerType bigIntegerType -> new ToBigIntegerConverter(); case BigIntegerType bigIntegerType -> new ToBigIntegerConverter();
case BigDecimalType bigDecimalType -> throw new UnsupportedOperationException(
"Conversion to BigDecimal is not yet supported.");
}; };
} }
} }

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.operation.map.numeric.arithmetic; package org.enso.table.data.column.operation.map.numeric.arithmetic;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.Storage;
@ -32,4 +33,10 @@ public class AddOp<T extends Number, I extends Storage<? super T>>
BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.add(b); return a.add(b);
} }
@Override
public BigDecimal doBigDecimal(
BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.add(b);
}
} }

View File

@ -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<T extends Number, I extends Storage<? super T>>
extends NumericBinaryOpReturningBigDecimal<T, I> {
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;
}
}
}

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.operation.map.numeric.arithmetic; package org.enso.table.data.column.operation.map.numeric.arithmetic;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.Storage;
@ -40,4 +41,15 @@ public class ModOp<T extends Number, I extends Storage<? super T>>
return a.mod(b); 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);
}
} }

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.operation.map.numeric.arithmetic; package org.enso.table.data.column.operation.map.numeric.arithmetic;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.Storage;
@ -32,4 +33,10 @@ public class MulOp<T extends Number, I extends Storage<? super T>>
BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.multiply(b); return a.multiply(b);
} }
@Override
public BigDecimal doBigDecimal(
BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.multiply(b);
}
} }

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.operation.map.numeric.arithmetic; package org.enso.table.data.column.operation.map.numeric.arithmetic;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
@ -10,4 +11,7 @@ public interface NumericBinaryOpDefinition {
BigInteger doBigInteger( BigInteger doBigInteger(
BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator); BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator);
BigDecimal doBigDecimal(
BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator);
} }

View File

@ -2,15 +2,18 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic;
import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage; import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.BitSet; import java.util.BitSet;
import org.enso.base.polyglot.NumericConverter; import org.enso.base.polyglot.NumericConverter;
import org.enso.table.data.column.operation.map.BinaryMapOperation; 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.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.BigIntegerArrayAdapter;
import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter; 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.Storage;
import org.enso.table.data.column.storage.numeric.AbstractLongStorage; 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.BigIntegerStorage;
import org.enso.table.data.column.storage.numeric.DoubleStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage;
import org.enso.table.data.column.storage.numeric.LongStorage; import org.enso.table.data.column.storage.numeric.LongStorage;
@ -41,6 +44,8 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator);
case BigIntegerStorage s -> runBigIntegerMap( case BigIntegerStorage s -> runBigIntegerMap(
BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator); BigIntegerArrayAdapter.fromStorage(s), rhs, problemAggregator);
case BigDecimalStorage s -> runBigDecimalMap(
BigDecimalArrayAdapter.fromStorage(s), new BigDecimal(rhs), problemAggregator);
case DoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemAggregator); case DoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemAggregator);
default -> throw new IllegalStateException( default -> throw new IllegalStateException(
"Unsupported storage: " + storage.getClass().getCanonicalName()); "Unsupported storage: " + storage.getClass().getCanonicalName());
@ -53,6 +58,10 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
BigIntegerArrayAdapter.fromStorage(s), BigIntegerArrayAdapter.fromStorage(s),
BigInteger.valueOf(argAsLong), BigInteger.valueOf(argAsLong),
problemAggregator); problemAggregator);
case BigDecimalStorage s -> runBigDecimalMap(
BigDecimalArrayAdapter.fromStorage(s),
BigDecimal.valueOf(argAsLong),
problemAggregator);
case DoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator); case DoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemAggregator);
default -> throw new IllegalStateException( default -> throw new IllegalStateException(
"Unsupported storage: " + storage.getClass().getCanonicalName()); "Unsupported storage: " + storage.getClass().getCanonicalName());
@ -64,10 +73,17 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator); DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator);
case BigIntegerStorage s -> runDoubleMap( case BigIntegerStorage s -> runDoubleMap(
DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator); DoubleArrayAdapter.fromStorage(s), doubleArg, problemAggregator);
case BigDecimalStorage s -> runBigDecimalMap(
BigDecimalArrayAdapter.fromStorage(s),
BigDecimal.valueOf(doubleArg),
problemAggregator);
case DoubleStorage s -> runDoubleMap(s, doubleArg, problemAggregator); case DoubleStorage s -> runDoubleMap(s, doubleArg, problemAggregator);
default -> throw new IllegalStateException( default -> throw new IllegalStateException(
"Unsupported storage: " + storage.getClass().getCanonicalName()); "Unsupported storage: " + storage.getClass().getCanonicalName());
}; };
} else if (arg instanceof BigDecimal bd) {
return runBigDecimalMap(
BigDecimalArrayAdapter.fromAnyStorage(storage), bd, problemAggregator);
} else { } else {
throw new UnexpectedTypeException("a Number."); throw new UnexpectedTypeException("a Number.");
} }
@ -78,7 +94,14 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
public Storage<? extends Number> runZip( public Storage<? extends Number> runZip(
I storage, Storage<?> arg, MapOperationProblemAggregator problemAggregator) { I storage, Storage<?> arg, MapOperationProblemAggregator problemAggregator) {
return switch (storage) { 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 lhs -> switch (arg) {
case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator); case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemAggregator);
@ -89,28 +112,45 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
} }
case DoubleStorage rhs -> runDoubleZip( case DoubleStorage rhs -> runDoubleZip(
DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); 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( default -> throw new IllegalStateException(
"Unsupported storage: " + arg.getClass().getCanonicalName()); "Unsupported storage: " + arg.getClass().getCanonicalName());
}; };
case BigIntegerStorage lhs -> { case BigIntegerStorage lhs -> {
BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
yield switch (arg) { yield switch (arg) {
case AbstractLongStorage rhs -> { case AbstractLongStorage rhs -> {
BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
yield runBigIntegerZip(left, right, problemAggregator); yield runBigIntegerZip(left, right, problemAggregator);
} }
case BigIntegerStorage rhs -> { case BigIntegerStorage rhs -> {
BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs); BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
yield runBigIntegerZip(left, right, problemAggregator); yield runBigIntegerZip(left, right, problemAggregator);
} }
case DoubleStorage rhs -> runDoubleZip( case DoubleStorage rhs -> runDoubleZip(
DoubleArrayAdapter.fromStorage(lhs), rhs, problemAggregator); 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( default -> throw new IllegalStateException(
"Unsupported storage: " + arg.getClass().getCanonicalName()); "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( default -> throw new IllegalStateException(
"Unsupported storage: " + storage.getClass().getCanonicalName()); "Unsupported storage: " + storage.getClass().getCanonicalName());
}; };
@ -276,4 +316,45 @@ public abstract class NumericBinaryOpImplementation<T extends Number, I extends
return new BigIntegerStorage(out, n); 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);
}
} }

View File

@ -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<T, I> {
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.");
}
}

View File

@ -2,6 +2,7 @@ package org.enso.table.data.column.operation.map.numeric.arithmetic;
import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage; import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.base.polyglot.NumericConverter; import org.enso.base.polyglot.NumericConverter;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
@ -52,4 +53,12 @@ public abstract class NumericBinaryOpReturningDouble<T extends Number, I extends
"Impossible: should not reach here - a NumericOpReturningDouble should always use the" "Impossible: should not reach here - a NumericOpReturningDouble should always use the"
+ " doDouble branch."); + " 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.");
}
} }

View File

@ -1,5 +1,6 @@
package org.enso.table.data.column.operation.map.numeric.arithmetic; package org.enso.table.data.column.operation.map.numeric.arithmetic;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.operation.map.MapOperationProblemAggregator; import org.enso.table.data.column.operation.map.MapOperationProblemAggregator;
import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.Storage;
@ -32,4 +33,10 @@ public class SubOp<T extends Number, I extends Storage<? super T>>
BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) { BigInteger a, BigInteger b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.subtract(b); return a.subtract(b);
} }
@Override
public BigDecimal doBigDecimal(
BigDecimal a, BigDecimal b, int ix, MapOperationProblemAggregator problemAggregator) {
return a.subtract(b);
}
} }

View File

@ -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<BigDecimal> 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<BigDecimal> storage;
private BigDecimalStorageAsBigDecimal(SpecializedStorage<BigDecimal> 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();
}
}
}

View File

@ -1,8 +1,10 @@
package org.enso.table.data.column.operation.map.numeric.helpers; package org.enso.table.data.column.operation.map.numeric.helpers;
import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.enso.table.data.column.storage.Storage; 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.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.BigIntegerStorage;
import org.enso.table.data.column.storage.numeric.DoubleStorage; import org.enso.table.data.column.storage.numeric.DoubleStorage;
@ -17,6 +19,10 @@ public interface DoubleArrayAdapter {
return new BigIntegerStorageAsDouble(storage); return new BigIntegerStorageAsDouble(storage);
} }
static DoubleArrayAdapter fromStorage(BigDecimalStorage storage) {
return new BigDecimalStorageAsDouble(storage);
}
static DoubleArrayAdapter fromStorage(AbstractLongStorage storage) { static DoubleArrayAdapter fromStorage(AbstractLongStorage storage) {
return new LongStorageAsDouble(storage); return new LongStorageAsDouble(storage);
} }
@ -30,6 +36,7 @@ public interface DoubleArrayAdapter {
case DoubleStorage s -> fromStorage(s); case DoubleStorage s -> fromStorage(s);
case AbstractLongStorage s -> fromStorage(s); case AbstractLongStorage s -> fromStorage(s);
case BigIntegerStorage s -> fromStorage(s); case BigIntegerStorage s -> fromStorage(s);
case BigDecimalStorage s -> fromStorage(s);
default -> throw new IllegalStateException( default -> throw new IllegalStateException(
"Unsupported storage: " + storage.getClass().getCanonicalName()); "Unsupported storage: " + storage.getClass().getCanonicalName());
}; };
@ -82,4 +89,28 @@ public interface DoubleArrayAdapter {
return storage.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();
}
}
} }

View File

@ -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<BigDecimal> {
/**
* @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<BigDecimal, SpecializedStorage<BigDecimal>> buildOps() {
MapOperationStorage<BigDecimal, SpecializedStorage<BigDecimal>> 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<BigDecimal> 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;
}
}

View File

@ -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;
}
}

View File

@ -13,6 +13,7 @@ import org.enso.base.polyglot.NumericConverter;
*/ */
public sealed interface StorageType public sealed interface StorageType
permits AnyObjectType, permits AnyObjectType,
BigDecimalType,
BigIntegerType, BigIntegerType,
BooleanType, BooleanType,
DateTimeType, DateTimeType,

View File

@ -651,6 +651,9 @@ add_specs suite_builder =
Decimal.new "12" . div (Decimal.new "0") . should_fail_with Arithmetic_Error 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-> suite_builder.group "pow" group_builder->
group_builder.specify "should define pow" <| group_builder.specify "should define pow" <|
Decimal.new "10" . pow 3 . should_equal 1000 Decimal.new "10" . pow 3 . should_equal 1000

View File

@ -16,7 +16,6 @@ import Standard.Database.DB_Table.DB_Table
from Standard.Test import all from Standard.Test import all
import enso_dev.Base_Tests.Data.Round_Spec import enso_dev.Base_Tests.Data.Round_Spec
from project.Common_Table_Operations.Util import run_default_backend 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 c.value_type . should_equal Value_Type.Mixed
(empty.set c).at c.name . 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. # 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 type Mixed_Type_Object

View File

@ -2,6 +2,7 @@ from Standard.Base import all
import project.Util import project.Util
import Standard.Base.Data.Vector.Map_Error
import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Arithmetic_Error
import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Common.Index_Out_Of_Bounds
import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Common.Type_Error
@ -95,7 +96,7 @@ add_specs suite_builder =
if x == 1 then Error.throw "X" else x if x == 1 then Error.throw "X" else x
col = Column.from_vector "Test" [foo 0, foo 1, foo 2] col = Column.from_vector "Test" [foo 0, foo 1, foo 2]
col . should_fail_with Text 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" <| group_builder.specify "should not allow invalid column names" <|
c1 = Column.from_vector "" [1, 2, 3] c1 = Column.from_vector "" [1, 2, 3]