diff --git a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso
index a7a5b76db889..14e4823e18a8 100644
--- a/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso
+++ b/distribution/lib/Standard/AWS/0.0.0-dev/src/Database/Redshift/Internal/Redshift_Dialect.enso
@@ -1,9 +1,9 @@
from Standard.Base import all
-from Standard.Table import Aggregate_Column
-from Standard.Table import Value_Type
+from Standard.Table import Aggregate_Column, Value_Type
import Standard.Database.Connection.Connection.Connection
+import Standard.Database.Data.Column.Column
import Standard.Database.Data.Dialect
import Standard.Database.Data.SQL.Builder
import Standard.Database.Data.SQL_Statement.SQL_Statement
@@ -168,3 +168,10 @@ type Redshift_Dialect
fetch_primary_key : Connection -> Text -> Vector Text ! Nothing
fetch_primary_key self connection table_name =
Dialect.default_fetch_primary_key connection table_name
+
+ ## PRIVATE
+ value_type_for_upload_of_existing_column : Column -> Value_Type
+ value_type_for_upload_of_existing_column self column =
+ ## TODO special behaviour for big integer columns should be added here, once we start testing this dialect again
+ See: https://docs.aws.amazon.com/redshift/latest/dg/r_Numeric_types201.html#r_Numeric_types201-decimal-or-numeric-type
+ column.value_type
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso
index c4a3e93948c1..02e45f15594e 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Dialect.enso
@@ -5,6 +5,7 @@ import Standard.Table.Internal.Problem_Builder.Problem_Builder
from Standard.Table import Aggregate_Column, Join_Kind, Value_Type
import project.Connection.Connection.Connection
+import project.Data.Column.Column
import project.Data.SQL.Builder
import project.Data.SQL_Statement.SQL_Statement
import project.Data.SQL_Type.SQL_Type
@@ -231,6 +232,17 @@ type Dialect
_ = [replace_params, action]
Unimplemented.throw "This is an interface only."
+ ## PRIVATE
+ Determines the value type to use when uploading the given column to the
+ Database.
+
+ This will usually just be `column.value_type`, but it allows the database
+ to do custom fallback handling for datatypes that are not supported.
+ value_type_for_upload_of_existing_column : Column -> Value_Type
+ value_type_for_upload_of_existing_column self column =
+ _ = column
+ Unimplemented.throw "This is an interface only."
+
## PRIVATE
The dialect of SQLite databases.
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso
index a52ebbc6dd4e..c8dc2f54dc27 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso
@@ -2027,7 +2027,15 @@ type Table
False ->
sql = preprocessed.to_sql
column_type_suggestions = preprocessed.internal_columns.map .sql_type_reference
- self.connection.read_statement sql column_type_suggestions
+ materialized_table = self.connection.read_statement sql column_type_suggestions
+
+ expected_types = self.columns.map .value_type
+ actual_types = materialized_table.columns.map .value_type
+ expected_types.zip actual_types . fold materialized_table acc-> types_pair->
+ expected_type = types_pair.first
+ actual_type = types_pair.second
+ if expected_type == actual_type then acc else
+ Warning.attach (Inexact_Type_Coercion.Warning expected_type actual_type) acc
## PRIVATE
Creates a query corresponding to this table.
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Column_Fetcher.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Column_Fetcher.enso
index 15754f394240..2ee25853bfe9 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Column_Fetcher.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Column_Fetcher.enso
@@ -1,4 +1,5 @@
from Standard.Base import all
+import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Table.Data.Column.Column as Materialized_Column
import Standard.Table.Data.Type.Value_Type.Bits
@@ -83,6 +84,18 @@ long_fetcher bits =
Builder.Value append (seal_java_builder java_builder)
Column_Fetcher.Value fetch_value make_builder
+## PRIVATE
+big_integer_fetcher : Column_Fetcher
+big_integer_fetcher =
+ fetch_value rs i =
+ big_decimal = rs.getBigDecimal i
+ if rs.wasNull then Nothing else
+ big_decimal.toBigIntegerExact
+ make_builder initial_size =
+ java_builder = Java_Exports.make_biginteger_builder initial_size
+ make_builder_from_java_object_builder java_builder
+ Column_Fetcher.Value fetch_value make_builder
+
## PRIVATE
text_fetcher : Value_Type -> Column_Fetcher
text_fetcher value_type =
@@ -145,6 +158,14 @@ default_fetcher_for_value_type value_type =
Value_Type.Time -> time_fetcher
# We currently don't distinguish timestamps without a timezone on the Enso value side.
Value_Type.Date_Time _ -> date_time_fetcher
+ # If we can determine that scale = 0
+ Value_Type.Decimal _ scale ->
+ is_guaranteed_integer = scale.is_nothing.not && scale <= 0
+ case is_guaranteed_integer of
+ True -> big_integer_fetcher
+ # If we cannot guarantee that the column is integer, we will fall back to Float values, since there is no BigDecimal implementation yet.
+ # TODO I think we should add a warning somewhere
+ False -> double_fetcher
_ -> fallback_fetcher
## PRIVATE
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso
index ac5b031f5700..30c7366a9f2e 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Dialect.enso
@@ -4,6 +4,7 @@ import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Errors.Unimplemented.Unimplemented
import Standard.Table.Data.Aggregate_Column.Aggregate_Column
+import Standard.Table.Data.Column.Column as Materialized_Column
import Standard.Table.Internal.Problem_Builder.Problem_Builder
import Standard.Table.Internal.Vector_Builder.Vector_Builder
from Standard.Table import Value_Type
@@ -252,6 +253,29 @@ type Postgres_Dialect
if_replace_params_supports self replace_params ~action =
if supported_replace_params.contains replace_params then action else replace_params.throw_unsupported
+
+ ## PRIVATE
+ value_type_for_upload_of_existing_column : Column -> Value_Type
+ value_type_for_upload_of_existing_column self column = case column of
+ # Return the type as-is for database columns.
+ _ : Column -> column.value_type
+ _ : Materialized_Column ->
+ base_type = column.value_type
+ case base_type of
+ Value_Type.Decimal precision scale ->
+ # We cannot have a specified scale and no precision, so special handling is needed for this:
+ case precision.is_nothing && scale.is_nothing.not of
+ True ->
+ needed_precision = column.java_column.getStorage.getMaxPrecisionStored
+ new_type = case needed_precision <= 1000 of
+ # If the precision is small enough that our number will fit, we create a column with maximum supported precision.
+ True -> Value_Type.Decimal 1000 scale
+ # If the needed precision is too big, we cannot set it, so we set the precision to unlimited. This loses scale.
+ False -> Value_Type.Decimal Nothing Nothing
+ Warning.attach (Inexact_Type_Coercion.Warning base_type new_type) new_type
+ False -> base_type
+ _ -> base_type
+
## PRIVATE
make_internal_generator_dialect =
cases = [["LOWER", Base_Generator.make_function "LOWER"], ["UPPER", Base_Generator.make_function "UPPER"]]
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Type_Mapping.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Type_Mapping.enso
index 3ddc4db109f5..f283e9d8bfa6 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Type_Mapping.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Postgres/Postgres_Type_Mapping.enso
@@ -34,8 +34,11 @@ type Postgres_Type_Mapping
SQL_Type.Value Types.REAL "float4"
Value_Type.Float Bits.Bits_64 ->
SQL_Type.Value Types.DOUBLE "float8"
- Value_Type.Decimal precision scale ->
- SQL_Type.Value Types.DECIMAL "decimal" precision scale
+ Value_Type.Decimal precision scale -> case precision of
+ # If precision is not set, scale is also lost because SQL is unable to express a scale without a precision.
+ Nothing -> SQL_Type.Value Types.DECIMAL "decimal" Nothing Nothing
+ # Scale can be set or not, if precision is given, so no check needed.
+ _ -> SQL_Type.Value Types.DECIMAL "decimal" precision scale
Value_Type.Char size variable ->
case variable of
True ->
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQL_Type_Mapping.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQL_Type_Mapping.enso
index 5d80cad5348d..c01c0fff4abc 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQL_Type_Mapping.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQL_Type_Mapping.enso
@@ -1,4 +1,5 @@
from Standard.Base import all
+import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.Unimplemented.Unimplemented
import Standard.Table.Data.Type.Value_Type.Value_Type
@@ -90,7 +91,11 @@ type SQL_Type_Mapping
## PRIVATE
default_sql_type_to_text sql_type =
- suffix = if sql_type.precision.is_nothing then "" else
- if sql_type.scale.is_nothing then "(" + sql_type.precision.to_text + ")" else
- " (" + sql_type.precision.to_text + "," + sql_type.scale.to_text + ")"
+ suffix = case sql_type.precision of
+ Nothing ->
+ if sql_type.scale.is_nothing.not then Error.throw (Illegal_Argument.Error "It is not possible to specify a scale but no precision in SQL, but got "+sql_type.to_text) else
+ ""
+ _ : Integer ->
+ if sql_type.scale.is_nothing then "(" + sql_type.precision.to_text + ")" else
+ " (" + sql_type.precision.to_text + "," + sql_type.scale.to_text + ")"
sql_type.name.trim + suffix
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso
index 79acb0ff4718..137839243224 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/SQLite/SQLite_Dialect.enso
@@ -3,9 +3,8 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import Standard.Base.Errors.Illegal_State.Illegal_State
import Standard.Base.Runtime.Ref.Ref
-import Standard.Table.Data.Aggregate_Column.Aggregate_Column
import Standard.Table.Internal.Problem_Builder.Problem_Builder
-from Standard.Table import Value_Type
+from Standard.Table import Value_Type, Aggregate_Column
from Standard.Table.Data.Aggregate_Column.Aggregate_Column import all
import project.Connection.Connection.Connection
@@ -269,6 +268,10 @@ type SQLite_Dialect
if_replace_params_supports self replace_params ~action =
if supported_replace_params.contains replace_params then action else replace_params.throw_unsupported
+ ## PRIVATE
+ value_type_for_upload_of_existing_column : Column -> Value_Type
+ value_type_for_upload_of_existing_column self column = column.value_type
+
## PRIVATE
make_internal_generator_dialect =
text = [starts_with, contains, ends_with, make_case_sensitive, ["REPLACE", replace]]+concat_ops+trim_ops
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Statement_Setter.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Statement_Setter.enso
index ad956f73ee19..da6e7da878f8 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Statement_Setter.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Statement_Setter.enso
@@ -1,9 +1,11 @@
from Standard.Base import all
import Standard.Base.Errors.Illegal_State.Illegal_State
+polyglot java import java.math.BigDecimal as Java_Big_Decimal
polyglot java import java.sql.PreparedStatement
polyglot java import java.sql.Types as Java_Types
+polyglot java import org.enso.base.polyglot.NumericConverter
polyglot java import org.enso.database.JDBCUtils
type Statement_Setter
@@ -31,7 +33,11 @@ type Statement_Setter
fill_hole_default stmt i value = case value of
Nothing -> stmt.setNull i Java_Types.NULL
_ : Boolean -> stmt.setBoolean i value
- _ : Integer -> stmt.setLong i value
+ _ : Integer -> case NumericConverter.isBigInteger value of
+ True ->
+ big_decimal = NumericConverter.bigIntegerAsBigDecimal value
+ stmt.setBigDecimal i big_decimal
+ False -> stmt.setLong i value
_ : Decimal -> stmt.setDouble i value
_ : Text -> stmt.setString i value
_ : Date_Time -> JDBCUtils.setZonedDateTime stmt i value
diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso
index b0306ead10f6..af873d713dda 100644
--- a/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso
+++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Internal/Upload_Table.enso
@@ -59,7 +59,7 @@ create_table_implementation connection table_name structure primary_key temporar
Does not check if the table already exists - so if it does, it may fail with
`SQL_Error`. The caller should perform the check for better error handling.
internal_create_table_structure connection table_name structure primary_key temporary on_problems =
- aligned_structure = align_structure structure
+ aligned_structure = align_structure connection structure
resolved_primary_key = resolve_primary_key aligned_structure primary_key
validate_structure connection.base_connection.column_naming_helper aligned_structure <|
create_table_statement = prepare_create_table_statement connection table_name aligned_structure resolved_primary_key temporary on_problems
@@ -217,21 +217,26 @@ raise_duplicated_primary_key_error source_table primary_key original_panic =
## PRIVATE
align_structure : Database_Table | In_Memory_Table | Vector Column_Description -> Vector Column_Description
-align_structure table_or_columns = case table_or_columns of
- vector : Vector -> if vector.is_empty then Error.throw (Illegal_Argument.Error "A table with no columns cannot be created. The `structure` must consist of at list one column description.") else
+align_structure connection table_or_columns = case table_or_columns of
+ vector : Vector -> align_vector_structure vector
+ table : Database_Table -> structure_from_existing_table connection table
+ table : In_Memory_Table -> structure_from_existing_table connection table
+
+## PRIVATE
+align_vector_structure vector =
+ if vector.is_empty then Error.throw (Illegal_Argument.Error "A table with no columns cannot be created. The `structure` must consist of at list one column description.") else
vector.map def-> case def of
_ : Column_Description -> def
_ : Function ->
Error.throw (Illegal_Argument.Error "The structure should be a vector of Column_Description. Maybe some arguments of Column_Description are missing?")
_ ->
Error.throw (Illegal_Argument.Error "The structure must be an existing Table or vector of Column_Description.")
- table : Database_Table -> structure_from_existing_table table
- table : In_Memory_Table -> structure_from_existing_table table
## PRIVATE
-structure_from_existing_table table =
+structure_from_existing_table connection table =
table.columns.map column->
- Column_Description.Value column.name column.value_type
+ value_type = connection.dialect.value_type_for_upload_of_existing_column column
+ Column_Description.Value column.name value_type
## PRIVATE
Verifies that the provided structure is valid, and runs the provided action
@@ -255,9 +260,10 @@ validate_structure column_naming_helper structure ~action =
Returns the name of the first column in the provided table structure.
It also verifies that the structure is correct.
Used to provide the default value for `primary_key` in `create_table`.
-first_column_name_in_structure structure =
- aligned = align_structure structure
- aligned.first.name
+first_column_name_in_structure structure = case structure of
+ vector : Vector -> align_vector_structure vector . first . name
+ table : Database_Table -> table.column_names.first
+ table : In_Memory_Table -> table.column_names.first
## PRIVATE
Creates a statement that will create a table with structure determined by the
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso
index 162dd375280d..9fac83cbd802 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso
@@ -60,7 +60,7 @@ type Column
example_from_vector =
Column.from_vector "My Column" [1, 2, 3, 4, 5]
from_vector : Text -> Vector -> Value_Type | Auto -> Column ! Invalid_Value_Type
- from_vector name items value_type=Auto =
+ from_vector (name : Text) (items : Vector) (value_type : Auto | Value_Type = Auto) =
## If the type does not accept date-time-like values, we can skip the
additional logic for polyglot conversions that would normally be used,
which is quite costly - so if we can guarantee it is unnecessary,
@@ -1118,12 +1118,12 @@ type Column
common_type.if_not_error <|
storage = self.java_column.getStorage
storage_type = Storage.from_value_type_strict common_type
- new_st = case default of
+ new_st = Java_Problems.unpack_value_with_aggregated_problems Problem_Behavior.Report_Warning <| case default of
Column.Value java_col ->
other_storage = java_col.getStorage
storage.fillMissingFrom other_storage storage_type
_ ->
- storage.fillMissing default
+ storage.fillMissing default storage_type
col = Java_Column.new self.name new_st
Column.Value col
@@ -1768,7 +1768,7 @@ type Column
cast self value_type on_problems=Problem_Behavior.Report_Warning =
Cast_Helpers.check_cast_compatibility self.value_type value_type <|
target_storage_type = Storage.from_value_type value_type on_problems
- cast_problem_builder = Cast_Helpers.new_java_problem_builder self.name value_type
+ cast_problem_builder = Cast_Helpers.new_java_problem_builder self.name target_storage_type
new_storage = self.java_column.getStorage.cast target_storage_type cast_problem_builder.to_java
problems = cast_problem_builder.get_problems
on_problems.attach_problems_before problems <|
@@ -1937,7 +1937,7 @@ type Column
example_at = Examples.integer_column.at 0
at : Integer -> (Any | Nothing) ! Index_Out_Of_Bounds
- at self index =
+ at self (index : Integer) =
valid_index = (index >= 0) && (index < self.length)
if valid_index.not then Error.throw (Index_Out_Of_Bounds.Error index self.length) else
storage = self.java_column.getStorage
@@ -2204,9 +2204,10 @@ type Column
run_vectorized_many_op : Column -> Text -> (Any -> Any -> Any) -> Vector -> Text|Nothing -> Boolean -> Column
run_vectorized_many_op column name fallback_fn operands new_name=Nothing skip_nulls=False =
effective_operands = Vector.unify_vector_or_element operands
+ all_operands = [column]+effective_operands
effective_new_name = new_name.if_nothing <|
- naming_helper.function_name name [column]+effective_operands
- common_type = Value_Type_Helpers.find_common_type_for_arguments effective_operands
+ naming_helper.function_name name all_operands
+ common_type = Value_Type_Helpers.find_common_type_for_arguments all_operands
common_type.if_not_error <|
problem_builder = MapOperationProblemBuilder.new effective_new_name
storage_type = resolve_storage_type common_type
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Enso_Types.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Enso_Types.enso
index 4df505a2dda9..3671b510b6b0 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Enso_Types.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Enso_Types.enso
@@ -5,6 +5,7 @@ import project.Data.Type.Value_Type.Bits
import project.Data.Type.Value_Type.Value_Type
polyglot java import org.enso.table.data.column.storage.type.IntegerType
+polyglot java import org.enso.base.polyglot.NumericConverter
## PRIVATE
Finds the most specific `Value_Type` that can be used to hold the given
@@ -22,13 +23,16 @@ most_specific_value_type value use_smallest=False =
_ : Date -> Value_Type.Date
_ : Time_Of_Day -> Value_Type.Time
_ : Date_Time -> Value_Type.Date_Time
- i : Integer -> case use_smallest of
- False -> Value_Type.Integer Bits.Bits_64
- True ->
- storage_type = IntegerType.smallestFitting i
- value_type = Storage.to_value_type storage_type
- # We do a small rewrite here - for integers we always return the Integer type, even if the value is small enough to fit in a Byte.
- if value_type == Value_Type.Byte then Value_Type.Integer Bits.Bits_16 else value_type
+ i : Integer ->
+ case NumericConverter.isBigInteger i of
+ False -> case use_smallest of
+ False -> Value_Type.Integer Bits.Bits_64
+ True ->
+ storage_type = IntegerType.smallestFitting i
+ value_type = Storage.to_value_type storage_type
+ # We do a small rewrite here - for integers we always return the Integer type, even if the value is small enough to fit in a Byte.
+ if value_type == Value_Type.Byte then Value_Type.Integer Bits.Bits_16 else value_type
+ True -> Value_Type.Decimal precision=Nothing scale=0
text : Text -> case use_smallest of
False -> Value_Type.Char size=Nothing variable_length=True
True -> Value_Type.Char size=text.length variable_length=False
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Storage.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Storage.enso
index a5754f8e11a0..2be7986adc04 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Storage.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Storage.enso
@@ -10,6 +10,7 @@ from Standard.Table.Errors import Inexact_Type_Coercion
polyglot java import org.enso.table.data.column.builder.Builder
polyglot java import org.enso.table.data.column.storage.type.AnyObjectType
polyglot java import org.enso.table.data.column.storage.type.Bits as Java_Bits
+polyglot java import org.enso.table.data.column.storage.type.BigIntegerType
polyglot java import org.enso.table.data.column.storage.type.BooleanType
polyglot java import org.enso.table.data.column.storage.type.DateTimeType
polyglot java import org.enso.table.data.column.storage.type.DateType
@@ -37,6 +38,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
+ _ : BigIntegerType -> Value_Type.Decimal scale=0
_ : AnyObjectType -> Value_Type.Mixed
## PRIVATE
@@ -58,6 +60,7 @@ closest_storage_type value_type = case value_type of
Value_Type.Date_Time _ -> DateTimeType.INSTANCE
Value_Type.Time -> TimeOfDayType.INSTANCE
Value_Type.Mixed -> AnyObjectType.INSTANCE
+ Value_Type.Decimal _ _ -> BigIntegerType.INSTANCE
_ ->
Error.throw (Illegal_Argument.Error "Columns of type "+value_type.to_display_text+" are currently not supported in the in-memory backend.")
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type_Helpers.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type_Helpers.enso
index 2c68964f2932..724d61649533 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type_Helpers.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Type/Value_Type_Helpers.enso
@@ -17,20 +17,32 @@ reconcile_types current new = case current of
Value_Type.Byte -> Value_Type.Integer size
# If we unify integers with floats, we select the default Float 64 regardless of the input sizes.
Value_Type.Float _ -> Value_Type.Float
+ Value_Type.Decimal _ _ -> new
_ -> Value_Type.Mixed
Value_Type.Float size -> case new of
Value_Type.Float new_size ->
Value_Type.Float (max_size size new_size)
# If we unify integers with floats, we select the default Float 64 regardless of the input sizes.
- Value_Type.Integer _ -> Value_Type.Float
- Value_Type.Byte -> Value_Type.Float
- _ -> Value_Type.Mixed
+ Value_Type.Integer _ -> Value_Type.Float
+ Value_Type.Byte -> Value_Type.Float
+ Value_Type.Decimal _ _ -> Value_Type.Float
+ _ -> Value_Type.Mixed
Value_Type.Byte -> case new of
Value_Type.Byte -> Value_Type.Byte
Value_Type.Integer size ->
Value_Type.Integer size
Value_Type.Float _ -> Value_Type.Float
+ Value_Type.Decimal _ _ -> new
_ -> Value_Type.Mixed
+ Value_Type.Decimal precision scale -> case new of
+ Value_Type.Decimal new_precision new_scale ->
+ if (precision == new_precision) && (scale == new_scale) then new else
+ # TODO at some point we may want a more clever merging of precision and scale, for now we don't use them too much anyway so we just default to Nothing if they do not agree
+ Value_Type.Decimal
+ Value_Type.Integer _ -> Value_Type.Decimal precision scale
+ Value_Type.Byte -> Value_Type.Decimal precision scale
+ Value_Type.Float _ -> Value_Type.Float
+ _ -> Value_Type.Mixed
Value_Type.Boolean -> case new of
Value_Type.Boolean -> Value_Type.Boolean
_ -> Value_Type.Mixed
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Cast_Helpers.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Cast_Helpers.enso
index ae54c328c32c..889bf12dba3a 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Cast_Helpers.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Cast_Helpers.enso
@@ -8,6 +8,7 @@ import project.Internal.Parse_Values_Helper
from project.Errors import Conversion_Failure
polyglot java import org.enso.table.data.column.operation.cast.CastProblemBuilder
+polyglot java import org.enso.table.data.column.storage.type.StorageType
## PRIVATE
Checks if one type can be cast into another and returns a dataflow error
@@ -48,7 +49,6 @@ type Cast_Problem_Builder
Java_Problems.parse_aggregated_problems self.to_java.getAggregatedProblems
## PRIVATE
-new_java_problem_builder : Text -> Value_Type -> Cast_Problem_Builder
-new_java_problem_builder column_name target_type =
- storage_type = Storage.from_value_type_strict target_type
- Cast_Problem_Builder.Value (CastProblemBuilder.new column_name storage_type)
+new_java_problem_builder : Text -> StorageType -> Cast_Problem_Builder
+new_java_problem_builder column_name target_storage_type =
+ Cast_Problem_Builder.Value (CastProblemBuilder.new column_name target_storage_type)
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Exports.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Exports.enso
index fe827bc545f6..3cd58f80d9b6 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Exports.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Exports.enso
@@ -4,6 +4,7 @@ import project.Data.Type.Storage
import project.Data.Type.Value_Type.Bits
import project.Data.Type.Value_Type.Value_Type
+polyglot java import org.enso.table.data.column.builder.BigIntegerBuilder
polyglot java import org.enso.table.data.column.builder.BoolBuilder
polyglot java import org.enso.table.data.column.builder.DateBuilder
polyglot java import org.enso.table.data.column.builder.DateTimeBuilder
@@ -27,6 +28,10 @@ make_long_builder initial_size bits=Bits.Bits_64 =
integer_type = Storage.from_value_type_strict (Value_Type.Integer bits)
NumericBuilder.createLongBuilder initial_size integer_type
+## PRIVATE
+make_biginteger_builder : Integer -> BigIntegerBuilder
+make_biginteger_builder initial_size = BigIntegerBuilder.new initial_size
+
## PRIVATE
make_string_builder : Integer -> Value_Type -> StringBuilder
make_string_builder initial_size value_type=Value_Type.Char =
diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Problems.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Problems.enso
index db0574fe72ae..d9bcd31dabc8 100644
--- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Problems.enso
+++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Java_Problems.enso
@@ -3,7 +3,7 @@ import Standard.Base.Errors.Common.Arithmetic_Error
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
import project.Data.Type.Storage
-from project.Errors import Additional_Invalid_Rows, Additional_Warnings, Arithmetic_Overflow, Conversion_Failure, Duplicate_Output_Column_Names, Floating_Point_Equality, Invalid_Aggregation, Invalid_Column_Names, Invalid_Row, Loss_Of_Integer_Precision, Unquoted_Characters_In_Output, Unquoted_Delimiter
+from project.Errors import all
polyglot java import org.enso.table.data.column.builder.LossOfIntegerPrecision
polyglot java import org.enso.table.data.column.operation.cast.ConversionFailure
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 d4d7f0028008..c36f845af944 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
@@ -1,6 +1,9 @@
package org.enso.base.polyglot;
+import org.graalvm.polyglot.Value;
+
import java.math.BigDecimal;
+import java.math.BigInteger;
/**
* The numeric converter deals with conversions of Java numeric types to the two main types
@@ -25,6 +28,7 @@ public static double coerceToDouble(Object o) {
case Double x -> x;
case BigDecimal x -> x.doubleValue();
case Float x -> x.doubleValue();
+ case BigInteger x -> x.doubleValue();
default -> (double) coerceToLong(o);
};
}
@@ -46,9 +50,18 @@ public static long coerceToLong(Object o) {
};
}
+ public static BigInteger coerceToBigInteger(Object o) {
+ if (o instanceof BigInteger bigInteger) {
+ return bigInteger;
+ } else {
+ long longValue = coerceToLong(o);
+ return BigInteger.valueOf(longValue);
+ }
+ }
+
/** Returns true if the object is any supported number. */
public static boolean isCoercibleToDouble(Object o) {
- return isDecimalLike(o)|| isCoercibleToLong(o);
+ return isDecimalLike(o)|| isCoercibleToLong(o) || o instanceof BigInteger;
}
public static boolean isDecimalLike(Object o) {
@@ -66,6 +79,10 @@ public static boolean isCoercibleToLong(Object o) {
return o instanceof Long || o instanceof Integer || o instanceof Short || o instanceof Byte;
}
+ public static boolean isCoercibleToBigInteger(Object o) {
+ return o instanceof BigInteger || isCoercibleToLong(o);
+ }
+
/**
* Tries converting the value to a Double.
*
@@ -75,6 +92,7 @@ public static Double tryConvertingToDouble(Object o) {
return switch (o) {
case Double x -> x;
case BigDecimal x -> x.doubleValue();
+ case BigInteger x -> x.doubleValue();
case Float x -> x.doubleValue();
case Long x -> x.doubleValue();
case Integer x -> x.doubleValue();
@@ -105,7 +123,23 @@ public static Long tryConvertingToLong(Object o) {
yield null;
}
}
+ case BigInteger x -> {
+ try {
+ yield x.longValueExact();
+ } catch (ArithmeticException e) {
+ yield null;
+ }
+ }
case null, default -> null;
};
}
+
+ public static boolean isBigInteger(Value v) {
+ return v.fitsInBigInteger() && !v.fitsInLong();
+ }
+
+ /** A workaround for #7790 */
+ public static BigDecimal bigIntegerAsBigDecimal(BigInteger x) {
+ return new BigDecimal(x);
+ }
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigIntegerBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigIntegerBuilder.java
new file mode 100644
index 000000000000..abaf10902432
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/BigIntegerBuilder.java
@@ -0,0 +1,98 @@
+package org.enso.table.data.column.builder;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import org.enso.base.polyglot.NumericConverter;
+import org.enso.table.data.column.storage.Storage;
+import org.enso.table.data.column.storage.numeric.BigIntegerStorage;
+import org.enso.table.data.column.storage.type.AnyObjectType;
+import org.enso.table.data.column.storage.type.BigIntegerType;
+import org.enso.table.data.column.storage.type.FloatType;
+import org.enso.table.data.column.storage.type.StorageType;
+import org.enso.table.error.ValueTypeMismatchException;
+
+// For now the BigInteger builder is just a stub, reusing the ObjectBuilder and adding a warning.
+public class BigIntegerBuilder extends TypedBuilderImpl {
+ @Override
+ protected BigInteger[] newArray(int size) {
+ return new BigInteger[size];
+ }
+
+ public BigIntegerBuilder(int size) {
+ super(size);
+ }
+
+ @Override
+ public void writeTo(Object[] items) {
+ super.writeTo(items);
+ }
+
+ @Override
+ public boolean canRetypeTo(StorageType type) {
+ return type instanceof FloatType || type instanceof AnyObjectType;
+ }
+
+ @Override
+ public TypedBuilder retypeTo(StorageType type) {
+ if (type instanceof FloatType) {
+ DoubleBuilder res = NumericBuilder.createDoubleBuilder(currentSize);
+ for (int i = 0; i < currentSize; i++) {
+ if (data[i] == null) {
+ res.appendNulls(1);
+ } else {
+ res.appendBigInteger(data[i]);
+ }
+ }
+ return res;
+ } else if (type instanceof AnyObjectType) {
+ Object[] widenedData = Arrays.copyOf(data, data.length, Object[].class);
+ ObjectBuilder res = new MixedBuilder(widenedData);
+ res.setCurrentSize(currentSize);
+ res.setPreExistingProblems(getProblems());
+ return res;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ protected Storage doSeal() {
+ return new BigIntegerStorage(data, currentSize);
+ }
+
+ @Override
+ public StorageType getType() {
+ return BigIntegerType.INSTANCE;
+ }
+
+ @Override
+ public boolean accepts(Object o) {
+ return NumericConverter.isCoercibleToBigInteger(o);
+ }
+
+ @Override
+ public void appendNoGrow(Object o) {
+ if (o == null) {
+ data[currentSize++] = null;
+ } else {
+ try {
+ data[currentSize++] = NumericConverter.coerceToBigInteger(o);
+ } catch (UnsupportedOperationException e) {
+ throw new ValueTypeMismatchException(BigIntegerType.INSTANCE, o);
+ }
+ }
+ }
+
+ public void appendRawNoGrow(BigInteger value) {
+ data[currentSize++] = value;
+ }
+
+ public static BigIntegerBuilder retypeFromLongBuilder(LongBuilder longBuilder) {
+ int n = longBuilder.currentSize;
+ BigIntegerBuilder res = new BigIntegerBuilder(n);
+ for (int i = 0; i < n; i++) {
+ res.appendNoGrow(BigInteger.valueOf(longBuilder.data[i]));
+ }
+ return res;
+ }
+}
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 7c8f0e5e7486..b7ca2e7ecbc8 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
@@ -6,6 +6,7 @@
import org.enso.table.data.column.storage.type.FloatType;
import org.enso.table.data.column.storage.type.IntegerType;
import org.enso.table.problems.AggregatedProblems;
+import org.enso.table.problems.WithAggregatedProblems;
/** A builder for creating columns dynamically. */
public abstract class Builder {
@@ -26,6 +27,7 @@ public static Builder getForType(StorageType type, int size) {
};
case IntegerType integerType -> NumericBuilder.createLongBuilder(size, integerType);
case TextType textType -> new StringBuilder(size, textType);
+ case BigIntegerType x -> new BigIntegerBuilder(size);
case null -> new InferredBuilder(size);
};
assert builder.getType().equals(type);
@@ -88,4 +90,8 @@ public static Builder getForType(StorageType type, int size) {
public AggregatedProblems getProblems() {
return AggregatedProblems.of();
}
+
+ public WithAggregatedProblems> sealWithProblems() {
+ return new WithAggregatedProblems<>(seal(), getProblems());
+ }
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java
index 9246992bdfb0..d76139f32e51 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/DoubleBuilder.java
@@ -14,6 +14,8 @@
import org.enso.table.problems.AggregatedProblems;
import org.enso.table.util.BitSets;
+import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.BitSet;
import java.util.Objects;
@@ -89,6 +91,9 @@ public void appendNoGrow(Object o) {
long value = NumericConverter.coerceToLong(o);
double converted = convertIntegerToDouble(value);
data[currentSize++] = Double.doubleToRawLongBits(converted);
+ } else if (o instanceof BigInteger bigInteger) {
+ double converted = convertBigIntegerToDouble(bigInteger);
+ data[currentSize++] = Double.doubleToRawLongBits(converted);
} else {
throw new ValueTypeMismatchException(getType(), o);
}
@@ -177,6 +182,15 @@ public void appendLong(long integer) {
appendRawNoGrow(Double.doubleToRawLongBits(converted));
}
+ public void appendBigInteger(BigInteger integer) {
+ if (currentSize >= this.data.length) {
+ grow();
+ }
+
+ double converted = convertBigIntegerToDouble(integer);
+ appendRawNoGrow(Double.doubleToRawLongBits(converted));
+ }
+
@Override
public Storage seal() {
return new DoubleStorage(data, currentSize, isMissing);
@@ -200,6 +214,20 @@ private double convertIntegerToDouble(long integer) {
return floatingPointValue;
}
+ private double convertBigIntegerToDouble(BigInteger bigInteger) {
+ double floatingPointValue = bigInteger.doubleValue();
+ BigInteger reconstructed = BigDecimal.valueOf(floatingPointValue).toBigInteger();
+ boolean isLosingPrecision = !bigInteger.equals(reconstructed);
+ if (isLosingPrecision) {
+ if (precisionLoss == null) {
+ precisionLoss = new LossOfIntegerPrecision(bigInteger, floatingPointValue);
+ } else {
+ precisionLoss.incrementAffectedRows();
+ }
+ }
+ return floatingPointValue;
+ }
+
private LossOfIntegerPrecision precisionLoss = null;
@Override
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 b64bb78d6077..1a69840f3b77 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,11 +1,13 @@
package org.enso.table.data.column.builder;
+import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.List;
import org.enso.base.polyglot.NumericConverter;
import org.enso.table.data.column.storage.Storage;
+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;
import org.enso.table.data.column.storage.type.DateType;
@@ -99,16 +101,18 @@ private void initBuilderFor(Object o) {
} else if (NumericConverter.isCoercibleToLong(o)) {
// In inferred builder, we always default to 64-bits.
currentBuilder = NumericBuilder.createLongBuilder(initialCapacity, IntegerType.INT_64);
- } else if (NumericConverter.isCoercibleToDouble(o)) {
+ } else if (NumericConverter.isDecimalLike(o)) {
currentBuilder = NumericBuilder.createDoubleBuilder(initialCapacity);
+ } else if (o instanceof String) {
+ currentBuilder = new StringBuilder(initialCapacity, TextType.VARIABLE_LENGTH);
+ } else if (o instanceof BigInteger) {
+ currentBuilder = new BigIntegerBuilder(initialCapacity);
} else if (o instanceof LocalDate) {
currentBuilder = new DateBuilder(initialCapacity);
} else if (o instanceof LocalTime) {
currentBuilder = new TimeOfDayBuilder(initialCapacity);
} else if (o instanceof ZonedDateTime) {
currentBuilder = new DateTimeBuilder(initialCapacity);
- } else if (o instanceof String) {
- currentBuilder = new StringBuilder(initialCapacity, TextType.VARIABLE_LENGTH);
} else {
currentBuilder = new MixedBuilder(initialCapacity);
}
@@ -134,7 +138,8 @@ private record RetypeInfo(Class> clazz, StorageType type) {}
// not apply only if a specific type is requested (so not in inferred builder).
new RetypeInfo(Integer.class, IntegerType.INT_64),
new RetypeInfo(Short.class, IntegerType.INT_64),
- new RetypeInfo(Byte.class, IntegerType.INT_64));
+ new RetypeInfo(Byte.class, IntegerType.INT_64),
+ new RetypeInfo(BigInteger.class, BigIntegerType.INSTANCE));
private void retypeAndAppend(Object o) {
for (RetypeInfo info : retypePairs) {
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java
index 02e89ecac787..ab762314ee55 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LongBuilder.java
@@ -6,6 +6,7 @@
import org.enso.table.data.column.storage.numeric.AbstractLongStorage;
import org.enso.table.data.column.storage.numeric.LongStorage;
import org.enso.table.data.column.storage.Storage;
+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.FloatType;
import org.enso.table.data.column.storage.type.IntegerType;
@@ -46,12 +47,14 @@ public void writeTo(Object[] items) {
@Override
public boolean canRetypeTo(StorageType type) {
- return Objects.equals(type, FloatType.FLOAT_64);
+ return Objects.equals(type, FloatType.FLOAT_64) || Objects.equals(type, BigIntegerType.INSTANCE);
}
@Override
public TypedBuilder retypeTo(StorageType type) {
- if (Objects.equals(type, FloatType.FLOAT_64)) {
+ if (Objects.equals(type, BigIntegerType.INSTANCE)) {
+ return BigIntegerBuilder.retypeFromLongBuilder(this);
+ } else if (Objects.equals(type, FloatType.FLOAT_64)) {
return DoubleBuilder.retypeFromLongBuilder(this);
} else {
throw new UnsupportedOperationException();
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LossOfIntegerPrecision.java b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LossOfIntegerPrecision.java
index 3926d168b462..cb5a5a1f30cd 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/builder/LossOfIntegerPrecision.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/builder/LossOfIntegerPrecision.java
@@ -4,17 +4,17 @@
/** Indicates that an integer being converted to double cannot be represented precisely. */
public class LossOfIntegerPrecision implements Problem {
- private final long exampleValue;
+ private final Number exampleValue;
private final double exampleValueConverted;
private long affectedRows;
- public LossOfIntegerPrecision(long exampleValue, double exampleValueConverted) {
+ public LossOfIntegerPrecision(Number exampleValue, double exampleValueConverted) {
this.exampleValue = exampleValue;
this.exampleValueConverted = exampleValueConverted;
this.affectedRows = 1;
}
- public long getExampleValue() {
+ public Number getExampleValue() {
return exampleValue;
}
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 8e18d323957e..fc8b1363b5e2 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
@@ -25,6 +25,7 @@ static StorageConverter> fromStorageType(StorageType storageType) {
case IntegerType integerType -> new ToIntegerStorageConverter(integerType);
case TextType textType -> new ToTextStorageConverter(textType);
case TimeOfDayType timeOfDayType -> new ToTimeOfDayStorageConverter();
+ case BigIntegerType bigIntegerType -> new ToBigIntegerConverter();
};
}
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToBigIntegerConverter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToBigIntegerConverter.java
new file mode 100644
index 000000000000..0fe0b5481150
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToBigIntegerConverter.java
@@ -0,0 +1,100 @@
+package org.enso.table.data.column.operation.cast;
+
+import org.enso.table.data.column.builder.BigIntegerBuilder;
+import org.enso.table.data.column.storage.BoolStorage;
+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.BigIntegerStorage;
+import org.enso.table.data.column.storage.numeric.DoubleStorage;
+import org.enso.table.data.column.storage.type.AnyObjectType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class ToBigIntegerConverter implements StorageConverter {
+ @Override
+ public Storage cast(Storage> storage, CastProblemBuilder problemBuilder) {
+ if (storage instanceof BigIntegerStorage bigIntegerStorage) {
+ return bigIntegerStorage;
+ } else if (storage instanceof AbstractLongStorage longStorage) {
+ return convertLongStorage(longStorage, problemBuilder);
+ } else if (storage instanceof DoubleStorage doubleStorage) {
+ return convertDoubleStorage(doubleStorage, problemBuilder);
+ } else if (storage instanceof BoolStorage boolStorage) {
+ return convertBoolStorage(boolStorage, problemBuilder);
+ } else if (storage.getType() instanceof AnyObjectType) {
+ return castFromMixed(storage, problemBuilder);
+ }else {
+ throw new IllegalStateException("No known strategy for casting storage " + storage + " to BigInteger.");
+ }
+ }
+
+ private Storage convertDoubleStorage(DoubleStorage doubleStorage, CastProblemBuilder problemBuilder) {
+ int n = doubleStorage.size();
+ BigIntegerBuilder builder = new BigIntegerBuilder(n);
+ for (int i = 0; i < n; i++) {
+ if (doubleStorage.isNa(i)) {
+ builder.appendNulls(1);
+ } else {
+ double x = doubleStorage.getItemAsDouble(i);
+ BigInteger bigInteger = BigDecimal.valueOf(x).toBigInteger();
+ builder.appendRawNoGrow(bigInteger);
+ }
+ }
+ return builder.seal();
+ }
+
+ private Storage convertLongStorage(AbstractLongStorage longStorage, CastProblemBuilder problemBuilder) {
+ int n = longStorage.size();
+ BigIntegerBuilder builder = new BigIntegerBuilder(n);
+ for (int i = 0; i < n; i++) {
+ if (longStorage.isNa(i)) {
+ builder.appendNulls(1);
+ } else {
+ long x = longStorage.getItem(i);
+ BigInteger bigInteger = BigInteger.valueOf(x);
+ builder.appendRawNoGrow(bigInteger);
+ }
+ }
+ return builder.seal();
+ }
+
+ private Storage convertBoolStorage(BoolStorage boolStorage, CastProblemBuilder problemBuilder) {
+ int n = boolStorage.size();
+ BigIntegerBuilder builder = new BigIntegerBuilder(n);
+ for (int i = 0; i < n; i++) {
+ if (boolStorage.isNa(i)) {
+ builder.appendNulls(1);
+ } else {
+ boolean x = boolStorage.getItem(i);
+ BigInteger bigInteger = booleanAsBigInteger(x);
+ builder.appendRawNoGrow(bigInteger);
+ }
+ }
+ return builder.seal();
+ }
+
+ private Storage castFromMixed(Storage> storage, CastProblemBuilder problemBuilder) {
+ int n = storage.size();
+ BigIntegerBuilder builder = new BigIntegerBuilder(n);
+ for (int i = 0; i < n; i++) {
+ Object o = storage.getItemBoxed(i);
+ switch (o) {
+ case null -> builder.appendNulls(1);
+ case Boolean b -> builder.appendRawNoGrow(booleanAsBigInteger(b));
+ case Long l -> builder.appendRawNoGrow(BigInteger.valueOf(l));
+ case Double d -> builder.appendRawNoGrow(BigDecimal.valueOf(d).toBigInteger());
+ case BigInteger bigInteger -> builder.appendRawNoGrow(bigInteger);
+ default -> {
+ problemBuilder.reportConversionFailure(o);
+ builder.appendNulls(1);
+ }
+ }
+ }
+ return builder.seal();
+ }
+
+ public static BigInteger booleanAsBigInteger(boolean value) {
+ return value ? BigInteger.ONE : BigInteger.ZERO;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToFloatStorageConverter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToFloatStorageConverter.java
index b2d425599fc4..e7852144003f 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToFloatStorageConverter.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToFloatStorageConverter.java
@@ -5,13 +5,18 @@
import org.enso.table.data.column.builder.NumericBuilder;
import org.enso.table.data.column.storage.BoolStorage;
import org.enso.table.data.column.storage.numeric.AbstractLongStorage;
+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.Storage;
+import org.enso.table.data.column.storage.numeric.LongStorage;
import org.enso.table.data.column.storage.type.AnyObjectType;
import org.enso.table.data.column.storage.type.Bits;
import org.enso.table.data.column.storage.type.FloatType;
import org.graalvm.polyglot.Context;
+import java.math.BigInteger;
+import java.util.BitSet;
+
public class ToFloatStorageConverter implements StorageConverter {
public ToFloatStorageConverter(FloatType targetType) {
if (targetType.bits() != Bits.BITS_64) {
@@ -26,6 +31,8 @@ public Storage cast(Storage> storage, CastProblemBuilder problemBuilde
return convertLongStorage(longStorage, problemBuilder);
} else if (storage instanceof BoolStorage boolStorage) {
return convertBoolStorage(boolStorage, problemBuilder);
+ } else if (storage instanceof BigIntegerStorage bigIntegerStorage) {
+ return convertBigIntegerStorage(bigIntegerStorage, problemBuilder);
} else if (storage.getType() instanceof AnyObjectType) {
return castFromMixed(storage, problemBuilder);
} else {
@@ -48,6 +55,8 @@ public Storage castFromMixed(Storage> mixedStorage, CastProblemBuilder
} else if (NumericConverter.isDecimalLike(o)) {
double x = NumericConverter.coerceToDouble(o);
builder.appendDouble(x);
+ } else if (o instanceof BigInteger bigInteger) {
+ builder.appendBigInteger(bigInteger);
} else {
problemBuilder.reportConversionFailure(o);
builder.appendNulls(1);
@@ -95,4 +104,23 @@ private Storage convertBoolStorage(BoolStorage boolStorage, CastProblemB
public static double booleanAsDouble(boolean value) {
return value ? 1.0 : 0.0;
}
+
+ private Storage convertBigIntegerStorage(Storage storage, CastProblemBuilder problemBuilder) {
+ int n = storage.size();
+ DoubleBuilder builder = NumericBuilder.createDoubleBuilder(n);
+ Context context = Context.getCurrent();
+ for (int i = 0; i < n; i++) {
+ BigInteger value = storage.getItemBoxed(i);
+ if (value == null) {
+ builder.appendNulls(1);
+ } else {
+ builder.appendBigInteger(value);
+ }
+
+ context.safepoint();
+ }
+
+ problemBuilder.aggregateOtherProblems(builder.getProblems());
+ return builder.seal();
+ }
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToIntegerStorageConverter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToIntegerStorageConverter.java
index bce9566f8779..f51199f06b8c 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToIntegerStorageConverter.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToIntegerStorageConverter.java
@@ -5,15 +5,16 @@
import org.enso.table.data.column.builder.NumericBuilder;
import org.enso.table.data.column.storage.BoolStorage;
import org.enso.table.data.column.storage.numeric.AbstractLongStorage;
+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;
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.Bits;
import org.enso.table.data.column.storage.type.IntegerType;
import org.enso.table.util.BitSets;
import org.graalvm.polyglot.Context;
+import java.math.BigInteger;
import java.util.BitSet;
public class ToIntegerStorageConverter implements StorageConverter {
@@ -34,6 +35,8 @@ public Storage cast(Storage> storage, CastProblemBuilder problemBuilder)
return convertDoubleStorage(doubleStorage, problemBuilder);
} else if (storage instanceof BoolStorage boolStorage) {
return convertBoolStorage(boolStorage, problemBuilder);
+ } else if (storage instanceof BigIntegerStorage bigIntegerStorage) {
+ return convertBigIntegerStorage(bigIntegerStorage, problemBuilder);
} else if (storage.getType() instanceof AnyObjectType) {
return castFromMixed(storage, problemBuilder);
} else {
@@ -67,6 +70,13 @@ public Storage castFromMixed(Storage> mixedStorage, CastProblemBuilder p
problemBuilder.reportNumberOutOfRange(x);
builder.appendNulls(1);
}
+ } else if (o instanceof BigInteger bigInteger) {
+ if (targetType.fits(bigInteger)) {
+ builder.appendLongUnchecked(bigInteger.longValue());
+ } else {
+ problemBuilder.reportNumberOutOfRange(bigInteger);
+ builder.appendNulls(1);
+ }
} else {
problemBuilder.reportConversionFailure(o);
builder.appendNulls(1);
@@ -106,7 +116,7 @@ private Storage convertDoubleStorage(DoubleStorage doubleStorage, CastProb
if (doubleStorage.isNa(i)) {
builder.appendNulls(1);
} else {
- double value = doubleStorage.getItem(i);
+ double value = doubleStorage.getItemAsDouble(i);
if (targetType.fits(value)) {
long converted = (long) value;
builder.appendLong(converted);
@@ -152,6 +162,28 @@ private Storage convertLongStorage(AbstractLongStorage longStorage, CastPr
}
}
+ private Storage convertBigIntegerStorage(Storage storage, CastProblemBuilder problemBuilder) {
+ Context context = Context.getCurrent();
+ int n = storage.size();
+ long[] data = new long[n];
+ BitSet isMissing = new BitSet();
+ for (int i = 0; i < n; i++) {
+ BigInteger value = storage.getItemBoxed(i);
+ if (value == null) {
+ isMissing.set(i);
+ } else if (targetType.fits(value)) {
+ data[i] = value.longValue();
+ } else {
+ isMissing.set(i);
+ problemBuilder.reportNumberOutOfRange(value);
+ }
+
+ context.safepoint();
+ }
+
+ return new LongStorage(data, n, isMissing, targetType);
+ }
+
public static long booleanAsLong(boolean value) {
return value ? 1L : 0L;
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToTextStorageConverter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToTextStorageConverter.java
index 443bf795f788..7323932bfd85 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToTextStorageConverter.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/cast/ToTextStorageConverter.java
@@ -1,6 +1,5 @@
package org.enso.table.data.column.operation.cast;
-import org.enso.base.Text_Utils;
import org.enso.polyglot.common_utils.Core_Date_Utils;
import org.enso.table.data.column.builder.StringBuilder;
import org.enso.table.data.column.storage.BoolStorage;
@@ -140,7 +139,7 @@ private Storage castDoubleStorage(DoubleStorage doubleStorage, CastProbl
if (doubleStorage.isNa(i)) {
builder.appendNulls(1);
} else {
- double value = doubleStorage.getItem(i);
+ double value = doubleStorage.getItemAsDouble(i);
builder.append(adapt(Double.toString(value), problemBuilder));
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleBooleanOp.java
deleted file mode 100644
index 26aca6d9b525..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleBooleanOp.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.base.polyglot.NumericConverter;
-import org.enso.table.data.column.operation.map.BinaryMapOperation;
-import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
-import org.enso.table.data.column.storage.BoolStorage;
-import org.enso.table.data.column.storage.numeric.AbstractLongStorage;
-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.Storage;
-import org.enso.table.error.UnexpectedTypeException;
-import org.graalvm.polyglot.Context;
-
-import java.util.BitSet;
-
-/** An operation expecting a numeric argument and returning a boolean. */
-public abstract class DoubleBooleanOp extends BinaryMapOperation {
- public DoubleBooleanOp(String name) {
- super(name);
- }
-
- protected abstract boolean doDouble(double a, double b);
-
- protected boolean doObject(double a, Object o) {
- throw new UnexpectedTypeException("a Number");
- }
-
- @Override
- public BoolStorage runBinaryMap(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- Double v = NumericConverter.tryConvertingToDouble(arg);
- if (v != null) {
- double x = v;
- BitSet newVals = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- if (doDouble(storage.getItem(i), x)) {
- newVals.set(i);
- }
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, storage.getIsMissing(), storage.size(), false);
- } else {
- BitSet newVals = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- if (doObject(storage.getItem(i), arg)) {
- newVals.set(i);
- }
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, storage.getIsMissing(), storage.size(), false);
- }
- }
-
- @Override
- public BoolStorage runZip(DoubleStorage storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg instanceof DoubleStorage v) {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- if (doDouble(storage.getItem(i), v.getItem(i))) {
- newVals.set(i);
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- } else if (arg instanceof AbstractLongStorage v) {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- double left = storage.getItem(i);
- long right = v.getItem(i);
- // We convert from long to double here. This may lose precision, but we do not report
- // LossOfIntegerPrecision, because it is expected that numeric operations involving floating point columns
- // are inherently imprecise.
- double rightConverted = (double) right;
- if (doDouble(left, rightConverted)) {
- newVals.set(i);
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- } else {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < arg.size() && !arg.isNa(i)) {
- Double x = NumericConverter.tryConvertingToDouble(arg.getItemBoxed(i));
- if (x == null) {
- if (doObject(storage.getItem(i), arg.getItemBoxed(i))) {
- newVals.set(i);
- }
- } else {
- if (doDouble(storage.getItem(i), x)) {
- newVals.set(i);
- }
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- }
- }
-
- @Override
- public boolean reliesOnSpecializedStorage() {
- return false;
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleComparison.java
deleted file mode 100644
index 7106b593bf9c..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleComparison.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.base.CompareException;
-
-public abstract class DoubleComparison extends DoubleBooleanOp {
- public DoubleComparison(String name) {
- super(name);
- }
-
- @Override
- protected boolean doObject(double a, Object o) {
- throw new CompareException(a, o);
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongMapOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongMapOpWithSpecialNumericHandling.java
index f907338ac45e..5f9418c69ecc 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongMapOpWithSpecialNumericHandling.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongMapOpWithSpecialNumericHandling.java
@@ -24,7 +24,7 @@ public LongStorage runUnaryMap(DoubleStorage storage, MapOperationProblemBuilder
for (int i = 0; i < storage.size(); i++) {
if (!storage.isNa(i)) {
- double item = storage.getItem(i);
+ double item = storage.getItemAsDouble(i);
boolean special = Double.isNaN(item) || Double.isInfinite(item);
if (!special) {
out[i] = doOperation(item);
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleNumericOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleNumericOp.java
deleted file mode 100644
index b3246fadc906..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleNumericOp.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.table.data.column.operation.map.BinaryMapOperation;
-import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
-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.DoubleStorage;
-import org.enso.table.error.UnexpectedTypeException;
-import org.graalvm.polyglot.Context;
-
-import java.util.BitSet;
-
-/** An operation expecting a numeric argument and returning a number. */
-public abstract class DoubleNumericOp extends BinaryMapOperation {
-
- public DoubleNumericOp(String name) {
- super(name);
- }
-
- protected abstract double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder);
-
- @Override
- public Storage runBinaryMap(DoubleStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) {
- if (arg == null) {
- return DoubleStorage.makeEmpty(storage.size());
- }
-
- double x;
- if (arg instanceof Double) {
- x = (Double) arg;
- } else if (arg instanceof Long) {
- x = (Long) arg;
- } else {
- throw new UnexpectedTypeException("a Number.");
- }
-
- Context context = Context.getCurrent();
- long[] out = new long[storage.size()];
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- out[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), x, i, problemBuilder));
- }
-
- context.safepoint();
- }
- return new DoubleStorage(out, storage.size(), storage.getIsMissing());
- }
-
- @Override
- public Storage runZip(DoubleStorage storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg instanceof AbstractLongStorage v) {
- long[] out = new long[storage.size()];
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- out[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), v.getItem(i), i, problemBuilder));
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new DoubleStorage(out, storage.size(), newMissing);
- } else if (arg instanceof DoubleStorage v) {
- long[] out = new long[storage.size()];
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- out[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), v.getItem(i), i, problemBuilder));
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new DoubleStorage(out, storage.size(), newMissing);
- } else {
- throw new UnexpectedTypeException("a Number.");
- }
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java
index 456ded42e76e..bad9bdef9215 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java
@@ -40,7 +40,7 @@ public Storage> runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec
for (int i = 0; i < storage.size(); i++) {
if (!storage.isNa(i)) {
- double item = storage.getItem(i);
+ double item = storage.getItemAsDouble(i);
boolean special = Double.isNaN(item) || Double.isInfinite(item);
if (!special) {
out[i] = (long) Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers);
@@ -62,7 +62,7 @@ public Storage> runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec
for (int i = 0; i < storage.size(); i++) {
if (!storage.isNa(i)) {
- double item = storage.getItem(i);
+ double item = storage.getItemAsDouble(i);
boolean special = Double.isNaN(item) || Double.isInfinite(item);
if (!special) {
doubleBuilder.appendDouble(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers));
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongBooleanOp.java
deleted file mode 100644
index a1373094b58b..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongBooleanOp.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.table.data.column.operation.map.BinaryMapOperation;
-import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
-import org.enso.table.data.column.storage.BoolStorage;
-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.DoubleStorage;
-import org.enso.table.error.UnexpectedTypeException;
-import org.graalvm.polyglot.Context;
-
-import java.util.BitSet;
-
-/**
- * An operation expecting a numeric argument and returning a boolean.
- */
-public abstract class LongBooleanOp extends BinaryMapOperation {
- public LongBooleanOp(String name) {
- super(name);
- }
-
- protected abstract boolean doLong(long a, long b);
-
- protected abstract boolean doDouble(long a, double b);
-
- protected boolean doObject(long a, Object b) {
- throw new UnexpectedTypeException("a Number");
- }
-
- @Override
- public BoolStorage runBinaryMap(AbstractLongStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg instanceof Long) {
- long x = (Long) arg;
- BitSet newVals = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- if (doLong(storage.getItem(i), x)) {
- newVals.set(i);
- }
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, storage.getIsMissing(), storage.size(), false);
- } else if (arg instanceof Double) {
- double x = (Double) arg;
- BitSet newVals = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- if (doDouble(storage.getItem(i), x)) {
- newVals.set(i);
- }
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, storage.getIsMissing(), storage.size(), false);
- } else {
- BitSet newVals = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- if (doObject(storage.getItem(i), arg)) {
- newVals.set(i);
- }
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, storage.getIsMissing(), storage.size(), false);
- }
- }
-
- @Override
- public BoolStorage runZip(AbstractLongStorage storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg instanceof DoubleStorage v) {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- if (doDouble(storage.getItem(i), v.getItem(i))) {
- newVals.set(i);
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- } else if (arg instanceof AbstractLongStorage v) {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- if (doLong(storage.getItem(i), v.getItem(i))) {
- newVals.set(i);
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- } else {
- BitSet newVals = new BitSet();
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < arg.size() && !arg.isNa(i)) {
- Object v = arg.getItemBoxed(i);
- if (v instanceof Long) {
- if (doLong(storage.getItem(i), (Long) v)) {
- newVals.set(i);
- }
- } else if (v instanceof Double) {
- if (doDouble(storage.getItem(i), (Double) v)) {
- newVals.set(i);
- }
- } else {
- if (doObject(storage.getItem(i), v)) {
- newVals.set(i);
- }
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new BoolStorage(newVals, newMissing, storage.size(), false);
- }
- }
-
- @Override
- public boolean reliesOnSpecializedStorage() {
- return false;
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongComparison.java
deleted file mode 100644
index cf61c7ec7855..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongComparison.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.base.CompareException;
-
-public abstract class LongComparison extends LongBooleanOp {
- public LongComparison(String name) {
- super(name);
- }
-
- @Override
- protected boolean doObject(long a, Object b) {
- throw new CompareException(a, b);
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongNumericOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongNumericOp.java
deleted file mode 100644
index 6b773ad0788c..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongNumericOp.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import org.enso.table.data.column.operation.map.BinaryMapOperation;
-import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
-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.DoubleStorage;
-import org.enso.table.data.column.storage.numeric.LongStorage;
-import org.enso.table.data.column.storage.numeric.NumericStorage;
-import org.enso.table.data.column.storage.type.IntegerType;
-import org.enso.table.error.UnexpectedTypeException;
-import org.enso.table.util.BitSets;
-import org.graalvm.polyglot.Context;
-
-import java.util.BitSet;
-
-/**
- * An operation expecting a numeric argument and returning a boolean.
- */
-public abstract class LongNumericOp extends BinaryMapOperation {
- private final boolean alwaysCastToDouble;
-
- // Regardless of input type, our operations return 64-bit integers.
- private static final IntegerType INTEGER_RESULT_TYPE = IntegerType.INT_64;
-
- public LongNumericOp(String name, boolean alwaysCastToDouble) {
- super(name);
- this.alwaysCastToDouble = alwaysCastToDouble;
- }
-
- public LongNumericOp(String name) {
- this(name, false);
- }
-
- public abstract double doDouble(long in, double arg, int ix, MapOperationProblemBuilder problemBuilder);
-
- public abstract Long doLong(long in, long arg, int ix, MapOperationProblemBuilder problemBuilder);
-
- @Override
- public NumericStorage> runBinaryMap(AbstractLongStorage storage, Object arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg == null) {
- if (alwaysCastToDouble) {
- return DoubleStorage.makeEmpty(storage.size());
- } else {
- return LongStorage.makeEmpty(storage.size(), INTEGER_RESULT_TYPE);
- }
- } else if (!alwaysCastToDouble && arg instanceof Long x) {
- BitSet newMissing = BitSets.makeDuplicate(storage.getIsMissing());
- long[] newVals = new long[storage.size()];
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- Long newVal = doLong(storage.getItem(i), x, i, problemBuilder);
- if (newVal == null) {
- newMissing.set(i);
- } else {
- newVals[i] = newVal;
- }
- }
-
- context.safepoint();
- }
-
- return new LongStorage(newVals, newVals.length, newMissing, INTEGER_RESULT_TYPE);
- } else if (arg instanceof Double || arg instanceof Long) {
- double x = (arg instanceof Double) ? (Double) arg : (Long) arg;
- long[] newVals = new long[storage.size()];
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- newVals[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), x, i, problemBuilder));
- }
-
- context.safepoint();
- }
- return new DoubleStorage(newVals, newVals.length, storage.getIsMissing());
- }
- throw new UnexpectedTypeException("a Number");
- }
-
- @Override
- public NumericStorage> runZip(AbstractLongStorage storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- if (arg instanceof AbstractLongStorage v) {
- long[] out = new long[storage.size()];
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- if (alwaysCastToDouble) {
- out[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), v.getItem(i), i, problemBuilder));
- } else {
- Long newVal = doLong(storage.getItem(i), v.getItem(i), i, problemBuilder);
- if (newVal == null) {
- newMissing.set(i);
- } else {
- out[i] = newVal;
- }
- }
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
-
- return alwaysCastToDouble ? new DoubleStorage(out, storage.size(), newMissing) : new LongStorage(out, storage.size(), newMissing, INTEGER_RESULT_TYPE);
- } else if (arg instanceof DoubleStorage v) {
- long[] out = new long[storage.size()];
- BitSet newMissing = new BitSet();
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i) && i < v.size() && !v.isNa(i)) {
- out[i] = Double.doubleToRawLongBits(doDouble(storage.getItem(i), v.getItem(i), i, problemBuilder));
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
- return new DoubleStorage(out, storage.size(), newMissing);
- } else {
- throw new UnexpectedTypeException("a Number.");
- }
- }
-}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/UnaryDoubleToLongOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/UnaryDoubleToLongOp.java
deleted file mode 100644
index b939e16e5fc3..000000000000
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/UnaryDoubleToLongOp.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.enso.table.data.column.operation.map.numeric;
-
-import java.util.BitSet;
-import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
-import org.enso.table.data.column.operation.map.UnaryMapOperation;
-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.type.IntegerType;
-import org.graalvm.polyglot.Context;
-
-/** An operation that takes a single double argumebnt and returns a long. */
-public abstract class UnaryDoubleToLongOp extends UnaryMapOperation {
-
- public UnaryDoubleToLongOp(String name) {
- super(name);
- }
-
- protected abstract long doOperation(double value);
-
- @Override
- protected LongStorage runUnaryMap(
- DoubleStorage storage, MapOperationProblemBuilder problemBuilder) {
- Context context = Context.getCurrent();
- BitSet newMissing = new BitSet();
- long[] newVals = new long[storage.size()];
- for (int i = 0; i < storage.size(); i++) {
- if (!storage.isNa(i)) {
- newVals[i] = doOperation(storage.getItem(i));
- } else {
- newMissing.set(i);
- }
-
- context.safepoint();
- }
-
- return new LongStorage(newVals, newVals.length, newMissing, IntegerType.INT_64);
- }
-}
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
new file mode 100644
index 000000000000..5296ec951616
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/AddOp.java
@@ -0,0 +1,34 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+import org.enso.table.data.column.storage.type.IntegerType;
+
+public class AddOp>
+ extends NumericBinaryOpImplementation {
+ public AddOp() {
+ super(Storage.Maps.ADD);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return a + b;
+ }
+
+ @Override
+ public Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder) {
+ try {
+ return Math.addExact(a, b);
+ } catch (ArithmeticException e) {
+ problemBuilder.reportOverflow(IntegerType.INT_64, a, "+", b);
+ return null;
+ }
+ }
+
+ @Override
+ public BigInteger doBigInteger(
+ BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return a.add(b);
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/DivideOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/DivideOp.java
new file mode 100644
index 000000000000..e78915b6d590
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/DivideOp.java
@@ -0,0 +1,19 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+
+public class DivideOp>
+ extends NumericBinaryOpReturningDouble {
+ public DivideOp() {
+ super(Storage.Maps.DIV);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ if (b == 0.0) {
+ problemBuilder.reportDivisionByZero(ix);
+ }
+ return a / b;
+ }
+}
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
new file mode 100644
index 000000000000..7edfcec7fd1f
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/ModOp.java
@@ -0,0 +1,42 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+
+public class ModOp>
+ extends NumericBinaryOpImplementation {
+ public ModOp() {
+ super(Storage.Maps.MOD);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ if (b == 0.0) {
+ problemBuilder.reportDivisionByZero(ix);
+ }
+
+ return a % b;
+ }
+
+ @Override
+ public Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder) {
+ if (b == 0) {
+ problemBuilder.reportDivisionByZero(ix);
+ return null;
+ }
+
+ return a % b;
+ }
+
+ @Override
+ public BigInteger doBigInteger(
+ BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder) {
+ if (b.equals(BigInteger.ZERO)) {
+ problemBuilder.reportDivisionByZero(ix);
+ return null;
+ }
+
+ return a.mod(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
new file mode 100644
index 000000000000..be5961003357
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/MulOp.java
@@ -0,0 +1,34 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+import org.enso.table.data.column.storage.type.IntegerType;
+
+public class MulOp>
+ extends NumericBinaryOpImplementation {
+ public MulOp() {
+ super(Storage.Maps.MUL);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return a * b;
+ }
+
+ @Override
+ public Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder) {
+ try {
+ return Math.multiplyExact(a, b);
+ } catch (ArithmeticException e) {
+ problemBuilder.reportOverflow(IntegerType.INT_64, a, "*", b);
+ return null;
+ }
+ }
+
+ @Override
+ public BigInteger doBigInteger(
+ BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder) {
+ 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
new file mode 100644
index 000000000000..d4f93aeedfbd
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpDefinition.java
@@ -0,0 +1,13 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+
+public interface NumericBinaryOpDefinition {
+ double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder);
+
+ Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder);
+
+ BigInteger doBigInteger(
+ BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder);
+}
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
new file mode 100644
index 000000000000..353bfadfe8c5
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpImplementation.java
@@ -0,0 +1,269 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import org.enso.base.polyglot.NumericConverter;
+import org.enso.table.data.column.operation.map.BinaryMapOperation;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+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.BigIntegerStorage;
+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.type.IntegerType;
+import org.enso.table.error.UnexpectedTypeException;
+import org.graalvm.polyglot.Context;
+
+import java.math.BigInteger;
+import java.util.BitSet;
+
+import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage;
+
+/**
+ * An operation expecting a numeric argument and returning a numeric column.
+ */
+public abstract class NumericBinaryOpImplementation> extends BinaryMapOperation implements NumericBinaryOpDefinition {
+
+ // The type to use for small integer results (regardless of the input bit size).
+ public static final IntegerType INTEGER_RESULT_TYPE = IntegerType.INT_64;
+
+ public NumericBinaryOpImplementation(String name) {
+ super(name);
+ }
+
+ @Override
+ public Storage extends Number> runBinaryMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) {
+ if (arg == null) {
+ return allNullStorageOfSameType(storage);
+ } else {
+ if (arg instanceof BigInteger rhs) {
+ return switch (storage) {
+ case AbstractLongStorage s -> runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), rhs, problemBuilder);
+ case BigIntegerStorage s -> runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), rhs, problemBuilder);
+ case DoubleStorage s -> runDoubleMap(s, rhs.doubleValue(), problemBuilder);
+ case default ->
+ throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ } else if (NumericConverter.isCoercibleToLong(arg)) {
+ long argAsLong = NumericConverter.coerceToLong(arg);
+ return switch (storage) {
+ case AbstractLongStorage s -> runLongMap(s, argAsLong, problemBuilder);
+ case BigIntegerStorage s ->
+ runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), BigInteger.valueOf(argAsLong), problemBuilder);
+ case DoubleStorage s -> runDoubleMap(s, (double) argAsLong, problemBuilder);
+ case default ->
+ throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ } else if (NumericConverter.isCoercibleToDouble(arg)) {
+ double doubleArg = NumericConverter.coerceToDouble(arg);
+ return switch (storage) {
+ case AbstractLongStorage s -> runDoubleMap(DoubleArrayAdapter.fromStorage(s), doubleArg, problemBuilder);
+ case BigIntegerStorage s -> runDoubleMap(DoubleArrayAdapter.fromStorage(s), doubleArg, problemBuilder);
+ case DoubleStorage s -> runDoubleMap(s, doubleArg, problemBuilder);
+ case default ->
+ throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ } else {
+ throw new UnexpectedTypeException("a Number.");
+ }
+ }
+ }
+
+ @Override
+ public Storage extends Number> runZip(I storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
+ return switch (storage) {
+ case DoubleStorage lhs -> runDoubleZip(lhs, fromAnyStorage(arg), problemBuilder);
+
+ case AbstractLongStorage lhs -> switch (arg) {
+ case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemBuilder);
+ case BigIntegerStorage rhs -> {
+ BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case DoubleStorage rhs -> runDoubleZip(DoubleArrayAdapter.fromStorage(lhs), rhs, problemBuilder);
+ case default -> throw new IllegalStateException("Unsupported storage: " + arg.getClass().getCanonicalName());
+ };
+
+ case BigIntegerStorage lhs -> {
+ BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
+ yield switch (arg) {
+ case AbstractLongStorage rhs -> {
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case BigIntegerStorage rhs -> {
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case DoubleStorage rhs -> runDoubleZip(DoubleArrayAdapter.fromStorage(lhs), rhs, problemBuilder);
+ case default -> throw new IllegalStateException("Unsupported storage: " + arg.getClass().getCanonicalName());
+ };
+ }
+
+ case default -> throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ }
+
+ protected DoubleStorage runDoubleZip(DoubleArrayAdapter a, DoubleArrayAdapter b,
+ MapOperationProblemBuilder problemBuilder) {
+ Context context = Context.getCurrent();
+ int n = a.size();
+ int m = Math.min(a.size(), b.size());
+ long[] out = new long[n];
+ BitSet newMissing = new BitSet();
+ for (int i = 0; i < m; i++) {
+ if (a.isNa(i) || b.isNa(i)) {
+ newMissing.set(i);
+ } else {
+ double r = doDouble(a.getItemAsDouble(i), b.getItemAsDouble(i), i, problemBuilder);
+ out[i] = Double.doubleToRawLongBits(r);
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ newMissing.set(m, n);
+ }
+
+ return new DoubleStorage(out, n, newMissing);
+ }
+
+ private static Storage extends Number> allNullStorageOfSameType(Storage> storage) {
+ return switch (storage) {
+ case AbstractLongStorage s -> LongStorage.makeEmpty(storage.size(), INTEGER_RESULT_TYPE);
+ case BigIntegerStorage s -> BigIntegerStorage.makeEmpty(storage.size());
+ case DoubleStorage s -> DoubleStorage.makeEmpty(storage.size());
+ case default -> throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ }
+
+ protected DoubleStorage runDoubleMap(DoubleArrayAdapter a, Double b, MapOperationProblemBuilder problemBuilder) {
+ if (b == null) {
+ return DoubleStorage.makeEmpty(a.size());
+ }
+
+ double bNonNull = b;
+ Context context = Context.getCurrent();
+ int n = a.size();
+ long[] out = new long[n];
+ BitSet newMissing = new BitSet();
+ for (int i = 0; i < n; i++) {
+ if (a.isNa(i)) {
+ newMissing.set(i);
+ } else {
+ double r = doDouble(a.getItemAsDouble(i), bNonNull, i, problemBuilder);
+ out[i] = Double.doubleToRawLongBits(r);
+ }
+
+ context.safepoint();
+ }
+
+ return new DoubleStorage(out, n, newMissing);
+ }
+
+ protected LongStorage runLongZip(AbstractLongStorage a, AbstractLongStorage b,
+ MapOperationProblemBuilder problemBuilder) {
+ Context context = Context.getCurrent();
+ int n = a.size();
+ int m = Math.min(a.size(), b.size());
+ long[] out = new long[n];
+ BitSet newMissing = new BitSet();
+ for (int i = 0; i < m; i++) {
+ if (a.isNa(i) || b.isNa(i)) {
+ newMissing.set(i);
+ } else {
+ Long r = doLong(a.getItem(i), b.getItem(i), i, problemBuilder);
+ if (r == null) {
+ newMissing.set(i);
+ } else {
+ out[i] = r;
+ }
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ newMissing.set(m, n);
+ }
+
+ return new LongStorage(out, n, newMissing, INTEGER_RESULT_TYPE);
+ }
+
+ protected LongStorage runLongMap(AbstractLongStorage a, Long b, MapOperationProblemBuilder problemBuilder) {
+ if (b == null) {
+ return LongStorage.makeEmpty(a.size(), INTEGER_RESULT_TYPE);
+ }
+
+ long bNonNull = b;
+ Context context = Context.getCurrent();
+ int n = a.size();
+ long[] out = new long[n];
+ BitSet newMissing = new BitSet();
+ for (int i = 0; i < n; i++) {
+ if (a.isNa(i)) {
+ newMissing.set(i);
+ } else {
+ Long r = doLong(a.getItem(i), bNonNull, i, problemBuilder);
+ if (r == null) {
+ newMissing.set(i);
+ } else {
+ out[i] = r;
+ }
+ }
+
+ context.safepoint();
+ }
+
+ return new LongStorage(out, n, newMissing, INTEGER_RESULT_TYPE);
+ }
+
+ protected BigIntegerStorage runBigIntegerZip(BigIntegerArrayAdapter a, BigIntegerArrayAdapter b,
+ MapOperationProblemBuilder problemBuilder) {
+ Context context = Context.getCurrent();
+ int n = a.size();
+ int m = Math.min(a.size(), b.size());
+ BigInteger[] out = new BigInteger[n];
+ BitSet newMissing = new BitSet();
+ for (int i = 0; i < m; i++) {
+ BigInteger x = a.getItem(i);
+ BigInteger y = b.getItem(i);
+ if (x == null || y == null) {
+ newMissing.set(i);
+ } else {
+ BigInteger r = doBigInteger(x, y, i, problemBuilder);
+ out[i] = r;
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ newMissing.set(m, n);
+ }
+
+ return new BigIntegerStorage(out, n);
+ }
+
+ protected BigIntegerStorage runBigIntegerMap(BigIntegerArrayAdapter a, BigInteger b,
+ MapOperationProblemBuilder problemBuilder) {
+ Context context = Context.getCurrent();
+ int n = a.size();
+ BigInteger[] out = new BigInteger[n];
+ for (int i = 0; i < n; i++) {
+ BigInteger x = a.getItem(i);
+ if (x == null || b == null) {
+ out[i] = null;
+ } else {
+ BigInteger r = doBigInteger(x, b, i, problemBuilder);
+ out[i] = r;
+ }
+
+ context.safepoint();
+ }
+
+ return new BigIntegerStorage(out, n);
+ }
+}
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
new file mode 100644
index 000000000000..01d93e6ca753
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/NumericBinaryOpReturningDouble.java
@@ -0,0 +1,46 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import org.enso.base.polyglot.NumericConverter;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+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.DoubleStorage;
+
+import java.math.BigInteger;
+
+import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage;
+
+public abstract class NumericBinaryOpReturningDouble> extends NumericBinaryOpImplementation {
+ public NumericBinaryOpReturningDouble(String name) {
+ super(name);
+ }
+
+ @Override
+ public Storage extends Number> runBinaryMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) {
+ if (arg == null) {
+ return DoubleStorage.makeEmpty(storage.size());
+ }
+
+ DoubleArrayAdapter lhs = fromAnyStorage(storage);
+ double rhs = (arg instanceof BigInteger bigInteger) ? bigInteger.doubleValue() :
+ NumericConverter.coerceToDouble(arg);
+ return runDoubleMap(lhs, rhs, problemBuilder);
+ }
+
+ @Override
+ public Storage extends Number> runZip(I storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
+ DoubleArrayAdapter lhs = fromAnyStorage(storage);
+ DoubleArrayAdapter rhs = fromAnyStorage(arg);
+ return runDoubleZip(lhs, rhs, problemBuilder);
+ }
+
+ @Override
+ public Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder) {
+ throw new IllegalStateException("Impossible: should not reach here - a NumericOpReturningDouble should always use the doDouble branch.");
+ }
+
+ @Override
+ public BigInteger doBigInteger(BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder) {
+ 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/PowerOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/PowerOp.java
new file mode 100644
index 000000000000..62a7efa93775
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/PowerOp.java
@@ -0,0 +1,16 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+
+public class PowerOp>
+ extends NumericBinaryOpReturningDouble {
+ public PowerOp() {
+ super(Storage.Maps.POWER);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return Math.pow(a, b);
+ }
+}
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
new file mode 100644
index 000000000000..c9a89d1cec63
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/arithmetic/SubOp.java
@@ -0,0 +1,34 @@
+package org.enso.table.data.column.operation.map.numeric.arithmetic;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.storage.Storage;
+import org.enso.table.data.column.storage.type.IntegerType;
+
+public class SubOp>
+ extends NumericBinaryOpImplementation {
+ public SubOp() {
+ super(Storage.Maps.SUB);
+ }
+
+ @Override
+ public double doDouble(double a, double b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return a - b;
+ }
+
+ @Override
+ public Long doLong(long a, long b, int ix, MapOperationProblemBuilder problemBuilder) {
+ try {
+ return Math.subtractExact(a, b);
+ } catch (ArithmeticException e) {
+ problemBuilder.reportOverflow(IntegerType.INT_64, a, "-", b);
+ return null;
+ }
+ }
+
+ @Override
+ public BigInteger doBigInteger(
+ BigInteger a, BigInteger b, int ix, MapOperationProblemBuilder problemBuilder) {
+ return a.subtract(b);
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java
new file mode 100644
index 000000000000..4d5a6e76c8f5
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/EqualsComparison.java
@@ -0,0 +1,48 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+import org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter;
+import org.enso.table.data.column.storage.BoolStorage;
+import org.enso.table.data.column.storage.Storage;
+
+public class EqualsComparison>
+ extends NumericComparison {
+ public EqualsComparison() {
+ super(Storage.Maps.EQ);
+ }
+
+ @Override
+ protected boolean doDouble(double a, double b) {
+ return a == b;
+ }
+
+ @Override
+ protected BoolStorage runDoubleMap(
+ DoubleArrayAdapter lhs, double rhs, MapOperationProblemBuilder problemBuilder) {
+ problemBuilder.reportFloatingPointEquality(-1);
+ return super.runDoubleMap(lhs, rhs, problemBuilder);
+ }
+
+ @Override
+ protected BoolStorage runDoubleZip(
+ DoubleArrayAdapter lhs, DoubleArrayAdapter rhs, MapOperationProblemBuilder problemBuilder) {
+ problemBuilder.reportFloatingPointEquality(-1);
+ return super.runDoubleZip(lhs, rhs, problemBuilder);
+ }
+
+ @Override
+ protected boolean doLong(long a, long b) {
+ return a == b;
+ }
+
+ @Override
+ protected boolean doBigInteger(BigInteger a, BigInteger b) {
+ return a.equals(b);
+ }
+
+ @Override
+ protected boolean onOtherType(Object a, Object b) {
+ return false;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterComparison.java
new file mode 100644
index 000000000000..b7155432a9b3
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterComparison.java
@@ -0,0 +1,26 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.storage.Storage;
+
+public class GreaterComparison>
+ extends NumericComparison {
+ public GreaterComparison() {
+ super(Storage.Maps.GT);
+ }
+
+ @Override
+ protected boolean doDouble(double a, double b) {
+ return a > b;
+ }
+
+ @Override
+ protected boolean doLong(long a, long b) {
+ return a > b;
+ }
+
+ @Override
+ protected boolean doBigInteger(BigInteger a, BigInteger b) {
+ return a.compareTo(b) > 0;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterOrEqualComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterOrEqualComparison.java
new file mode 100644
index 000000000000..9ab0ee3a849f
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/GreaterOrEqualComparison.java
@@ -0,0 +1,26 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.storage.Storage;
+
+public class GreaterOrEqualComparison>
+ extends NumericComparison {
+ public GreaterOrEqualComparison() {
+ super(Storage.Maps.GTE);
+ }
+
+ @Override
+ protected boolean doDouble(double a, double b) {
+ return a >= b;
+ }
+
+ @Override
+ protected boolean doLong(long a, long b) {
+ return a >= b;
+ }
+
+ @Override
+ protected boolean doBigInteger(BigInteger a, BigInteger b) {
+ return a.compareTo(b) >= 0;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessComparison.java
new file mode 100644
index 000000000000..1aa34a517aed
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessComparison.java
@@ -0,0 +1,26 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.storage.Storage;
+
+public class LessComparison>
+ extends NumericComparison {
+ public LessComparison() {
+ super(Storage.Maps.LT);
+ }
+
+ @Override
+ protected boolean doDouble(double a, double b) {
+ return a < b;
+ }
+
+ @Override
+ protected boolean doLong(long a, long b) {
+ return a < b;
+ }
+
+ @Override
+ protected boolean doBigInteger(BigInteger a, BigInteger b) {
+ return a.compareTo(b) < 0;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessOrEqualComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessOrEqualComparison.java
new file mode 100644
index 000000000000..22c80c32e56b
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/LessOrEqualComparison.java
@@ -0,0 +1,26 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.storage.Storage;
+
+public class LessOrEqualComparison>
+ extends NumericComparison {
+ public LessOrEqualComparison() {
+ super(Storage.Maps.LTE);
+ }
+
+ @Override
+ protected boolean doDouble(double a, double b) {
+ return a <= b;
+ }
+
+ @Override
+ protected boolean doLong(long a, long b) {
+ return a <= b;
+ }
+
+ @Override
+ protected boolean doBigInteger(BigInteger a, BigInteger b) {
+ return a.compareTo(b) <= 0;
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java
new file mode 100644
index 000000000000..4b60f200e254
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/comparisons/NumericComparison.java
@@ -0,0 +1,333 @@
+package org.enso.table.data.column.operation.map.numeric.comparisons;
+
+import org.enso.base.CompareException;
+import org.enso.base.polyglot.NumericConverter;
+import org.enso.table.data.column.operation.map.BinaryMapOperation;
+import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
+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.BoolStorage;
+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.BigIntegerStorage;
+import org.enso.table.data.column.storage.numeric.DoubleStorage;
+import org.enso.table.data.column.storage.type.AnyObjectType;
+import org.enso.table.util.BitSets;
+import org.graalvm.polyglot.Context;
+
+import java.math.BigInteger;
+import java.util.BitSet;
+
+import static org.enso.table.data.column.operation.map.numeric.helpers.DoubleArrayAdapter.fromAnyStorage;
+
+public abstract class NumericComparison> extends BinaryMapOperation {
+
+ protected abstract boolean doDouble(double a, double b);
+
+ protected abstract boolean doLong(long a, long b);
+
+ protected abstract boolean doBigInteger(BigInteger a, BigInteger b);
+
+ protected boolean onOtherType(Object a, Object b) {
+ throw new CompareException(a, b);
+ }
+
+ public NumericComparison(String name) {
+ super(name);
+ }
+
+ @Override
+ public BoolStorage runBinaryMap(I storage, Object arg, MapOperationProblemBuilder problemBuilder) {
+ if (arg == null) {
+ return BoolStorage.makeEmpty(storage.size());
+ } else if (arg instanceof BigInteger bigInteger) {
+ return switch (storage) {
+ case AbstractLongStorage s ->
+ runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), bigInteger, problemBuilder);
+ case BigIntegerStorage s -> runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), bigInteger, problemBuilder);
+ case DoubleStorage s -> runDoubleMap(s, bigInteger.doubleValue(), problemBuilder);
+ default -> throw new IllegalStateException("Unsupported lhs storage: " + storage.getClass().getCanonicalName());
+ };
+ } else if (NumericConverter.isCoercibleToLong(arg)) {
+ long rhs = NumericConverter.coerceToLong(arg);
+ return switch (storage) {
+ case AbstractLongStorage s -> runLongMap(s, rhs, problemBuilder);
+ case BigIntegerStorage s ->
+ runBigIntegerMap(BigIntegerArrayAdapter.fromStorage(s), BigInteger.valueOf(rhs), problemBuilder);
+ case DoubleStorage s -> runDoubleMap(s, (double) rhs, problemBuilder);
+ default -> throw new IllegalStateException("Unsupported lhs storage: " + storage.getClass().getCanonicalName());
+ };
+ } else if (NumericConverter.isCoercibleToDouble(arg)) {
+ DoubleArrayAdapter lhs = DoubleArrayAdapter.fromAnyStorage(storage);
+ double rhs = NumericConverter.coerceToDouble(arg);
+ return runDoubleMap(lhs, rhs, problemBuilder);
+ } else {
+ int n = storage.size();
+ BitSet missing = new BitSet();
+ BitSet comparisonResults = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < n; ++i) {
+ Object item = storage.getItemBoxed(i);
+ if (item == null) {
+ missing.set(i);
+ } else {
+ boolean r = onOtherType(item, arg);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+ }
+
+ protected BoolStorage runLongMap(AbstractLongStorage lhs, long rhs, MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = BitSets.makeDuplicate(lhs.getIsMissing());
+ Context context = Context.getCurrent();
+ for (int i = 0; i < n; ++i) {
+ if (!lhs.isNa(i)) {
+ long item = lhs.getItem(i);
+ boolean r = doLong(item, rhs);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ protected BoolStorage runDoubleMap(DoubleArrayAdapter lhs, double rhs, MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < n; ++i) {
+ if (lhs.isNa(i)) {
+ missing.set(i);
+ } else {
+ double item = lhs.getItemAsDouble(i);
+ boolean r = doDouble(item, rhs);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ protected BoolStorage runBigIntegerMap(BigIntegerArrayAdapter lhs, BigInteger rhs,
+ MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < n; ++i) {
+ BigInteger item = lhs.getItem(i);
+ if (item == null) {
+ missing.set(i);
+ } else {
+ boolean r = doBigInteger(item, rhs);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ @Override
+ public BoolStorage runZip(I storage, Storage> arg, MapOperationProblemBuilder problemBuilder) {
+ return switch (storage) {
+ case DoubleStorage lhs -> {
+ if (arg.getType() instanceof AnyObjectType) {
+ yield runMixedZip(lhs, arg, problemBuilder);
+ } else {
+ yield runDoubleZip(lhs, fromAnyStorage(arg), problemBuilder);
+ }
+ }
+
+ case AbstractLongStorage lhs -> switch (arg) {
+ case AbstractLongStorage rhs -> runLongZip(lhs, rhs, problemBuilder);
+ case BigIntegerStorage rhs -> {
+ BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case DoubleStorage rhs -> runDoubleZip(DoubleArrayAdapter.fromStorage(lhs), rhs, problemBuilder);
+ case default -> runMixedZip(lhs, arg, problemBuilder);
+ };
+
+ case BigIntegerStorage lhs -> {
+ BigIntegerArrayAdapter left = BigIntegerArrayAdapter.fromStorage(lhs);
+ yield switch (arg) {
+ case AbstractLongStorage rhs -> {
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case BigIntegerStorage rhs -> {
+ BigIntegerArrayAdapter right = BigIntegerArrayAdapter.fromStorage(rhs);
+ yield runBigIntegerZip(left, right, problemBuilder);
+ }
+ case DoubleStorage rhs -> runDoubleZip(DoubleArrayAdapter.fromStorage(lhs), rhs, problemBuilder);
+ case default -> runMixedZip(lhs, arg, problemBuilder);
+ };
+ }
+
+ case default ->
+ throw new IllegalStateException("Unsupported lhs storage: " + storage.getClass().getCanonicalName());
+ };
+ }
+
+ protected BoolStorage runLongZip(AbstractLongStorage lhs, AbstractLongStorage rhs,
+ MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ int m = Math.min(lhs.size(), rhs.size());
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < m; ++i) {
+ if (lhs.isNa(i) || rhs.isNa(i)) {
+ missing.set(i);
+ } else {
+ long x = lhs.getItem(i);
+ long y = rhs.getItem(i);
+ boolean r = doLong(x, y);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ missing.set(m, n);
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ protected BoolStorage runDoubleZip(DoubleArrayAdapter lhs, DoubleArrayAdapter rhs,
+ MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ int m = Math.min(lhs.size(), rhs.size());
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < m; ++i) {
+ if (lhs.isNa(i) || rhs.isNa(i)) {
+ missing.set(i);
+ } else {
+ double x = lhs.getItemAsDouble(i);
+ double y = rhs.getItemAsDouble(i);
+ boolean r = doDouble(x, y);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ missing.set(m, n);
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ protected BoolStorage runBigIntegerZip(BigIntegerArrayAdapter lhs, BigIntegerArrayAdapter rhs,
+ MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ int m = Math.min(lhs.size(), rhs.size());
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < m; ++i) {
+ BigInteger x = lhs.getItem(i);
+ BigInteger y = rhs.getItem(i);
+ if (x == null || y == null) {
+ missing.set(i);
+ } else {
+ boolean r = doBigInteger(x, y);
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ missing.set(m, n);
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+
+ protected BoolStorage runMixedZip(Storage> lhs, Storage> rhs, MapOperationProblemBuilder problemBuilder) {
+ int n = lhs.size();
+ int m = Math.min(lhs.size(), rhs.size());
+ BitSet comparisonResults = new BitSet();
+ BitSet missing = new BitSet();
+ Context context = Context.getCurrent();
+ for (int i = 0; i < m; ++i) {
+ Object x = lhs.getItemBoxed(i);
+ Object y = rhs.getItemBoxed(i);
+ if (x == null || y == null) {
+ missing.set(i);
+ } else {
+ boolean r = false;
+ // Any number is coercible to double, if the value is not coercible, it is not a supported number type.
+ if (NumericConverter.isCoercibleToDouble(x) && NumericConverter.isCoercibleToDouble(y)) {
+
+ // If any of the values is decimal like, then decimal type is used for comparison.
+ if (NumericConverter.isDecimalLike(x) || NumericConverter.isDecimalLike(y)) {
+ double a = NumericConverter.coerceToDouble(x);
+ double b = NumericConverter.coerceToDouble(y);
+ r = doDouble(a, b);
+ } else {
+ if (x instanceof BigInteger || y instanceof BigInteger) {
+ BigInteger a = NumericConverter.coerceToBigInteger(x);
+ BigInteger b = NumericConverter.coerceToBigInteger(y);
+ r = doBigInteger(a, b);
+ } else {
+ long a = NumericConverter.coerceToLong(x);
+ long b = NumericConverter.coerceToLong(y);
+ r = doLong(a, b);
+ }
+ }
+ } else {
+ r = onOtherType(x, y);
+ }
+
+ if (r) {
+ comparisonResults.set(i);
+ }
+ }
+
+ context.safepoint();
+ }
+
+ if (m < n) {
+ missing.set(m, n);
+ }
+
+ return new BoolStorage(comparisonResults, missing, n, false);
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java
new file mode 100644
index 000000000000..62f2bbc919c2
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/BigIntegerArrayAdapter.java
@@ -0,0 +1,60 @@
+package org.enso.table.data.column.operation.map.numeric.helpers;
+
+import java.math.BigInteger;
+import org.enso.table.data.column.storage.numeric.AbstractLongStorage;
+import org.enso.table.data.column.storage.numeric.BigIntegerStorage;
+
+public interface BigIntegerArrayAdapter {
+ BigInteger getItem(int i);
+
+ int size();
+
+ static BigIntegerArrayAdapter fromStorage(BigIntegerStorage storage) {
+ return new BigIntegerStorageAsBigInteger(storage);
+ }
+
+ static BigIntegerArrayAdapter fromStorage(AbstractLongStorage storage) {
+ return new LongStorageAsBigInteger(storage);
+ }
+
+ class BigIntegerStorageAsBigInteger implements BigIntegerArrayAdapter {
+ private final BigIntegerStorage storage;
+
+ private BigIntegerStorageAsBigInteger(BigIntegerStorage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public BigInteger getItem(int i) {
+ return storage.getItemBoxed(i);
+ }
+
+ @Override
+ public int size() {
+ return storage.size();
+ }
+ }
+
+ class LongStorageAsBigInteger implements BigIntegerArrayAdapter {
+ private final AbstractLongStorage storage;
+
+ private LongStorageAsBigInteger(AbstractLongStorage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public BigInteger getItem(int i) {
+ if (storage.isNa(i)) {
+ return null;
+ } else {
+ long x = storage.getItem(i);
+ return BigInteger.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
new file mode 100644
index 000000000000..7e4702068732
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/helpers/DoubleArrayAdapter.java
@@ -0,0 +1,85 @@
+package org.enso.table.data.column.operation.map.numeric.helpers;
+
+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.BigIntegerStorage;
+import org.enso.table.data.column.storage.numeric.DoubleStorage;
+
+import java.math.BigInteger;
+
+public interface DoubleArrayAdapter {
+ double getItemAsDouble(int i);
+
+ boolean isNa(int i);
+
+ int size();
+
+ static DoubleArrayAdapter fromStorage(BigIntegerStorage storage) {
+ return new BigIntegerStorageAsDouble(storage);
+ }
+
+ static DoubleArrayAdapter fromStorage(AbstractLongStorage storage) {
+ return new LongStorageAsDouble(storage);
+ }
+
+ static DoubleArrayAdapter fromStorage(DoubleStorage storage) {
+ return storage;
+ }
+
+ static DoubleArrayAdapter fromAnyStorage(Storage> storage) {
+ return switch (storage) {
+ case DoubleStorage s -> fromStorage(s);
+ case AbstractLongStorage s -> fromStorage(s);
+ case BigIntegerStorage s -> fromStorage(s);
+ case default -> throw new IllegalStateException("Unsupported storage: " + storage.getClass().getCanonicalName());
+ };
+ }
+
+ class LongStorageAsDouble implements DoubleArrayAdapter {
+ private final AbstractLongStorage storage;
+
+ private LongStorageAsDouble(AbstractLongStorage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public double getItemAsDouble(int i) {
+ long x = storage.getItem(i);
+ return (double) x;
+ }
+
+ @Override
+ public boolean isNa(int i) {
+ return storage.isNa(i);
+ }
+
+ @Override
+ public int size() {
+ return storage.size();
+ }
+ }
+
+ class BigIntegerStorageAsDouble implements DoubleArrayAdapter {
+ private final BigIntegerStorage storage;
+
+ private BigIntegerStorageAsDouble(BigIntegerStorage storage) {
+ this.storage = storage;
+ }
+
+ @Override
+ public double getItemAsDouble(int i) {
+ BigInteger x = storage.getItem(i);
+ return x.doubleValue();
+ }
+
+ @Override
+ public boolean isNa(int 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/operation/map/numeric/isin/BigIntegerIsInOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/BigIntegerIsInOp.java
new file mode 100644
index 000000000000..929641918671
--- /dev/null
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/BigIntegerIsInOp.java
@@ -0,0 +1,36 @@
+package org.enso.table.data.column.operation.map.numeric.isin;
+
+import org.enso.base.polyglot.NumericConverter;
+import org.enso.table.data.column.operation.map.SpecializedIsInOp;
+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.BigIntegerStorage;
+import org.graalvm.polyglot.Context;
+
+import java.math.BigInteger;
+import java.util.HashSet;
+import java.util.List;
+
+public class BigIntegerIsInOp> extends SpecializedIsInOp {
+ @Override
+ protected CompactRepresentation prepareList(List> list) {
+ Context context = Context.getCurrent();
+ HashSet set = new HashSet<>();
+ boolean hasNulls = false;
+ for (Object o : list) {
+ hasNulls |= o == null;
+
+ if (o instanceof BigInteger bigInteger) {
+ set.add(bigInteger);
+ } else {
+ Long x = NumericConverter.tryConvertingToLong(o);
+ if (x != null) {
+ set.add(BigInteger.valueOf(x));
+ }
+ }
+
+ context.safepoint();
+ }
+ return new CompactRepresentation<>(set, hasNulls);
+ }
+}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleIsInOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/DoubleIsInOp.java
similarity index 92%
rename from std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleIsInOp.java
rename to std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/DoubleIsInOp.java
index 65f4eddbd566..f7cf3c76cb48 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleIsInOp.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/DoubleIsInOp.java
@@ -1,4 +1,4 @@
-package org.enso.table.data.column.operation.map.numeric;
+package org.enso.table.data.column.operation.map.numeric.isin;
import java.util.HashSet;
import java.util.List;
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongIsInOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/LongIsInOp.java
similarity index 92%
rename from std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongIsInOp.java
rename to std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/LongIsInOp.java
index 06102430c4ea..5c3d77196a1b 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongIsInOp.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/isin/LongIsInOp.java
@@ -1,4 +1,4 @@
-package org.enso.table.data.column.operation.map.numeric;
+package org.enso.table.data.column.operation.map.numeric.isin;
import java.util.HashSet;
import java.util.List;
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java
index 64292dac3fb6..eb4b05a5c3e2 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/BoolStorage.java
@@ -15,8 +15,8 @@
import org.enso.table.data.mask.SliceRange;
import org.enso.table.error.UnexpectedColumnTypeException;
import org.enso.table.error.UnexpectedTypeException;
+import org.enso.table.problems.AggregatedProblems;
import org.enso.table.problems.WithAggregatedProblems;
-import org.enso.table.problems.WithProblems;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
@@ -45,6 +45,10 @@ public static BoolStorage makeEmpty(int size) {
return new BoolStorage(new BitSet(), isMissing, size, false);
}
+ public static BoolStorage makeConstant(int size, boolean r) {
+ return new BoolStorage(new BitSet(), new BitSet(), size, r);
+ }
+
@Override
public int size() {
return size;
@@ -119,22 +123,23 @@ public BitSet getIsMissing() {
* accordingly. If `arg` is true, new values are `values || isMissing` and if `arg` is false, new
* values are `values && (~isMissing)`.
*/
- private BoolStorage fillMissingBoolean(boolean arg) {
+ private WithAggregatedProblems> fillMissingBoolean(boolean arg) {
final var newValues = (BitSet) values.clone();
if (arg) {
newValues.or(isMissing);
} else {
newValues.andNot(isMissing);
}
- return new BoolStorage(newValues, new BitSet(), size, negated);
+ var storage = new BoolStorage(newValues, new BitSet(), size, negated);
+ return new WithAggregatedProblems<>(storage, AggregatedProblems.of());
}
@Override
- public Storage> fillMissing(Value arg) {
+ public WithAggregatedProblems> fillMissing(Value arg, StorageType commonType) {
if (arg.isBoolean()) {
return fillMissingBoolean(arg.asBoolean());
} else {
- return super.fillMissing(arg);
+ return super.fillMissing(arg, commonType);
}
}
diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/MixedStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/MixedStorage.java
index 215f307f6104..0736c163c389 100644
--- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/MixedStorage.java
+++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/MixedStorage.java
@@ -4,6 +4,7 @@
import org.enso.table.data.column.builder.MixedBuilder;
import org.enso.table.data.column.operation.map.MapOperationProblemBuilder;
import org.enso.table.data.column.storage.type.AnyObjectType;
+import org.enso.table.data.column.storage.type.BigIntegerType;
import org.enso.table.data.column.storage.type.FloatType;
import org.enso.table.data.column.storage.type.IntegerType;
import org.enso.table.data.column.storage.type.StorageType;
@@ -48,6 +49,26 @@ protected SpecializedStorage