From ee11df17bfa3673ca4e776c3ddf874b41f8ddeff Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 1 Aug 2023 15:53:47 -0400 Subject: [PATCH 01/36] wip --- .../Table/0.0.0-dev/src/Data/Column.enso | 17 ++++++- .../table/data/column/storage/Storage.java | 1 + .../column/storage/numeric/DoubleStorage.java | 8 ++++ test/Benchmarks/src/Column_Numeric.enso | 44 +++++++++++-------- .../src/In_Memory/Column_Spec.enso | 2 +- 5 files changed, 50 insertions(+), 22 deletions(-) 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 9dbfa77f370c..f229800fc859 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 @@ -758,8 +758,8 @@ type Column Round a column of `Decimal` values`. Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) - round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False = + old_round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type + old_round self decimal_places=0 use_bankers=False = Rounding_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| # If it's an integer column and decimal_places >=0 then it's a no-op. new_name = Naming_Helpers.function_name "round" [self] @@ -794,6 +794,19 @@ type Column result = action (within_range.iif self Nothing) Warning.attach warning result + round : Column ! Invalid_Value_Type + round self = Value_Type.expect_numeric self <| + Illegal_Argument.handle_java_exception <| Arithmetic_Error.handle_java_exception <| + new_name = Naming_Helpers.function_name "round" [self] + is_integer = self.inferred_precise_value_type.is_integer + if is_integer && decimal_places >= 0 then self.rename new_name else + should_cast_to_integer = decimal_places <= 0 || is_integer + target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type + result = + result_float = simple_unary_op self Java_Storage.Maps.CEIL + cast_if_needed result_float target_value_type + result.rename new_name + ## ALIAS int If the column is numeric, truncate the floating-point values to an diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java index 9399052371a9..dd8cb9bb278d 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -80,6 +80,7 @@ public static final class Maps { public static final String TRUNCATE = "truncate"; public static final String CEIL = "ceil"; public static final String FLOOR = "floor"; + public static final String ROUND = "round"; public static final String NOT = "not"; public static final String AND = "&&"; public static final String OR = "||"; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index c59331a19c23..898a230781fe 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -2,6 +2,7 @@ import java.util.BitSet; import java.util.List; +import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.NumericBuilder; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; @@ -289,6 +290,13 @@ protected long doOperation(double a) { return (long) Math.floor(a); } }) + .add( + new DoubleNumericOp(Maps.ROUND) { + @Override + protected double doDouble(double n, int decimalPlaces, boolean useBankers) { + return Core_Math_Utils.roundDouble(n, decimalPlaces, use_bankers); + } + }) .add( new DoubleComparison(Maps.LT) { @Override diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index ea94b069d3d7..2bfb1e838847 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -5,8 +5,8 @@ from Standard.Test import Bench, Faker ## Bench Utilities ============================================================ -vector_size = 1000000 -iter_size = 100 +vector_size = 10000 +iter_size = 10 num_iterations = 10 # The Benchmarks ============================================================== @@ -25,22 +25,28 @@ bench = ints = Column.from_vector "ints" ints_vec IO.println <| ".round floats" - Bench.measure floats.round "Column.round floats" iter_size num_iterations - - IO.println <| ".truncate floats" - Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations - IO.println <| ".ceil floats" - Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations - IO.println <| ".floor floats" - Bench.measure floats.floor "Column.floor floats" iter_size num_iterations - - IO.println <| ".round ints" - Bench.measure ints.round "Column.round ints" iter_size num_iterations - IO.println <| ".truncate ints" - Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations - IO.println <| ".ceil ints" - Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations - IO.println <| ".floor ints" - Bench.measure ints.floor "Column.floor ints" iter_size num_iterations + Bench.measure floats.old_round "old Column.round floats" iter_size num_iterations + Bench.measure floats.round "new Column.round floats" iter_size num_iterations + + ## + IO.println <| ".truncate floats" + Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations + IO.println <| ".ceil floats" + Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations + IO.println <| ".floor floats" + Bench.measure floats.floor "Column.floor floats" iter_size num_iterations + + ## + IO.println <| ".round ints" + Bench.measure ints.old_round "old Column.round ints" iter_size num_iterations + Bench.measure ints.round "new Column.round ints" iter_size num_iterations + + ## + IO.println <| ".truncate ints" + Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations + IO.println <| ".ceil ints" + Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations + IO.println <| ".floor ints" + Bench.measure ints.floor "Column.floor ints" iter_size num_iterations main = bench diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 6a0229347484..a8dacbfb8d2b 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -183,7 +183,7 @@ spec = Test.specify "Should error on input of the wrong type" <| Column.from_vector "foo" ["asdf", "zxcv", "qwer"] . ceil . should_fail_with Invalid_Value_Type - Test.group "floor" <| + Test.group "floorasdfasdf" <| Test.specify "should be able to take the floor of a column of floats" <| Column.from_vector "foo" [1.25, 2.33, 3.57] . floor . should_equal <| Column.from_vector "floor([foo])" [1, 2, 3] Column.from_vector "foo" [1.25, 2.33, 3.57] . floor . value_type . should_equal Value_Type.Integer From 0740b4dd483a4185c555c0ba22f9afe97813e4b7 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 7 Aug 2023 15:28:19 -0400 Subject: [PATCH 02/36] some pass --- .../Table/0.0.0-dev/src/Data/Column.enso | 27 +++++++--- .../common_utils/Core_Math_Utils.java | 2 +- .../operation/map/MapOperationStorage.java | 46 +++++++++++++++-- .../operation/map/TernaryMapOperation.java | 37 ++++++++++++++ .../map/numeric/DoubleLongBooleanOp.java | 51 +++++++++++++++++++ .../table/data/column/storage/Storage.java | 27 +++++++++- .../column/storage/numeric/DoubleStorage.java | 18 +++++-- 7 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java create mode 100644 std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java 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 077bbf69ac94..561d1a1c9cb2 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 @@ -1,6 +1,7 @@ from Standard.Base import all import Standard.Base.Data.Array_Proxy.Array_Proxy import Standard.Base.Data.Index_Sub_Range as Index_Sub_Range_Module +import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Incomparable_Values import Standard.Base.Errors.Common.Index_Out_Of_Bounds import Standard.Base.Errors.Common.No_Such_Method @@ -796,18 +797,17 @@ type Column result = action (within_range.iif self Nothing) Warning.attach warning result - round : Column ! Invalid_Value_Type - round self = Value_Type.expect_numeric self <| + round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type + round self decimal_places=0 use_bankers=False = Value_Type.expect_numeric self <| Illegal_Argument.handle_java_exception <| Arithmetic_Error.handle_java_exception <| - new_name = Naming_Helpers.function_name "round" [self] + new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer if is_integer && decimal_places >= 0 then self.rename new_name else should_cast_to_integer = decimal_places <= 0 || is_integer target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type - result = - result_float = simple_unary_op self Java_Storage.Maps.CEIL - cast_if_needed result_float target_value_type - result.rename new_name + result_float = run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name expected_result_type=target_value_type + result = cast_if_needed result_float target_value_type + result #result.rename new_name ## ALIAS int @@ -2125,6 +2125,19 @@ run_vectorized_binary_op column name operand new_name=Nothing fallback_fn=Nothin Problem_Behavior.Report_Warning.attach_problems_after result <| Java_Problems.parse_aggregated_problems problem_builder.getProblems +run_vectorized_ternary_op : Column -> Text -> Any -> Any -> Text|Nothing -> Value_Type -> Boolean -> Column +run_vectorized_ternary_op column name operand0 operand1 new_name=Nothing expected_result_type=Nothing skip_nulls=True = + effective_new_name = new_name.if_nothing <| + naming_helper.function_name name [column, operand0, operand1] + problem_builder = MapOperationProblemBuilder.new column.name + storage_type = resolve_storage_type expected_result_type + s1 = column.java_column.getStorage + rs = Polyglot_Helpers.handle_polyglot_dataflow_errors <| + s1.vectorizedTernaryMap name problem_builder operand0 operand1 skip_nulls storage_type + result = Column.Value (Java_Column.new effective_new_name rs) + Problem_Behavior.Report_Warning.attach_problems_after result <| + Java_Problems.parse_aggregated_problems problem_builder.getProblems + ## PRIVATE Runs a binary operation over the provided column and operand which may be another column or a scalar value. diff --git a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java index a34e24eccadb..bc42008c2f4d 100644 --- a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java +++ b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java @@ -31,7 +31,7 @@ public class Core_Math_Utils { * @throws IllegalArgumentException if `n` is outside the allowed range. * @throws IllegalArgumentException if `decimalPlaces` is outside the allowed range. */ - public static double roundDouble(double n, int decimalPlaces, boolean useBankers) { + public static double roundDouble(double n, long decimalPlaces, boolean useBankers) { if (decimalPlaces < ROUND_MIN_DECIMAL_PLACES || decimalPlaces > ROUND_MAX_DECIMAL_PLACES) { String msg = "round: decimalPlaces must be between " diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java index 44c85906019a..143a09e0a2bf 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java @@ -11,8 +11,9 @@ * @param the storage type handled by these operations. */ public class MapOperationStorage> { - private final Map> binaryOps = new HashMap<>(); private final Map> unaryOps = new HashMap<>(); + private final Map> binaryOps = new HashMap<>(); + private final Map> ternaryOps = new HashMap<>(); /** * Checks if a unary operation is supported by this set. @@ -68,6 +69,34 @@ public Storage runBinaryMap( return binaryOps.get(n).runBinaryMap(storage, arg, problemBuilder); } + /** + * Checks if a binary operation is supported by this set. + * + * @param n the operation name + * @return whether the operation is supported + */ + public boolean isSupportedTernary(String n) { + return n != null && ternaryOps.get(n) != null; + } + + /** + * Runs the specified operation in map node. + * + * @param n the operation name + * @param storage the storage to run operation on + * @param arg the argument to pass to the operation + * @param problemBuilder the builder allowing to report computation problems + * @return the result of running the operation + */ + public Storage runTernaryMap( + String n, S storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { + if (!isSupportedTernary(n)) { + throw new IllegalStateException( + "Requested vectorized ternary operation " + n + ", but no such operation is known."); + } + return ternaryOps.get(n).runTernaryMap(storage, arg0, arg1, problemBuilder); + } + /** * Runs the specified operation in zip node. * @@ -94,6 +123,17 @@ public Storage runZip( return operation.runZip(storage, arg, problemBuilder); } + /** + * Adds a new operation to this set. + * + * @param op the operation to add + * @return this operation set + */ + public MapOperationStorage add(UnaryMapOperation op) { + unaryOps.put(op.getName(), op); + return this; + } + /** * Adds a new operation to this set. * @@ -111,8 +151,8 @@ public MapOperationStorage add(BinaryMapOperation op) { * @param op the operation to add * @return this operation set */ - public MapOperationStorage add(UnaryMapOperation op) { - unaryOps.put(op.getName(), op); + public MapOperationStorage add(TernaryMapOperation op) { + ternaryOps.put(op.getName(), op); return this; } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java new file mode 100644 index 000000000000..72ef02331185 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java @@ -0,0 +1,37 @@ +package org.enso.table.data.column.operation.map; + +import org.enso.table.data.column.storage.Storage; + +/** + * A representation of a map-like operation that can be performed on given storage types. + * + * @param the supported storage type. + */ +public abstract class TernaryMapOperation> { + private final String name; + + /** + * Creates a new operation with the given name. + * + * @param name the operation name + */ + public TernaryMapOperation(String name) { + this.name = name; + } + + /** + * Run the operation in map mode - combining every row of the storage with a scalar argument. + * + * @param storage the storage to run operation on + * @param arg the argument passed to the operation + * @param problemBuilder the builder allowing to report computation problems + * @return the result of running the operation + */ + public abstract Storage runTernaryMap( + I storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder); + + /** @return the name of this operation */ + public String getName() { + return name; + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java new file mode 100644 index 000000000000..95a49b555162 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java @@ -0,0 +1,51 @@ +package org.enso.table.data.column.operation.map.numeric; + +import org.enso.table.data.column.operation.map.TernaryMapOperation; +import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; +import org.enso.table.data.column.storage.numeric.DoubleStorage; +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 number. */ +public abstract class DoubleLongBooleanOp extends TernaryMapOperation { + + public DoubleLongBooleanOp(String name) { + super(name); + } + + protected abstract double doLongBoolean(double a, long b, boolean c, int ix, MapOperationProblemBuilder problemBuilder); + + @Override + public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { + if (arg0 == null || arg1 == null) { + return DoubleStorage.makeEmpty(storage.size()); + } + + long longArg; + if (arg0 instanceof Long) { + longArg = (Long) arg0; + } else { + throw new UnexpectedTypeException("a long."); + } + boolean booleanArg; + if (arg1 instanceof Boolean) { + booleanArg = (Boolean) arg1; + } else { + throw new UnexpectedTypeException("a boolean."); + } + + 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(doLongBoolean(storage.getItem(i), longArg, booleanArg, i, problemBuilder)); + } + + context.safepoint(); + } + return new DoubleStorage(out, storage.size(), storage.getIsMissing()); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java index dd8cb9bb278d..c49f9100d69c 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -118,7 +118,18 @@ public abstract Storage runVectorizedUnaryMap( /** Runs a vectorized operation on this storage, taking one scalar argument. */ public abstract Storage runVectorizedBinaryMap( - String name, Object argument, MapOperationProblemBuilder problemBuilder); + String name, Object argument, MapOperationProblemBuilder problemBuilder); + + /* Specifies if the given binary operation has a vectorized implementation available for this storage.*/ + public boolean isTernaryOpVectorized(String name) { + return false; + } + + /** Runs a vectorized operation on this storage, taking one scalar argument. */ + public Storage runVectorizedTernaryMap( + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + throw new IllegalArgumentException("Unsupported ternary operation: " + name); + } /** * Runs a vectorized operation on this storage, taking a storage as the right argument - @@ -283,6 +294,20 @@ public final Storage vectorizedOrFallbackBinaryMap( } } + public final Storage vectorizedTernaryMap( + String name, + MapOperationProblemBuilder problemBuilder, + Object argument0, + Object argument1, + boolean skipNulls, + StorageType expectedResultType) { + if (isTernaryOpVectorized(name)) { + return runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); + } else { + throw new IllegalArgumentException("Unsupported ternary operation: " + name); + } + } + /** * Runs a binary operation with a storage argument. * diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 898a230781fe..5e85e3caf78e 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -12,6 +12,7 @@ import org.enso.table.data.column.operation.map.numeric.DoubleComparison; import org.enso.table.data.column.operation.map.numeric.DoubleIsInOp; import org.enso.table.data.column.operation.map.numeric.DoubleLongMapOpWithSpecialNumericHandling; +import org.enso.table.data.column.operation.map.numeric.DoubleLongBooleanOp; import org.enso.table.data.column.operation.map.numeric.DoubleNumericOp; import org.enso.table.data.column.storage.BoolStorage; import org.enso.table.data.column.storage.Storage; @@ -111,6 +112,17 @@ public Storage runVectorizedBinaryMap( return ops.runBinaryMap(name, this, argument, problemBuilder); } + @Override + public boolean isTernaryOpVectorized(String op) { + return ops.isSupportedTernary(op); + } + + @Override + public Storage runVectorizedTernaryMap( + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + return ops.runTernaryMap(name, this, argument0, argument1, problemBuilder); + } + @Override public Storage runVectorizedZip( String name, Storage argument, MapOperationProblemBuilder problemBuilder) { @@ -291,10 +303,10 @@ protected long doOperation(double a) { } }) .add( - new DoubleNumericOp(Maps.ROUND) { + new DoubleLongBooleanOp(Maps.ROUND) { @Override - protected double doDouble(double n, int decimalPlaces, boolean useBankers) { - return Core_Math_Utils.roundDouble(n, decimalPlaces, use_bankers); + protected double doLongBoolean(double n, long decimalPlaces, boolean useBankers, int ix, MapOperationProblemBuilder problemBuilder) { + return Core_Math_Utils.roundDouble(n, decimalPlaces, useBankers); } }) .add( From 61ad15e3fedc00aef75c35133d0801cef0defa1c Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Mon, 7 Aug 2023 15:38:49 -0400 Subject: [PATCH 03/36] more tests pass --- ...ngBooleanOpWithSpecialNumericHandling.java} | 18 +++++++++++++++--- .../column/storage/numeric/DoubleStorage.java | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) rename std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/{DoubleLongBooleanOp.java => DoubleLongBooleanOpWithSpecialNumericHandling.java} (67%) diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java similarity index 67% rename from std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java rename to std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java index 95a49b555162..28b456d11119 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java @@ -10,9 +10,9 @@ import java.util.BitSet; /** An operation expecting a numeric argument and returning a number. */ -public abstract class DoubleLongBooleanOp extends TernaryMapOperation { +public abstract class DoubleLongBooleanOpWithSpecialNumericHandling extends TernaryMapOperation { - public DoubleLongBooleanOp(String name) { + public DoubleLongBooleanOpWithSpecialNumericHandling(String name) { super(name); } @@ -39,9 +39,21 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object Context context = Context.getCurrent(); long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + for (int i = 0; i < storage.size(); i++) { if (!storage.isNa(i)) { - out[i] = Double.doubleToRawLongBits(doLongBoolean(storage.getItem(i), longArg, booleanArg, i, problemBuilder)); + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = Double.doubleToRawLongBits(doLongBoolean(storage.getItem(i), longArg, booleanArg, i, problemBuilder)); + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); + } + } else { + isMissing.set(i); } context.safepoint(); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 5e85e3caf78e..4e402e9c7cfb 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -12,7 +12,7 @@ import org.enso.table.data.column.operation.map.numeric.DoubleComparison; import org.enso.table.data.column.operation.map.numeric.DoubleIsInOp; import org.enso.table.data.column.operation.map.numeric.DoubleLongMapOpWithSpecialNumericHandling; -import org.enso.table.data.column.operation.map.numeric.DoubleLongBooleanOp; +import org.enso.table.data.column.operation.map.numeric.DoubleLongBooleanOpWithSpecialNumericHandling; import org.enso.table.data.column.operation.map.numeric.DoubleNumericOp; import org.enso.table.data.column.storage.BoolStorage; import org.enso.table.data.column.storage.Storage; @@ -303,7 +303,7 @@ protected long doOperation(double a) { } }) .add( - new DoubleLongBooleanOp(Maps.ROUND) { + new DoubleLongBooleanOpWithSpecialNumericHandling(Maps.ROUND) { @Override protected double doLongBoolean(double n, long decimalPlaces, boolean useBankers, int ix, MapOperationProblemBuilder problemBuilder) { return Core_Math_Utils.roundDouble(n, decimalPlaces, useBankers); From 8bc823afe3fd283f0bdae7f9de160eb6c6a5fa9f Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 10:47:51 -0400 Subject: [PATCH 04/36] throwing --- .../common_utils/Core_Math_Utils.java | 18 +++++- ...ngBooleanOpWithSpecialNumericHandling.java | 2 +- .../map/numeric/LongLongBooleanOp.java | 57 +++++++++++++++++++ .../storage/numeric/AbstractLongStorage.java | 20 +++++++ .../src/In_Memory/Column_Spec.enso | 9 +++ 5 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java diff --git a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java index bc42008c2f4d..3801b4901903 100644 --- a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java +++ b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java @@ -13,6 +13,12 @@ public class Core_Math_Utils { /** Minimum value for the `n` parameter to `roundDouble`. */ private static final double ROUND_MAX_DOUBLE = 99999999999999.0; + /** Minimum value for the `n` parameter to `roundDouble`. */ + private static final long ROUND_MIN_LONG = -99999999999999L; + + /** Minimum value for the `n` parameter to `roundDouble`. */ + private static final long ROUND_MAX_LONG = 99999999999999L; + /** * Round to a specified number of decimal places. * @@ -84,7 +90,7 @@ public static double roundDouble(double n, long decimalPlaces, boolean useBanker * @return the rounded number. * @throws IllegalArgumentException if `decimalPlaces` is outside the allowed range. */ - public static long roundLong(long n, int decimalPlaces, boolean useBankers) { + public static long roundLong(long n, long decimalPlaces, boolean useBankers) { if (decimalPlaces < ROUND_MIN_DECIMAL_PLACES || decimalPlaces > ROUND_MAX_DECIMAL_PLACES) { String msg = "round: decimalPlaces must be between " @@ -95,6 +101,16 @@ public static long roundLong(long n, int decimalPlaces, boolean useBankers) { + decimalPlaces; throw new IllegalArgumentException(msg); } + if (n < ROUND_MIN_LONG || n > ROUND_MAX_LONG) { + String msg = + "Error: `round` can only accept values between " + + ROUND_MIN_LONG + + " and " + + ROUND_MAX_LONG + + " (inclusive), but was " + + n; + throw new IllegalArgumentException(msg); + } if (decimalPlaces >= 0) { return n; diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java index 28b456d11119..dc2bd924fd11 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java @@ -46,7 +46,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object double item = storage.getItem(i); boolean special = Double.isNaN(item) || Double.isInfinite(item); if (!special) { - out[i] = Double.doubleToRawLongBits(doLongBoolean(storage.getItem(i), longArg, booleanArg, i, problemBuilder)); + out[i] = Double.doubleToRawLongBits(doLongBoolean(item, longArg, booleanArg, i, problemBuilder)); } else { String msg = "Value is " + item; problemBuilder.reportArithmeticError(msg, i); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java new file mode 100644 index 000000000000..a9df9595fad3 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java @@ -0,0 +1,57 @@ +package org.enso.table.data.column.operation.map.numeric; + +import org.enso.table.data.column.operation.map.TernaryMapOperation; +import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; +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.error.UnexpectedTypeException; +import org.graalvm.polyglot.Context; + +import java.util.BitSet; + +/** An operation expecting a numeric argument and returning a number. */ +public abstract class LongLongBooleanOp extends TernaryMapOperation { + + public LongLongBooleanOp(String name) { + super(name); + } + + protected abstract long doLongBoolean(long a, long b, boolean c, int ix, MapOperationProblemBuilder problemBuilder); + + @Override + public Storage runTernaryMap(AbstractLongStorage storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { + if (arg0 == null || arg1 == null) { + return LongStorage.makeEmpty(storage.size()); + } + + long longArg; + if (arg0 instanceof Long) { + longArg = (Long) arg0; + } else { + throw new UnexpectedTypeException("a long."); + } + boolean booleanArg; + if (arg1 instanceof Boolean) { + booleanArg = (Boolean) arg1; + } else { + throw new UnexpectedTypeException("a boolean."); + } + + Context context = Context.getCurrent(); + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + + for (int i = 0; i < storage.size(); i++) { + if (!storage.isNa(i)) { + long item = storage.getItem(i); + out[i] = doLongBoolean(item, longArg, booleanArg, i, problemBuilder); + } else { + isMissing.set(i); + } + + context.safepoint(); + } + return new LongStorage(out, storage.size(), storage.getIsMissing()); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java index 1fb892aee5c5..b18dd31befdd 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java @@ -1,6 +1,7 @@ package org.enso.table.data.column.storage.numeric; import java.util.BitSet; +import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.NumericBuilder; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; @@ -9,6 +10,7 @@ import org.enso.table.data.column.operation.map.numeric.LongBooleanOp; import org.enso.table.data.column.operation.map.numeric.LongComparison; import org.enso.table.data.column.operation.map.numeric.LongIsInOp; +import org.enso.table.data.column.operation.map.numeric.LongLongBooleanOp; import org.enso.table.data.column.operation.map.numeric.LongNumericOp; import org.enso.table.data.column.operation.map.numeric.UnaryLongToLongOp; import org.enso.table.data.column.storage.BoolStorage; @@ -48,6 +50,17 @@ public Storage runVectorizedBinaryMap( return ops.runBinaryMap(name, this, argument, problemBuilder); } + @Override + public boolean isTernaryOpVectorized(String op) { + return ops.isSupportedTernary(op); + } + + @Override + public Storage runVectorizedTernaryMap( + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + return ops.runTernaryMap(name, this, argument0, argument1, problemBuilder); + } + @Override public Storage runVectorizedZip( String name, Storage argument, MapOperationProblemBuilder problemBuilder) { @@ -161,6 +174,13 @@ protected long doOperation(long a) { return a; } }) + .add( + new LongLongBooleanOp(Maps.ROUND) { + @Override + protected long doLongBoolean(long n, long decimalPlaces, boolean useBankers, int ix, MapOperationProblemBuilder problemBuilder) { + return Core_Math_Utils.roundLong(n, decimalPlaces, useBankers); + } + }) .add( new LongNumericOp(Storage.Maps.DIV, true) { @Override diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 16dcb5d14b03..a88eb13498ba 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -15,6 +15,15 @@ from Standard.Test import Test, Test_Suite, Problems main = Test_Suite.run_main spec spec = + Test.group "asdfasdf" <| + Test.specify "should report out-of-range values as warnings" <| + col = Column.from_vector "foo" [12, 23, 99999999999999999] + IO.println <| "QQQ " + (col.at 2 . to_text) + expected = Column.from_vector "round([foo])" [10, 20, Nothing] + actual = col.round -1 + actual . should_equal expected + Warning.get_all actual . map .value . should_equal [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive)"] + Test.group "Columns" <| test_column = Column.from_vector "Test" [1, 3, 5, 2, 4, 6] empty_column = Column.from_vector "Test" [] From c7f32489d22542b7e77ddb07599f8c82f63c6a6a Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 11:35:20 -0400 Subject: [PATCH 05/36] tests passing --- .../Table/0.0.0-dev/src/Data/Column.enso | 2 +- .../0.0.0-dev/src/Internal/Java_Problems.enso | 3 ++ .../map/MapOperationProblemBuilder.java | 5 ++++ ...ngBooleanOpWithSpecialNumericHandling.java | 2 +- .../map/numeric/LongLongBooleanOp.java | 25 ++++++++++++++-- .../table/problems/IllegalArgumentError.java | 29 +++++++++++++++++++ .../src/In_Memory/Column_Spec.enso | 11 +------ 7 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/data/table/problems/IllegalArgumentError.java 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 561d1a1c9cb2..02d9c7f3f94a 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 @@ -799,7 +799,7 @@ type Column round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = Value_Type.expect_numeric self <| - Illegal_Argument.handle_java_exception <| Arithmetic_Error.handle_java_exception <| + Illegal_Argument.handle_java_exception <| new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer if is_integer && decimal_places >= 0 then self.rename new_name else 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 8e77327e26e0..38ea3887ee83 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 @@ -6,6 +6,7 @@ from project.Errors import Additional_Invalid_Rows, Additional_Warnings, Duplica polyglot java import org.enso.table.data.table.problems.ArithmeticError polyglot java import org.enso.table.data.table.problems.FloatingPointGrouping +polyglot java import org.enso.table.data.table.problems.IllegalArgumentError polyglot java import org.enso.table.data.table.problems.InvalidAggregation polyglot java import org.enso.table.data.table.problems.UnquotedCharactersInOutput polyglot java import org.enso.table.data.table.problems.UnquotedDelimiter @@ -23,6 +24,8 @@ translate_problem p = case p of Error.throw err _ : ArithmeticError -> Arithmetic_Error.Error p.getMessage + _ : IllegalArgumentError -> + Illegal_Argument.Error p.getMessage _ : FloatingPointGrouping -> Floating_Point_Equality.Error p.getLocationName _ : UnquotedCharactersInOutput -> diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java index 237c1b6bd63d..abb9848ee149 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java @@ -1,6 +1,7 @@ package org.enso.table.data.column.operation.map; import org.enso.table.data.table.problems.ArithmeticError; +import org.enso.table.data.table.problems.IllegalArgumentError; import org.enso.table.data.table.problems.FloatingPointGrouping; import org.enso.table.problems.AggregatedProblems; @@ -24,6 +25,10 @@ public void reportArithmeticError(String message, Integer row) { problems.add(new ArithmeticError(location, message, row)); } + public void reportIllegalArgumentError(String message, Integer row) { + problems.add(new IllegalArgumentError(location, message, row)); + } + public void reportDivisionByZero(Integer row) { reportArithmeticError("Division by zero", row); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java index dc2bd924fd11..3342a720c585 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java @@ -58,6 +58,6 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object context.safepoint(); } - return new DoubleStorage(out, storage.size(), storage.getIsMissing()); + return new DoubleStorage(out, storage.size(), isMissing); } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java index a9df9595fad3..45e75842827c 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java @@ -13,6 +13,12 @@ /** An operation expecting a numeric argument and returning a number. */ public abstract class LongLongBooleanOp extends TernaryMapOperation { + /** Minimum value for the `n` parameter to `roundDouble`. */ + private static final long ROUND_MIN_LONG = -99999999999999L; + + /** Minimum value for the `n` parameter to `roundDouble`. */ + private static final long ROUND_MAX_LONG = 99999999999999L; + public LongLongBooleanOp(String name) { super(name); } @@ -45,13 +51,28 @@ public Storage runTernaryMap(AbstractLongStorage storage, Object arg0, Obj for (int i = 0; i < storage.size(); i++) { if (!storage.isNa(i)) { long item = storage.getItem(i); - out[i] = doLongBoolean(item, longArg, booleanArg, i, problemBuilder); + boolean outOfRange = item < ROUND_MIN_LONG || item > ROUND_MAX_LONG; + if (!outOfRange) { + out[i] = doLongBoolean(item, longArg, booleanArg, i, problemBuilder); + } else { + String msg = + "Error: `round` can only accept values between " + + ROUND_MIN_LONG + + " and " + + ROUND_MAX_LONG + + " (inclusive), but was " + + item; + problemBuilder.reportIllegalArgumentError(msg, i); + isMissing.set(i); + } + } else { isMissing.set(i); } context.safepoint(); } - return new LongStorage(out, storage.size(), storage.getIsMissing()); + + return new LongStorage(out, storage.size(), isMissing); } } diff --git a/std-bits/table/src/main/java/org/enso/table/data/table/problems/IllegalArgumentError.java b/std-bits/table/src/main/java/org/enso/table/data/table/problems/IllegalArgumentError.java new file mode 100644 index 000000000000..40262f727372 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/data/table/problems/IllegalArgumentError.java @@ -0,0 +1,29 @@ +package org.enso.table.data.table.problems; + +import java.util.stream.Collectors; + +public class IllegalArgumentError extends ColumnAggregatedProblems { + private final String message; + + public IllegalArgumentError(String locationName, String message, Integer row) { + super(locationName, row); + this.message = message; + } + + @Override + public boolean merge(ColumnAggregatedProblems another) { + if (another instanceof IllegalArgumentError IllegalArgumentError + && this.getLocationName().equals(IllegalArgumentError.getLocationName()) + && this.message.equals(IllegalArgumentError.message)) { + this.rows.addAll(another.rows); + return true; + } + + return false; + } + + @Override + public String getMessage() { + return message + " (at rows " + makeTruncatedRowsString() + ")."; + } +} diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index a88eb13498ba..c508c4699546 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -15,15 +15,6 @@ from Standard.Test import Test, Test_Suite, Problems main = Test_Suite.run_main spec spec = - Test.group "asdfasdf" <| - Test.specify "should report out-of-range values as warnings" <| - col = Column.from_vector "foo" [12, 23, 99999999999999999] - IO.println <| "QQQ " + (col.at 2 . to_text) - expected = Column.from_vector "round([foo])" [10, 20, Nothing] - actual = col.round -1 - actual . should_equal expected - Warning.get_all actual . map .value . should_equal [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive)"] - Test.group "Columns" <| test_column = Column.from_vector "Test" [1, 3, 5, 2, 4, 6] empty_column = Column.from_vector "Test" [] @@ -162,7 +153,7 @@ spec = expected = Column.from_vector "round([foo])" [10, 20, Nothing] actual = col.round -1 actual . should_equal expected - Warning.get_all actual . map .value . should_equal [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive)"] + Warning.get_all actual . map .value . should_equal [Illegal_Argument.Error "Error: `round` can only accept values between -99999999999999 and 99999999999999 (inclusive), but was 99999999999999999 (at rows [2])."] Test.specify "should throw an error on decimal places out of range" <| col = Column.from_vector "foo" [12, 23, 99999999999999999] From fed0636fbf2a9b7d15bd04313de9f83b06448807 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 11:58:37 -0400 Subject: [PATCH 06/36] cleanup --- test/Table_Tests/src/In_Memory/Column_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index c508c4699546..26403d757fe3 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -183,7 +183,7 @@ spec = Test.specify "Should error on input of the wrong type" <| Column.from_vector "foo" ["asdf", "zxcv", "qwer"] . ceil . should_fail_with Invalid_Value_Type - Test.group "floorasdfasdf" <| + Test.group "floor" <| Test.specify "should be able to take the floor of a column of floats" <| Column.from_vector "foo" [1.25, 2.33, 3.57] . floor . should_equal <| Column.from_vector "floor([foo])" [1, 2, 3] Column.from_vector "foo" [1.25, 2.33, 3.57] . floor . value_type . should_equal Value_Type.Integer From 09a6563b6d04826ea5326d039feafcea5c098a81 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 12:23:19 -0400 Subject: [PATCH 07/36] fix cos test --- distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 02d9c7f3f94a..a25394322262 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 @@ -799,7 +799,7 @@ type Column round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = Value_Type.expect_numeric self <| - Illegal_Argument.handle_java_exception <| + Illegal_Argument.handle_java_exception <| Rounding_Helpers.check_decimal_places decimal_places <| new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer if is_integer && decimal_places >= 0 then self.rename new_name else From 66050d4886165f1d3323b51c69646e7648427bb0 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 13:38:20 -0400 Subject: [PATCH 08/36] mixed col works --- .../data/column/storage/MixedStorage.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 acf2a3dc57f7..156b16b04dc0 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 @@ -161,6 +161,25 @@ private VectorizedOperationAvailability resolveBinaryOp(String name) { } } + private VectorizedOperationAvailability resolveTernaryOp(String name) { + // Shortcut - if the storage is already specialized - we prefer it. + if (cachedInferredStorage != null && cachedInferredStorage.isTernaryOpVectorized(name)) { + return VectorizedOperationAvailability.AVAILABLE_IN_SPECIALIZED_STORAGE; + } + + // Otherwise, we try to avoid specializing if not yet necessary. + if (super.isTernaryOpVectorized(name)) { + return VectorizedOperationAvailability.AVAILABLE_IN_SUPER; + } else { + // But if our storage does not provide the operation, we have to try checking the other one. + if (getInferredStorage() != null && getInferredStorage().isTernaryOpVectorized(name)) { + return VectorizedOperationAvailability.AVAILABLE_IN_SPECIALIZED_STORAGE; + } else { + return VectorizedOperationAvailability.NOT_AVAILABLE; + } + } + } + @Override public boolean isUnaryOpVectorized(String name) { return resolveUnaryOp(name) != VectorizedOperationAvailability.NOT_AVAILABLE; @@ -192,6 +211,22 @@ public Storage runVectorizedBinaryMap( } } + @Override + public boolean isTernaryOpVectorized(String name) { + return resolveTernaryOp(name) != VectorizedOperationAvailability.NOT_AVAILABLE; + } + + @Override + public Storage runVectorizedTernaryMap( + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + if (resolveTernaryOp(name) == VectorizedOperationAvailability.AVAILABLE_IN_SPECIALIZED_STORAGE) { + return getInferredStorage().runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); + } else { + // Even if the operation is not available, we rely on super to report an exception. + return super.runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); + } + } + @Override public Storage runVectorizedZip( String name, Storage argument, MapOperationProblemBuilder problemBuilder) { From f17b54755d8f04a093dac39cce2bbb942a2692ad Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 13:48:27 -0400 Subject: [PATCH 09/36] bench ints too --- test/Benchmarks/src/Column_Numeric.enso | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index 2bfb1e838847..4c1162e31c90 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -5,7 +5,7 @@ from Standard.Test import Bench, Faker ## Bench Utilities ============================================================ -vector_size = 10000 +vector_size = 1000000 iter_size = 10 num_iterations = 10 @@ -36,10 +36,9 @@ bench = IO.println <| ".floor floats" Bench.measure floats.floor "Column.floor floats" iter_size num_iterations - ## - IO.println <| ".round ints" - Bench.measure ints.old_round "old Column.round ints" iter_size num_iterations - Bench.measure ints.round "new Column.round ints" iter_size num_iterations + IO.println <| ".round ints" + Bench.measure ints.old_round "old Column.round ints" iter_size num_iterations + Bench.measure ints.round "new Column.round ints" iter_size num_iterations ## IO.println <| ".truncate ints" From 10bb7d1bf6393310f08e46bd9fb7c85ed29612b0 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 13:51:35 -0400 Subject: [PATCH 10/36] removed expected_result_type=target_value_type --- distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a25394322262..613ae380bde6 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 @@ -805,7 +805,7 @@ type Column if is_integer && decimal_places >= 0 then self.rename new_name else should_cast_to_integer = decimal_places <= 0 || is_integer target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type - result_float = run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name expected_result_type=target_value_type + result_float = run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name result = cast_if_needed result_float target_value_type result #result.rename new_name From 04ba2660586ab2e398dd6ef8b7490f140024419d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 13:58:15 -0400 Subject: [PATCH 11/36] instanceof syntax --- ...oubleLongBooleanOpWithSpecialNumericHandling.java | 12 +++--------- .../operation/map/numeric/LongLongBooleanOp.java | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java index 3342a720c585..f1fac631e42a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java @@ -24,16 +24,10 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object return DoubleStorage.makeEmpty(storage.size()); } - long longArg; - if (arg0 instanceof Long) { - longArg = (Long) arg0; - } else { + if (!(arg0 instanceof Long arg0AsLong)) { throw new UnexpectedTypeException("a long."); } - boolean booleanArg; - if (arg1 instanceof Boolean) { - booleanArg = (Boolean) arg1; - } else { + if (!(arg1 instanceof Boolean arg1AsBoolean)) { throw new UnexpectedTypeException("a boolean."); } @@ -46,7 +40,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object double item = storage.getItem(i); boolean special = Double.isNaN(item) || Double.isInfinite(item); if (!special) { - out[i] = Double.doubleToRawLongBits(doLongBoolean(item, longArg, booleanArg, i, problemBuilder)); + out[i] = Double.doubleToRawLongBits(doLongBoolean(item, arg0AsLong, arg1AsBoolean, i, problemBuilder)); } else { String msg = "Value is " + item; problemBuilder.reportArithmeticError(msg, i); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java index 45e75842827c..f2d611315e7e 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java @@ -31,16 +31,10 @@ public Storage runTernaryMap(AbstractLongStorage storage, Object arg0, Obj return LongStorage.makeEmpty(storage.size()); } - long longArg; - if (arg0 instanceof Long) { - longArg = (Long) arg0; - } else { + if (!(arg0 instanceof Long arg0AsLong)) { throw new UnexpectedTypeException("a long."); } - boolean booleanArg; - if (arg1 instanceof Boolean) { - booleanArg = (Boolean) arg1; - } else { + if (!(arg1 instanceof Boolean arg1AsBoolean)) { throw new UnexpectedTypeException("a boolean."); } @@ -53,7 +47,7 @@ public Storage runTernaryMap(AbstractLongStorage storage, Object arg0, Obj long item = storage.getItem(i); boolean outOfRange = item < ROUND_MIN_LONG || item > ROUND_MAX_LONG; if (!outOfRange) { - out[i] = doLongBoolean(item, longArg, booleanArg, i, problemBuilder); + out[i] = doLongBoolean(item, arg0AsLong, arg1AsBoolean, i, problemBuilder); } else { String msg = "Error: `round` can only accept values between " From 99d760701960ca5db290e3078c4a0263144d525b Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 14:13:36 -0400 Subject: [PATCH 12/36] ungeneralize --- ...lNumericHandling.java => DoubleRoundOp.java} | 17 ++++++++--------- ...{LongLongBooleanOp.java => LongRoundOp.java} | 17 ++++++++--------- .../storage/numeric/AbstractLongStorage.java | 10 ++-------- .../column/storage/numeric/DoubleStorage.java | 11 ++--------- 4 files changed, 20 insertions(+), 35 deletions(-) rename std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/{DoubleLongBooleanOpWithSpecialNumericHandling.java => DoubleRoundOp.java} (70%) rename std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/{LongLongBooleanOp.java => LongRoundOp.java} (79%) diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java similarity index 70% rename from std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java rename to std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java index f1fac631e42a..0eeb502c93cc 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleLongBooleanOpWithSpecialNumericHandling.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/DoubleRoundOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric; +import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.operation.map.TernaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; import org.enso.table.data.column.storage.numeric.DoubleStorage; @@ -10,24 +11,22 @@ import java.util.BitSet; /** An operation expecting a numeric argument and returning a number. */ -public abstract class DoubleLongBooleanOpWithSpecialNumericHandling extends TernaryMapOperation { +public class DoubleRoundOp extends TernaryMapOperation { - public DoubleLongBooleanOpWithSpecialNumericHandling(String name) { + public DoubleRoundOp(String name) { super(name); } - protected abstract double doLongBoolean(double a, long b, boolean c, int ix, MapOperationProblemBuilder problemBuilder); - @Override - public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { - if (arg0 == null || arg1 == null) { + public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { + if (decimalPlacesObject == null || useBankersObject == null) { return DoubleStorage.makeEmpty(storage.size()); } - if (!(arg0 instanceof Long arg0AsLong)) { + if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } - if (!(arg1 instanceof Boolean arg1AsBoolean)) { + if (!(useBankersObject instanceof Boolean useBankers)) { throw new UnexpectedTypeException("a boolean."); } @@ -40,7 +39,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object arg0, Object double item = storage.getItem(i); boolean special = Double.isNaN(item) || Double.isInfinite(item); if (!special) { - out[i] = Double.doubleToRawLongBits(doLongBoolean(item, arg0AsLong, arg1AsBoolean, i, problemBuilder)); + out[i] = Double.doubleToRawLongBits(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers)); } else { String msg = "Value is " + item; problemBuilder.reportArithmeticError(msg, i); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java similarity index 79% rename from std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java rename to std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java index f2d611315e7e..71ab45af242f 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongLongBooleanOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java @@ -1,5 +1,6 @@ package org.enso.table.data.column.operation.map.numeric; +import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.operation.map.TernaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; import org.enso.table.data.column.storage.numeric.AbstractLongStorage; @@ -11,7 +12,7 @@ import java.util.BitSet; /** An operation expecting a numeric argument and returning a number. */ -public abstract class LongLongBooleanOp extends TernaryMapOperation { +public class LongRoundOp extends TernaryMapOperation { /** Minimum value for the `n` parameter to `roundDouble`. */ private static final long ROUND_MIN_LONG = -99999999999999L; @@ -19,22 +20,20 @@ public abstract class LongLongBooleanOp extends TernaryMapOperation runTernaryMap(AbstractLongStorage storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { - if (arg0 == null || arg1 == null) { + public Storage runTernaryMap(AbstractLongStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { + if (decimalPlacesObject == null || useBankersObject == null) { return LongStorage.makeEmpty(storage.size()); } - if (!(arg0 instanceof Long arg0AsLong)) { + if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } - if (!(arg1 instanceof Boolean arg1AsBoolean)) { + if (!(useBankersObject instanceof Boolean useBankers)) { throw new UnexpectedTypeException("a boolean."); } @@ -47,7 +46,7 @@ public Storage runTernaryMap(AbstractLongStorage storage, Object arg0, Obj long item = storage.getItem(i); boolean outOfRange = item < ROUND_MIN_LONG || item > ROUND_MAX_LONG; if (!outOfRange) { - out[i] = doLongBoolean(item, arg0AsLong, arg1AsBoolean, i, problemBuilder); + out[i] = Core_Math_Utils.roundLong(item, decimalPlaces, useBankers); } else { String msg = "Error: `round` can only accept values between " diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java index b18dd31befdd..5e884663e252 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java @@ -10,8 +10,8 @@ import org.enso.table.data.column.operation.map.numeric.LongBooleanOp; import org.enso.table.data.column.operation.map.numeric.LongComparison; import org.enso.table.data.column.operation.map.numeric.LongIsInOp; -import org.enso.table.data.column.operation.map.numeric.LongLongBooleanOp; import org.enso.table.data.column.operation.map.numeric.LongNumericOp; +import org.enso.table.data.column.operation.map.numeric.LongRoundOp; import org.enso.table.data.column.operation.map.numeric.UnaryLongToLongOp; import org.enso.table.data.column.storage.BoolStorage; import org.enso.table.data.column.storage.Storage; @@ -174,13 +174,7 @@ protected long doOperation(long a) { return a; } }) - .add( - new LongLongBooleanOp(Maps.ROUND) { - @Override - protected long doLongBoolean(long n, long decimalPlaces, boolean useBankers, int ix, MapOperationProblemBuilder problemBuilder) { - return Core_Math_Utils.roundLong(n, decimalPlaces, useBankers); - } - }) + .add(new LongRoundOp(Maps.ROUND)) .add( new LongNumericOp(Storage.Maps.DIV, true) { @Override diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 4e402e9c7cfb..45beec6a13b6 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -2,7 +2,6 @@ import java.util.BitSet; import java.util.List; -import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.NumericBuilder; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; @@ -12,8 +11,8 @@ import org.enso.table.data.column.operation.map.numeric.DoubleComparison; import org.enso.table.data.column.operation.map.numeric.DoubleIsInOp; import org.enso.table.data.column.operation.map.numeric.DoubleLongMapOpWithSpecialNumericHandling; -import org.enso.table.data.column.operation.map.numeric.DoubleLongBooleanOpWithSpecialNumericHandling; import org.enso.table.data.column.operation.map.numeric.DoubleNumericOp; +import org.enso.table.data.column.operation.map.numeric.DoubleRoundOp; import org.enso.table.data.column.storage.BoolStorage; import org.enso.table.data.column.storage.Storage; import org.enso.table.data.column.storage.type.FloatType; @@ -302,13 +301,7 @@ protected long doOperation(double a) { return (long) Math.floor(a); } }) - .add( - new DoubleLongBooleanOpWithSpecialNumericHandling(Maps.ROUND) { - @Override - protected double doLongBoolean(double n, long decimalPlaces, boolean useBankers, int ix, MapOperationProblemBuilder problemBuilder) { - return Core_Math_Utils.roundDouble(n, decimalPlaces, useBankers); - } - }) + .add(new DoubleRoundOp(Maps.ROUND)) .add( new DoubleComparison(Maps.LT) { @Override From f502c99e5ae06b00b5fdd6062c60d8daa073d0f4 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 14:22:59 -0400 Subject: [PATCH 13/36] specialize to returning long, no conversion --- .../Table/0.0.0-dev/src/Data/Column.enso | 6 +- .../operation/map/numeric/DoubleRoundOp.java | 58 ++++++++++++++----- 2 files changed, 43 insertions(+), 21 deletions(-) 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 613ae380bde6..78867934514b 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 @@ -803,11 +803,7 @@ type Column new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer if is_integer && decimal_places >= 0 then self.rename new_name else - should_cast_to_integer = decimal_places <= 0 || is_integer - target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type - result_float = run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name - result = cast_if_needed result_float target_value_type - result #result.rename new_name + run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name ## ALIAS int 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 0eeb502c93cc..043809371ff7 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 @@ -4,6 +4,7 @@ import org.enso.table.data.column.operation.map.TernaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; 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; @@ -18,7 +19,7 @@ public DoubleRoundOp(String name) { } @Override - public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { + public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { if (decimalPlacesObject == null || useBankersObject == null) { return DoubleStorage.makeEmpty(storage.size()); } @@ -31,26 +32,51 @@ public Storage runTernaryMap(DoubleStorage storage, Object decimalPlaces } Context context = Context.getCurrent(); - long[] out = new long[storage.size()]; - BitSet isMissing = new BitSet(); - - for (int i = 0; i < storage.size(); i++) { - if (!storage.isNa(i)) { - double item = storage.getItem(i); - boolean special = Double.isNaN(item) || Double.isInfinite(item); - if (!special) { - out[i] = Double.doubleToRawLongBits(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers)); + + if (decimalPlaces <= 0) { + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + + for (int i = 0; i < storage.size(); i++) { + if (!storage.isNa(i)) { + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = (long) Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers); + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); + } } else { - String msg = "Value is " + item; - problemBuilder.reportArithmeticError(msg, i); isMissing.set(i); } - } else { - isMissing.set(i); + + context.safepoint(); } + return new LongStorage(out, storage.size(), isMissing); + } else { + long[] out = new long[storage.size()]; + BitSet isMissing = new BitSet(); + + for (int i = 0; i < storage.size(); i++) { + if (!storage.isNa(i)) { + double item = storage.getItem(i); + boolean special = Double.isNaN(item) || Double.isInfinite(item); + if (!special) { + out[i] = Double.doubleToRawLongBits(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers)); + } else { + String msg = "Value is " + item; + problemBuilder.reportArithmeticError(msg, i); + isMissing.set(i); + } + } else { + isMissing.set(i); + } - context.safepoint(); + context.safepoint(); + } + return new DoubleStorage(out, storage.size(), isMissing); } - return new DoubleStorage(out, storage.size(), isMissing); } } From 460fd7b8a261efeffa295dec0ed40451ee1802eb Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 14:35:10 -0400 Subject: [PATCH 14/36] docs --- .../Table/0.0.0-dev/src/Data/Column.enso | 15 +++++++++++++++ .../polyglot/common_utils/Core_Math_Utils.java | 1 + .../operation/map/MapOperationStorage.java | 5 +++-- .../operation/map/TernaryMapOperation.java | 3 ++- .../operation/map/numeric/DoubleRoundOp.java | 4 +++- .../operation/map/numeric/LongRoundOp.java | 2 +- .../data/column/storage/MixedStorage.java | 1 + .../table/data/column/storage/Storage.java | 18 ++++++++++++++++-- 8 files changed, 42 insertions(+), 7 deletions(-) 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 78867934514b..69d053e9d55a 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 @@ -2121,6 +2121,21 @@ run_vectorized_binary_op column name operand new_name=Nothing fallback_fn=Nothin Problem_Behavior.Report_Warning.attach_problems_after result <| Java_Problems.parse_aggregated_problems problem_builder.getProblems +## PRIVATE + + Executes a vectorized ternary operation over the provided column. + + Arguments: + - column: The column to execute the operation over. + - name: The name of the vectorized operation. + - operand0: The first operand to apply to the function after `column`. + - operand1: The second operand to apply to the function after `column`. + - new_name: The name of the column created as the result of this operation. + - expected_result_type: The expected result type of the operation. + - skip_nulls: Specifies if nulls should be skipped. If set to `True`, a null + value results in null without passing it to the function. If set to + `False`, the null values are passed as any other value and can have custom + handling logic. run_vectorized_ternary_op : Column -> Text -> Any -> Any -> Text|Nothing -> Value_Type -> Boolean -> Column run_vectorized_ternary_op column name operand0 operand1 new_name=Nothing expected_result_type=Nothing skip_nulls=True = effective_new_name = new_name.if_nothing <| diff --git a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java index 3801b4901903..75d0214fbbe1 100644 --- a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java +++ b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java @@ -88,6 +88,7 @@ public static double roundDouble(double n, long decimalPlaces, boolean useBanker * in rounding to positive integer powers of 10. Must be between -15 and 15 (inclusive). * @param useBankers: Rounds mid-point to nearest even number. * @return the rounded number. + * @throws IllegalArgumentException if `n` is outside the allowed range. * @throws IllegalArgumentException if `decimalPlaces` is outside the allowed range. */ public static long roundLong(long n, long decimalPlaces, boolean useBankers) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java index 143a09e0a2bf..982d7b78167a 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java @@ -70,7 +70,7 @@ public Storage runBinaryMap( } /** - * Checks if a binary operation is supported by this set. + * Checks if a ternary operation is supported by this set. * * @param n the operation name * @return whether the operation is supported @@ -84,7 +84,8 @@ public boolean isSupportedTernary(String n) { * * @param n the operation name * @param storage the storage to run operation on - * @param arg the argument to pass to the operation + * @param arg0 the first argument to pass to the operation + * @param arg1 the second argument to pass to the operation * @param problemBuilder the builder allowing to report computation problems * @return the result of running the operation */ diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java index 72ef02331185..b44b0e69a6f9 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java @@ -23,7 +23,8 @@ public TernaryMapOperation(String name) { * Run the operation in map mode - combining every row of the storage with a scalar argument. * * @param storage the storage to run operation on - * @param arg the argument passed to the operation + * @param arg0 the first argument passed to the operation + * @param arg1 the first argument passed to the operation * @param problemBuilder the builder allowing to report computation problems * @return the result of running the operation */ 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 043809371ff7..9ac7e15ed747 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 @@ -11,7 +11,7 @@ import java.util.BitSet; -/** An operation expecting a numeric argument and returning a number. */ +/** An operation rounding floating-point numbers. */ public class DoubleRoundOp extends TernaryMapOperation { public DoubleRoundOp(String name) { @@ -34,6 +34,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec Context context = Context.getCurrent(); if (decimalPlaces <= 0) { + // Return Long storage long[] out = new long[storage.size()]; BitSet isMissing = new BitSet(); @@ -56,6 +57,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec } return new LongStorage(out, storage.size(), isMissing); } else { + // Return double storage. long[] out = new long[storage.size()]; BitSet isMissing = new BitSet(); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java index 71ab45af242f..088f6b6417c6 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java @@ -11,7 +11,7 @@ import java.util.BitSet; -/** An operation expecting a numeric argument and returning a number. */ +/** An operation rounding integers. */ public class LongRoundOp extends TernaryMapOperation { /** Minimum value for the `n` parameter to `roundDouble`. */ 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 156b16b04dc0..f954f0c58cd5 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 @@ -161,6 +161,7 @@ private VectorizedOperationAvailability resolveBinaryOp(String name) { } } + /** {@see resolveUnaryOp} for explanations. */ private VectorizedOperationAvailability resolveTernaryOp(String name) { // Shortcut - if the storage is already specialized - we prefer it. if (cachedInferredStorage != null && cachedInferredStorage.isTernaryOpVectorized(name)) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java index c49f9100d69c..c01a712959a6 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -120,12 +120,12 @@ public abstract Storage runVectorizedUnaryMap( public abstract Storage runVectorizedBinaryMap( String name, Object argument, MapOperationProblemBuilder problemBuilder); - /* Specifies if the given binary operation has a vectorized implementation available for this storage.*/ + /* Specifies if the given ternary operation has a vectorized implementation available for this storage.*/ public boolean isTernaryOpVectorized(String name) { return false; } - /** Runs a vectorized operation on this storage, taking one scalar argument. */ + /** Runs a vectorized operation on this storage, taking two scalar arguments. */ public Storage runVectorizedTernaryMap( String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { throw new IllegalArgumentException("Unsupported ternary operation: " + name); @@ -294,6 +294,20 @@ public final Storage vectorizedOrFallbackBinaryMap( } } + /** + * Runs a ternary operation with two scalar arguments. + * + *

Does not take a fallback function. + * + * @param name the name of the vectorized operation + * @param problemBuilder the problem builder to use for the vectorized implementation + * @param argument0 the first argument to pass to each run of the function + * @param argument1 the second argument to pass to each run of the function + * @param skipNulls specifies whether null values on the input should result in a null result + * @param expectedResultType the expected type for the result storage; it is ignored if the + * operation is vectorized + * @return the result of running the operation on each row + */ public final Storage vectorizedTernaryMap( String name, MapOperationProblemBuilder problemBuilder, From bb65f40fd3e9b865bdea9c5ae2136c94cbba373c Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 14:58:34 -0400 Subject: [PATCH 15/36] all bench --- test/Benchmarks/src/Column_Numeric.enso | 28 +++++++++++-------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index 4c1162e31c90..e1d89febf1a6 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -27,25 +27,21 @@ bench = IO.println <| ".round floats" Bench.measure floats.old_round "old Column.round floats" iter_size num_iterations Bench.measure floats.round "new Column.round floats" iter_size num_iterations - - ## - IO.println <| ".truncate floats" - Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations - IO.println <| ".ceil floats" - Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations - IO.println <| ".floor floats" - Bench.measure floats.floor "Column.floor floats" iter_size num_iterations + IO.println <| ".truncate floats" + Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations + IO.println <| ".ceil floats" + Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations + IO.println <| ".floor floats" + Bench.measure floats.floor "Column.floor floats" iter_size num_iterations IO.println <| ".round ints" Bench.measure ints.old_round "old Column.round ints" iter_size num_iterations Bench.measure ints.round "new Column.round ints" iter_size num_iterations - - ## - IO.println <| ".truncate ints" - Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations - IO.println <| ".ceil ints" - Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations - IO.println <| ".floor ints" - Bench.measure ints.floor "Column.floor ints" iter_size num_iterations + IO.println <| ".truncate ints" + Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations + IO.println <| ".ceil ints" + Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations + IO.println <| ".floor ints" + Bench.measure ints.floor "Column.floor ints" iter_size num_iterations main = bench From fd8578b71bd1523b6b60c5dba3261cbeb33fa7ff Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 15:27:32 -0400 Subject: [PATCH 16/36] bench different params --- test/Benchmarks/src/Column_Numeric.enso | 46 +++++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index e1d89febf1a6..dd5c493ca021 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -24,24 +24,32 @@ bench = ints_vec = Vector.new vector_size _->(faker.integer -1000000000 1000000000) ints = Column.from_vector "ints" ints_vec - IO.println <| ".round floats" - Bench.measure floats.old_round "old Column.round floats" iter_size num_iterations - Bench.measure floats.round "new Column.round floats" iter_size num_iterations - IO.println <| ".truncate floats" - Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations - IO.println <| ".ceil floats" - Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations - IO.println <| ".floor floats" - Bench.measure floats.floor "Column.floor floats" iter_size num_iterations - - IO.println <| ".round ints" - Bench.measure ints.old_round "old Column.round ints" iter_size num_iterations - Bench.measure ints.round "new Column.round ints" iter_size num_iterations - IO.println <| ".truncate ints" - Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations - IO.println <| ".ceil ints" - Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations - IO.println <| ".floor ints" - Bench.measure ints.floor "Column.floor ints" iter_size num_iterations + ## + IO.println <| ".truncate floats" + Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations + IO.println <| ".ceil floats" + Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations + IO.println <| ".floor floats" + Bench.measure floats.floor "Column.floor floats" iter_size num_iterations + + IO.println <| ".truncate ints" + Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations + IO.println <| ".ceil ints" + Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations + IO.println <| ".floor ints" + Bench.measure ints.floor "Column.floor ints" iter_size num_iterations + + [True, False].map use_bankers-> + [0, -2, 2].map decimal_places-> + name = "round decimal_places=" + decimal_places.to_text + " use_bankers=" + use_bankers.to_text + old_fun = _.old_round #decimal_places use_bankers + fun = _.round #decimal_places use_bankers + + IO.println <| ".round floats" + Bench.measure (old_fun floats) "float old "+name iter_size num_iterations + Bench.measure (fun floats) "float new "+name iter_size num_iterations + IO.println <| ".round ints" + Bench.measure (old_fun ints) "int old "+name iter_size num_iterations + Bench.measure (fun ints) "int new "+name iter_size num_iterations main = bench From ca1f0cfe382b08f8a2ed1364baaa408bb64728d3 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 15:33:58 -0400 Subject: [PATCH 17/36] removed old impl --- .../Table/0.0.0-dev/src/Data/Column.enso | 36 ------------------- test/Benchmarks/src/Column_Numeric.enso | 34 ++++++++---------- 2 files changed, 15 insertions(+), 55 deletions(-) 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 69d053e9d55a..8d8cf85025c7 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 @@ -761,42 +761,6 @@ type Column Round a column of `Decimal` values`. Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) - old_round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type - old_round self decimal_places=0 use_bankers=False = - Rounding_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| - # If it's an integer column and decimal_places >=0 then it's a no-op. - new_name = naming_helper.function_name "round" [self] - is_integer = self.inferred_precise_value_type.is_integer - if is_integer && decimal_places >= 0 then self.rename new_name else - should_cast_to_integer = decimal_places <= 0 || is_integer - target_value_type = if should_cast_to_integer then Value_Type.Integer else self.value_type - result = self.check_round_input col-> - scale = 10 ^ decimal_places - scaled = col * scale - round_base = scaled.floor - round_midpoint = (round_base + 0.5) / scale - even_is_up = (col >= 0).iif ((scaled.truncate % 2) != 0) ((scaled.truncate % 2) == 0) - half_goes_up = if use_bankers then even_is_up else col >= 0 - do_round_up = half_goes_up.iif (col >= round_midpoint) (col > round_midpoint) - result_float = do_round_up.iif ((round_base + 1.0) / scale) (round_base / scale) - cast_if_needed result_float target_value_type - result.rename new_name - - ## PRIVATE - Restrict allowed range of input to rounding methods. - check_round_input : (Column -> Column) -> Any ! Illegal_Argument - check_round_input self action = - min = Rounding_Helpers.round_min_long - max = Rounding_Helpers.round_max_long - within_range = if self.value_type.is_floating_point.not then self.between min max else - self.is_nan || self.is_infinite || self.between min max - - any_issues = within_range.to_vector . any (== False) - if any_issues.not then action self else - warning = Illegal_Argument.Error <| "Error: `round` can only accept values between " + min.to_text + " and " + max.to_text + " (inclusive)" - result = action (within_range.iif self Nothing) - Warning.attach warning result - round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type round self decimal_places=0 use_bankers=False = Value_Type.expect_numeric self <| Illegal_Argument.handle_java_exception <| Rounding_Helpers.check_decimal_places decimal_places <| diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index dd5c493ca021..a01937c8a86f 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -24,32 +24,28 @@ bench = ints_vec = Vector.new vector_size _->(faker.integer -1000000000 1000000000) ints = Column.from_vector "ints" ints_vec - ## - IO.println <| ".truncate floats" - Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations - IO.println <| ".ceil floats" - Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations - IO.println <| ".floor floats" - Bench.measure floats.floor "Column.floor floats" iter_size num_iterations - - IO.println <| ".truncate ints" - Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations - IO.println <| ".ceil ints" - Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations - IO.println <| ".floor ints" - Bench.measure ints.floor "Column.floor ints" iter_size num_iterations + IO.println <| ".truncate floats" + Bench.measure floats.truncate "Column.truncate floats" iter_size num_iterations + IO.println <| ".ceil floats" + Bench.measure floats.ceil "Column.ceil floats" iter_size num_iterations + IO.println <| ".floor floats" + Bench.measure floats.floor "Column.floor floats" iter_size num_iterations + + IO.println <| ".truncate ints" + Bench.measure ints.truncate "Column.truncate ints" iter_size num_iterations + IO.println <| ".ceil ints" + Bench.measure ints.ceil "Column.ceil ints" iter_size num_iterations + IO.println <| ".floor ints" + Bench.measure ints.floor "Column.floor ints" iter_size num_iterations [True, False].map use_bankers-> [0, -2, 2].map decimal_places-> name = "round decimal_places=" + decimal_places.to_text + " use_bankers=" + use_bankers.to_text - old_fun = _.old_round #decimal_places use_bankers fun = _.round #decimal_places use_bankers IO.println <| ".round floats" - Bench.measure (old_fun floats) "float old "+name iter_size num_iterations - Bench.measure (fun floats) "float new "+name iter_size num_iterations + Bench.measure (fun floats) "float "+name iter_size num_iterations IO.println <| ".round ints" - Bench.measure (old_fun ints) "int old "+name iter_size num_iterations - Bench.measure (fun ints) "int new "+name iter_size num_iterations + Bench.measure (fun ints) "int "+name iter_size num_iterations main = bench From 0d5b378edc5e9b2951980b7a652a4877510ef0d8 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 15:34:22 -0400 Subject: [PATCH 18/36] java fmt --- .../common_utils/Core_Math_Utils.java | 12 ++--- .../map/MapOperationProblemBuilder.java | 2 +- .../operation/map/MapOperationStorage.java | 4 +- .../operation/map/TernaryMapOperation.java | 48 +++++++++---------- .../data/column/storage/MixedStorage.java | 8 ++-- .../table/data/column/storage/Storage.java | 16 +++---- .../storage/numeric/AbstractLongStorage.java | 3 +- .../column/storage/numeric/DoubleStorage.java | 2 +- 8 files changed, 48 insertions(+), 47 deletions(-) diff --git a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java index 75d0214fbbe1..787ca8d2ae51 100644 --- a/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java +++ b/lib/scala/common-polyglot-core-utils/src/main/java/org/enso/polyglot/common_utils/Core_Math_Utils.java @@ -104,12 +104,12 @@ public static long roundLong(long n, long decimalPlaces, boolean useBankers) { } if (n < ROUND_MIN_LONG || n > ROUND_MAX_LONG) { String msg = - "Error: `round` can only accept values between " - + ROUND_MIN_LONG - + " and " - + ROUND_MAX_LONG - + " (inclusive), but was " - + n; + "Error: `round` can only accept values between " + + ROUND_MIN_LONG + + " and " + + ROUND_MAX_LONG + + " (inclusive), but was " + + n; throw new IllegalArgumentException(msg); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java index abb9848ee149..6fabc4ae09ce 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationProblemBuilder.java @@ -1,8 +1,8 @@ package org.enso.table.data.column.operation.map; import org.enso.table.data.table.problems.ArithmeticError; -import org.enso.table.data.table.problems.IllegalArgumentError; import org.enso.table.data.table.problems.FloatingPointGrouping; +import org.enso.table.data.table.problems.IllegalArgumentError; import org.enso.table.problems.AggregatedProblems; public class MapOperationProblemBuilder { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java index 982d7b78167a..d1104cf043ae 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/MapOperationStorage.java @@ -90,10 +90,10 @@ public boolean isSupportedTernary(String n) { * @return the result of running the operation */ public Storage runTernaryMap( - String n, S storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { + String n, S storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder) { if (!isSupportedTernary(n)) { throw new IllegalStateException( - "Requested vectorized ternary operation " + n + ", but no such operation is known."); + "Requested vectorized ternary operation " + n + ", but no such operation is known."); } return ternaryOps.get(n).runTernaryMap(storage, arg0, arg1, problemBuilder); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java index b44b0e69a6f9..cc6a08c7f06d 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/TernaryMapOperation.java @@ -8,31 +8,31 @@ * @param the supported storage type. */ public abstract class TernaryMapOperation> { - private final String name; + private final String name; - /** - * Creates a new operation with the given name. - * - * @param name the operation name - */ - public TernaryMapOperation(String name) { - this.name = name; - } + /** + * Creates a new operation with the given name. + * + * @param name the operation name + */ + public TernaryMapOperation(String name) { + this.name = name; + } - /** - * Run the operation in map mode - combining every row of the storage with a scalar argument. - * - * @param storage the storage to run operation on - * @param arg0 the first argument passed to the operation - * @param arg1 the first argument passed to the operation - * @param problemBuilder the builder allowing to report computation problems - * @return the result of running the operation - */ - public abstract Storage runTernaryMap( - I storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder); + /** + * Run the operation in map mode - combining every row of the storage with a scalar argument. + * + * @param storage the storage to run operation on + * @param arg0 the first argument passed to the operation + * @param arg1 the first argument passed to the operation + * @param problemBuilder the builder allowing to report computation problems + * @return the result of running the operation + */ + public abstract Storage runTernaryMap( + I storage, Object arg0, Object arg1, MapOperationProblemBuilder problemBuilder); - /** @return the name of this operation */ - public String getName() { - return name; - } + /** @return the name of this operation */ + public String getName() { + return name; + } } 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 f954f0c58cd5..215f307f6104 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 @@ -219,9 +219,11 @@ public boolean isTernaryOpVectorized(String name) { @Override public Storage runVectorizedTernaryMap( - String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { - if (resolveTernaryOp(name) == VectorizedOperationAvailability.AVAILABLE_IN_SPECIALIZED_STORAGE) { - return getInferredStorage().runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + if (resolveTernaryOp(name) + == VectorizedOperationAvailability.AVAILABLE_IN_SPECIALIZED_STORAGE) { + return getInferredStorage() + .runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); } else { // Even if the operation is not available, we rely on super to report an exception. return super.runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java index c01a712959a6..04404429f5cf 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/Storage.java @@ -118,7 +118,7 @@ public abstract Storage runVectorizedUnaryMap( /** Runs a vectorized operation on this storage, taking one scalar argument. */ public abstract Storage runVectorizedBinaryMap( - String name, Object argument, MapOperationProblemBuilder problemBuilder); + String name, Object argument, MapOperationProblemBuilder problemBuilder); /* Specifies if the given ternary operation has a vectorized implementation available for this storage.*/ public boolean isTernaryOpVectorized(String name) { @@ -127,7 +127,7 @@ public boolean isTernaryOpVectorized(String name) { /** Runs a vectorized operation on this storage, taking two scalar arguments. */ public Storage runVectorizedTernaryMap( - String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { throw new IllegalArgumentException("Unsupported ternary operation: " + name); } @@ -309,12 +309,12 @@ public final Storage vectorizedOrFallbackBinaryMap( * @return the result of running the operation on each row */ public final Storage vectorizedTernaryMap( - String name, - MapOperationProblemBuilder problemBuilder, - Object argument0, - Object argument1, - boolean skipNulls, - StorageType expectedResultType) { + String name, + MapOperationProblemBuilder problemBuilder, + Object argument0, + Object argument1, + boolean skipNulls, + StorageType expectedResultType) { if (isTernaryOpVectorized(name)) { return runVectorizedTernaryMap(name, argument0, argument1, problemBuilder); } else { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java index 5e884663e252..1604da1dfb18 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/AbstractLongStorage.java @@ -1,7 +1,6 @@ package org.enso.table.data.column.storage.numeric; import java.util.BitSet; -import org.enso.polyglot.common_utils.Core_Math_Utils; import org.enso.table.data.column.builder.Builder; import org.enso.table.data.column.builder.NumericBuilder; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; @@ -57,7 +56,7 @@ public boolean isTernaryOpVectorized(String op) { @Override public Storage runVectorizedTernaryMap( - String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { return ops.runTernaryMap(name, this, argument0, argument1, problemBuilder); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java index 45beec6a13b6..34e71b02b0cc 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/storage/numeric/DoubleStorage.java @@ -118,7 +118,7 @@ public boolean isTernaryOpVectorized(String op) { @Override public Storage runVectorizedTernaryMap( - String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { + String name, Object argument0, Object argument1, MapOperationProblemBuilder problemBuilder) { return ops.runTernaryMap(name, this, argument0, argument1, problemBuilder); } From 036c4851dd4ead028e6d0fbcdd1909231cdc4106 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 8 Aug 2023 15:41:29 -0400 Subject: [PATCH 19/36] bench params --- test/Benchmarks/src/Column_Numeric.enso | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index a01937c8a86f..97185248af77 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -5,8 +5,8 @@ from Standard.Test import Bench, Faker ## Bench Utilities ============================================================ -vector_size = 1000000 -iter_size = 10 +vector_size = 100000 +iter_size = 100 num_iterations = 10 # The Benchmarks ============================================================== From d3a2bbcfd1e265d09f2fa3d37520cc01dd2433dc Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Wed, 9 Aug 2023 12:23:25 -0400 Subject: [PATCH 20/36] review --- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 2 +- .../data/column/operation/map/numeric/DoubleRoundOp.java | 8 ++++++-- .../data/column/operation/map/numeric/LongRoundOp.java | 8 ++++++-- test/Table_Tests/src/In_Memory/Column_Spec.enso | 4 ++++ 4 files changed, 17 insertions(+), 5 deletions(-) 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 d2aeeb06d3bd..ede8988aa302 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 @@ -767,7 +767,7 @@ type Column Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False = Value_Type.expect_numeric self <| + round self round self (decimal_places:Integer = 0) (use_bankers:Boolean = False) = Value_Type.expect_numeric self <| Illegal_Argument.handle_java_exception <| Rounding_Helpers.check_decimal_places decimal_places <| new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer 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 9ac7e15ed747..1c3abfe2878c 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 @@ -20,8 +20,12 @@ public DoubleRoundOp(String name) { @Override public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { - if (decimalPlacesObject == null || useBankersObject == null) { - return DoubleStorage.makeEmpty(storage.size()); + if (decimalPlacesObject == null) { + throw new IllegalArgumentException("decimalPlacesObject must not be null"); + } + + if (useBankersObject == null) { + throw new IllegalArgumentException("useBankersObject must not be null"); } if (!(decimalPlacesObject instanceof Long decimalPlaces)) { diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java index 088f6b6417c6..635168f29086 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java @@ -26,8 +26,12 @@ public LongRoundOp(String name) { @Override public Storage runTernaryMap(AbstractLongStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { - if (decimalPlacesObject == null || useBankersObject == null) { - return LongStorage.makeEmpty(storage.size()); + if (decimalPlacesObject == null) { + throw new IllegalArgumentException("decimalPlacesObject must not be null"); + } + + if (useBankersObject == null) { + throw new IllegalArgumentException("useBankersObject must not be null"); } if (!(decimalPlacesObject instanceof Long decimalPlaces)) { diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 26403d757fe3..f143109ef2a2 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -159,6 +159,10 @@ spec = col = Column.from_vector "foo" [12, 23, 99999999999999999] col.round decimal_places=-1200 . should_fail_with Illegal_Argument + Test.specify "should handle type errors" <| + Test.expect_panic_with (column.round use_bankers="string") Type_Error + Test.expect_panic_with (column.round decimal_places="string") Type_Error + Test.group "truncate" <| Test.specify "should be able to truncate a column of floats" <| Column.from_vector "foo" [1.25, 2.33, 3.57] . truncate . should_equal <| Column.from_vector "truncate([foo])" [1, 2, 3] From b56359ea4763093335e971413b284603d798bed0 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Thu, 10 Aug 2023 10:20:06 -0400 Subject: [PATCH 21/36] fix typo --- distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ede8988aa302..b131ac958a4a 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 @@ -767,7 +767,7 @@ type Column Column.from_vector "foo" [1.2, 2.3, 3.6] . round == (Column.from_vector "foo" [1, 2, 4]) round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type - round self round self (decimal_places:Integer = 0) (use_bankers:Boolean = False) = Value_Type.expect_numeric self <| + round self (decimal_places:Integer = 0) (use_bankers:Boolean = False) = Value_Type.expect_numeric self <| Illegal_Argument.handle_java_exception <| Rounding_Helpers.check_decimal_places decimal_places <| new_name = naming_helper.function_name "round" [self] is_integer = self.inferred_precise_value_type.is_integer From c27e6cb7347016c3f2c0781edaea950c252e7d90 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 11:49:05 -0400 Subject: [PATCH 22/36] return type test --- test/Table_Tests/src/In_Memory/Column_Spec.enso | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index f143109ef2a2..8850ddb67df1 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -4,6 +4,7 @@ import project.Util import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Index_Out_Of_Bounds +import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Examples import Standard.Test.Extensions @@ -15,6 +16,8 @@ from Standard.Test import Test, Test_Suite, Problems main = Test_Suite.run_main spec spec = + Test.group "asdfasdf" <| + Test.group "Columns" <| test_column = Column.from_vector "Test" [1, 3, 5, 2, 4, 6] empty_column = Column.from_vector "Test" [] @@ -159,9 +162,16 @@ spec = col = Column.from_vector "foo" [12, 23, 99999999999999999] col.round decimal_places=-1200 . should_fail_with Illegal_Argument + Test.specify "should return the correct column type" <| + col = Column.from_vector "foo" [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59] + col.round 1 . value_type . should_equal Value_Type.Float + col.round . value_type . should_equal Value_Type.Integer + col.round -1 . value_type . should_equal Value_Type.Integer + Test.specify "should handle type errors" <| - Test.expect_panic_with (column.round use_bankers="string") Type_Error - Test.expect_panic_with (column.round decimal_places="string") Type_Error + col = Column.from_vector "foo" [12, 23, 45] + Test.expect_panic_with (col.round use_bankers="string") Type_Error + Test.expect_panic_with (col.round decimal_places="string") Type_Error Test.group "truncate" <| Test.specify "should be able to truncate a column of floats" <| From 54052c7af03369580d93ad3ef2bc802e4a9d7b73 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 13:15:45 -0400 Subject: [PATCH 23/36] more rounding tests --- .../Column_Operations_Spec.enso | 15 ++++++++++++++- test/Tests/src/Data/Numbers_Spec.enso | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso index 05e518ab3bbc..ccf59e40a91f 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso @@ -771,7 +771,7 @@ spec setup = do_round 0.00001 . should_equal 0 do_round -0.00001 . should_equal 0 - Test.specify "Can round decimals to a specified number of decimal places" <| + Test.specify "Can round positive decimals to a specified number of decimal places" <| do_round 3.0001 2 . should_equal 3.0 do_round 3.1414 2 . should_equal 3.14 do_round 3.1415 2 . should_equal 3.14 @@ -784,6 +784,19 @@ spec setup = do_round 3.1416 3 . should_equal 3.142 do_round 3.9999 3 . should_equal 4.0 + Test.specify "Can round negative decimals to a specified number of decimal places" <| + do_round -3.0001 2 . should_equal -3.0 + do_round -3.1414 2 . should_equal -3.14 + do_round -3.1415 2 . should_equal -3.14 + do_round -3.1416 2 . should_equal -3.14 + do_round -3.9999 2 . should_equal -4.0 + + do_round -3.0001 3 . should_equal -3.0 + do_round -3.1414 3 . should_equal -3.141 + do_round -3.1415 3 . should_equal -3.142 + do_round -3.1416 3 . should_equal -3.142 + do_round -3.9999 3 . should_equal -4.0 + Test.specify "Can round positive decimals to a specified negative number of decimal places" <| do_round 1234.0 -1 . should_equal 1230 do_round 1234.0 -2 . should_equal 1200 diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 75d5d2ce12b7..9ed730db5f96 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -504,7 +504,7 @@ spec = 0.00001 . round . should_equal 0 -0.00001 . round . should_equal 0 - Test.specify "Can round decimals to a specified number of decimal places" <| + Test.specify "Can round positive decimals to a specified number of decimal places" <| 3.0001 . round 2 . should_equal 3.0 3.1414 . round 2 . should_equal 3.14 3.1415 . round 2 . should_equal 3.14 @@ -517,6 +517,19 @@ spec = 3.1416 . round 3 . should_equal 3.142 3.9999 . round 3 . should_equal 4.0 + Test.specify "Can round negative decimals to a specified number of decimal places" <| + -3.0001 . round 2 . should_equal -3.0 + -3.1414 . round 2 . should_equal -3.14 + -3.1415 . round 2 . should_equal -3.14 + -3.1416 . round 2 . should_equal -3.14 + -3.9999 . round 2 . should_equal -4.0 + + -3.0001 . round 3 . should_equal -3.0 + -3.1414 . round 3 . should_equal -3.141 + -3.1415 . round 3 . should_equal -3.142 + -3.1416 . round 3 . should_equal -3.142 + -3.9999 . round 3 . should_equal -4.0 + Test.specify "Can round positive decimals to a specified negative number of decimal places" <| 1234.0 . round -1 . should_equal 1230 1234.0 . round -2 . should_equal 1200 From 63f29825828085743b14456d6117f5fd4197bf0d Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 14:37:10 -0400 Subject: [PATCH 24/36] use DoubleBuilder --- .../column/operation/map/numeric/DoubleRoundOp.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 1c3abfe2878c..e777ff3b0e31 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 @@ -1,6 +1,8 @@ package org.enso.table.data.column.operation.map.numeric; import org.enso.polyglot.common_utils.Core_Math_Utils; +import org.enso.table.data.column.builder.DoubleBuilder; +import org.enso.table.data.column.builder.NumericBuilder; import org.enso.table.data.column.operation.map.TernaryMapOperation; import org.enso.table.data.column.operation.map.MapOperationProblemBuilder; import org.enso.table.data.column.storage.numeric.DoubleStorage; @@ -62,27 +64,26 @@ public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec return new LongStorage(out, storage.size(), isMissing); } else { // Return double storage. - long[] out = new long[storage.size()]; - BitSet isMissing = new BitSet(); + DoubleBuilder doubleBuilder = NumericBuilder.createDoubleBuilder(storage.size()); for (int i = 0; i < storage.size(); i++) { if (!storage.isNa(i)) { double item = storage.getItem(i); boolean special = Double.isNaN(item) || Double.isInfinite(item); if (!special) { - out[i] = Double.doubleToRawLongBits(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers)); + doubleBuilder.appendDouble(Core_Math_Utils.roundDouble(item, decimalPlaces, useBankers)); } else { String msg = "Value is " + item; problemBuilder.reportArithmeticError(msg, i); - isMissing.set(i); + doubleBuilder.appendNulls(1); } } else { - isMissing.set(i); + doubleBuilder.appendNulls(1); } context.safepoint(); } - return new DoubleStorage(out, storage.size(), isMissing); + return doubleBuilder.seal(); } } } From dce30803225d54db60508fb73afd3ac18f094e11 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 14:51:30 -0400 Subject: [PATCH 25/36] move fast return into java --- .../lib/Standard/Table/0.0.0-dev/src/Data/Column.enso | 4 +--- .../column/operation/map/numeric/DoubleRoundOp.java | 1 + .../data/column/operation/map/numeric/LongRoundOp.java | 10 ++++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) 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 b131ac958a4a..2fe1b978e0d0 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 @@ -770,9 +770,7 @@ type Column round self (decimal_places:Integer = 0) (use_bankers:Boolean = False) = Value_Type.expect_numeric self <| Illegal_Argument.handle_java_exception <| Rounding_Helpers.check_decimal_places decimal_places <| new_name = naming_helper.function_name "round" [self] - is_integer = self.inferred_precise_value_type.is_integer - if is_integer && decimal_places >= 0 then self.rename new_name else - run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name + run_vectorized_ternary_op self Java_Storage.Maps.ROUND decimal_places use_bankers new_name=new_name ## ALIAS int 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 e777ff3b0e31..f102d0ee54e9 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 @@ -33,6 +33,7 @@ public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObjec if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } + if (!(useBankersObject instanceof Boolean useBankers)) { throw new UnexpectedTypeException("a boolean."); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java index 635168f29086..6564c5eb67d5 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java @@ -14,10 +14,10 @@ /** An operation rounding integers. */ public class LongRoundOp extends TernaryMapOperation { - /** Minimum value for the `n` parameter to `roundDouble`. */ + /** Minimum value for the `n` parameter to `roundLong`. */ private static final long ROUND_MIN_LONG = -99999999999999L; - /** Minimum value for the `n` parameter to `roundDouble`. */ + /** Minimum value for the `n` parameter to `roundLong`. */ private static final long ROUND_MAX_LONG = 99999999999999L; public LongRoundOp(String name) { @@ -37,10 +37,16 @@ public Storage runTernaryMap(AbstractLongStorage storage, Object decimalPl if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } + if (!(useBankersObject instanceof Boolean useBankers)) { throw new UnexpectedTypeException("a boolean."); } + if (decimalPlaces >= 0) { + // No change, return original storage. + return storage; + } + Context context = Context.getCurrent(); long[] out = new long[storage.size()]; BitSet isMissing = new BitSet(); From 4ae155d32fd6346ba6ad7bcdf4bfc36703126c97 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 14:57:21 -0400 Subject: [PATCH 26/36] cleanup --- test/Benchmarks/src/Column_Numeric.enso | 2 +- test/Table_Tests/src/In_Memory/Column_Spec.enso | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index 97185248af77..3b04800aeab4 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -41,7 +41,7 @@ bench = [True, False].map use_bankers-> [0, -2, 2].map decimal_places-> name = "round decimal_places=" + decimal_places.to_text + " use_bankers=" + use_bankers.to_text - fun = _.round #decimal_places use_bankers + fun = _.round decimal_places use_bankers IO.println <| ".round floats" Bench.measure (fun floats) "float "+name iter_size num_iterations diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 8850ddb67df1..9ba7e217ff83 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -16,8 +16,6 @@ from Standard.Test import Test, Test_Suite, Problems main = Test_Suite.run_main spec spec = - Test.group "asdfasdf" <| - Test.group "Columns" <| test_column = Column.from_vector "Test" [1, 3, 5, 2, 4, 6] empty_column = Column.from_vector "Test" [] From c6be7fea42c098e790f7d03d09746044bc6f175a Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 15:21:03 -0400 Subject: [PATCH 27/36] value_type tests --- .../src/In_Memory/Column_Spec.enso | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/test/Table_Tests/src/In_Memory/Column_Spec.enso b/test/Table_Tests/src/In_Memory/Column_Spec.enso index 9ba7e217ff83..baf74debf783 100644 --- a/test/Table_Tests/src/In_Memory/Column_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Column_Spec.enso @@ -132,9 +132,10 @@ spec = Column.from_vector "foo" [-12.0, -24.0, -25.0, -29.0] . round -1 . should_equal <| Column.from_vector "round([foo])" [-10, -20, -30, -30] Test.specify "decimal rounding should return the correct column type" <| - Column.from_vector "foo" [1.21, 2.34, 3.68] . round -1 . value_type . should_equal Value_Type.Integer - Column.from_vector "foo" [1.21, 2.34, 3.68] . round . value_type . should_equal Value_Type.Integer - Column.from_vector "foo" [1.21, 2.34, 3.68] . round 1 . value_type . should_equal Value_Type.Float + col = Column.from_vector "foo" [1.21, 2.34, 3.68] + col . round -1 . value_type . should_equal Value_Type.Integer + col . round . value_type . should_equal Value_Type.Integer + col . round 1 . value_type . should_equal Value_Type.Float Test.specify "should be able to round a column of integers" <| Column.from_vector "foo" [12, 24, 25, 29] . round . should_equal <| Column.from_vector "round([foo])" [12, 24, 25, 29] @@ -142,9 +143,10 @@ spec = Column.from_vector "foo" [15, 25, 35] . round -1 use_bankers=True . should_equal <| Column.from_vector "round([foo])" [20, 20, 40] Test.specify "integer rounding should return the correct column type" <| - Column.from_vector "foo" [12, 24, 25, 29] . round 1 . value_type . should_equal Value_Type.Integer - Column.from_vector "foo" [12, 24, 25, 29] . round 0 . value_type . should_equal Value_Type.Integer - Column.from_vector "foo" [12, 24, 25, 29] . round -1 . value_type . should_equal Value_Type.Integer + col = Column.from_vector "foo" [12, 24, 25, 29] + col . round 1 . value_type . should_equal Value_Type.Integer + col . round 0 . value_type . should_equal Value_Type.Integer + col . round -1 . value_type . should_equal Value_Type.Integer Test.specify "rounding should not attach a warning by default" <| Problems.assume_no_problems <| Column.from_vector "foo" [12, 24, 25, 29] . round 1 @@ -160,12 +162,6 @@ spec = col = Column.from_vector "foo" [12, 23, 99999999999999999] col.round decimal_places=-1200 . should_fail_with Illegal_Argument - Test.specify "should return the correct column type" <| - col = Column.from_vector "foo" [0.51, 0.59, 3.51, 3.59, -0.51, -0.59, -3.51, -3.59] - col.round 1 . value_type . should_equal Value_Type.Float - col.round . value_type . should_equal Value_Type.Integer - col.round -1 . value_type . should_equal Value_Type.Integer - Test.specify "should handle type errors" <| col = Column.from_vector "foo" [12, 23, 45] Test.expect_panic_with (col.round use_bankers="string") Type_Error From 5c7980e3ce105f473ab3a8035bc8c8b6b66c0785 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Fri, 11 Aug 2023 15:37:08 -0400 Subject: [PATCH 28/36] 1M --- test/Benchmarks/src/Column_Numeric.enso | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Benchmarks/src/Column_Numeric.enso b/test/Benchmarks/src/Column_Numeric.enso index 3b04800aeab4..a58aeab871a4 100644 --- a/test/Benchmarks/src/Column_Numeric.enso +++ b/test/Benchmarks/src/Column_Numeric.enso @@ -5,8 +5,8 @@ from Standard.Test import Bench, Faker ## Bench Utilities ============================================================ -vector_size = 100000 -iter_size = 100 +vector_size = 1000000 +iter_size = 10 num_iterations = 10 # The Benchmarks ============================================================== @@ -41,7 +41,7 @@ bench = [True, False].map use_bankers-> [0, -2, 2].map decimal_places-> name = "round decimal_places=" + decimal_places.to_text + " use_bankers=" + use_bankers.to_text - fun = _.round decimal_places use_bankers + fun x = x.round decimal_places use_bankers IO.println <| ".round floats" Bench.measure (fun floats) "float "+name iter_size num_iterations From d413b11e55074dddd165858ea0104ef6fade52f6 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 10:05:00 -0400 Subject: [PATCH 29/36] unnecessary null checks --- .../data/column/operation/map/numeric/DoubleRoundOp.java | 8 -------- .../data/column/operation/map/numeric/LongRoundOp.java | 8 -------- 2 files changed, 16 deletions(-) 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 f102d0ee54e9..da1cad53680b 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 @@ -22,14 +22,6 @@ public DoubleRoundOp(String name) { @Override public Storage runTernaryMap(DoubleStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { - if (decimalPlacesObject == null) { - throw new IllegalArgumentException("decimalPlacesObject must not be null"); - } - - if (useBankersObject == null) { - throw new IllegalArgumentException("useBankersObject must not be null"); - } - if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } diff --git a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java index 6564c5eb67d5..9c8c2ee8d559 100644 --- a/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java +++ b/std-bits/table/src/main/java/org/enso/table/data/column/operation/map/numeric/LongRoundOp.java @@ -26,14 +26,6 @@ public LongRoundOp(String name) { @Override public Storage runTernaryMap(AbstractLongStorage storage, Object decimalPlacesObject, Object useBankersObject, MapOperationProblemBuilder problemBuilder) { - if (decimalPlacesObject == null) { - throw new IllegalArgumentException("decimalPlacesObject must not be null"); - } - - if (useBankersObject == null) { - throw new IllegalArgumentException("useBankersObject must not be null"); - } - if (!(decimalPlacesObject instanceof Long decimalPlaces)) { throw new UnexpectedTypeException("a long."); } From 641e903fdfc1622866972c783bd223936607d886 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 10:43:59 -0400 Subject: [PATCH 30/36] standalone RS --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 1 + test/Tests/src/Data/Numbers_Spec.enso | 3 - test/Tests/src/Data/Round_Spec.enso | 287 ++++++++++++++++++ 3 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 test/Tests/src/Data/Round_Spec.enso diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index d1d279ffb4e9..c019be7df6cd 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -9,6 +9,7 @@ import project.Function.Function import project.Internal.Rounding_Helpers import project.Nothing.Nothing import project.Panic.Panic +import project.IO from project.Data.Boolean import Boolean, False, True from project.Errors.Common import Unsupported_Argument_Types diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 3071299619ff..bb425c0dad31 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -883,11 +883,8 @@ spec = [x, y, z, w].each b-> a+b . should_equal 20 - - main = Test_Suite.run_main spec - foreign js to_js_bigint n = """ return BigInt(n) diff --git a/test/Tests/src/Data/Round_Spec.enso b/test/Tests/src/Data/Round_Spec.enso new file mode 100644 index 000000000000..71193498bf62 --- /dev/null +++ b/test/Tests/src/Data/Round_Spec.enso @@ -0,0 +1,287 @@ +from Standard.Base import all +import Standard.Base.Errors.Common.Arithmetic_Error +import Standard.Base.Errors.Common.Incomparable_Values +import Standard.Base.Errors.Common.Type_Error +import Standard.Base.Errors.Illegal_Argument.Illegal_Argument + +from Standard.Base.Data.Numbers import Number_Parse_Error + +from Standard.Test import Test, Test_Suite +import Standard.Test.Extensions + +polyglot java import java.math.BigInteger + +spec prefix round_fun = + Test.group prefix+"Rounding numeric tests" <| + Test.specify "Can round positive decimals correctly" <| + round_fun 3.0 . should_equal 3 + round_fun 3.00001 . should_equal 3 + round_fun 3.3 . should_equal 3 + round_fun 3.49999 . should_equal 3 + round_fun 3.5 . should_equal 4 + round_fun 3.50001 . should_equal 4 + round_fun 3.99999 . should_equal 4 + + Test.specify "Can round negative decimals correctly" <| + round_fun -3.0 . should_equal -3 + round_fun -3.00001 . should_equal -3 + round_fun -3.3 . should_equal -3 + round_fun -3.49999 . should_equal -3 + round_fun -3.5 . should_equal -4 + round_fun -3.50001 . should_equal -4 + round_fun -3.99999 . should_equal -4 + + Test.specify "Explicit and implicit 0 decimal places work the same" <| + round_fun 3.00001 0 . should_equal 3 + round_fun 3.3 0 . should_equal 3 + round_fun 3.00001 . should_equal 3 + round_fun 3.3 . should_equal 3 + + Test.specify "Can round zero and small decimals correctly" <| + round_fun 0.0 . should_equal 0 + round_fun 0.00001 . should_equal 0 + round_fun -0.00001 . should_equal 0 + + Test.specify "Can round positive decimals to a specified number of decimal places" <| + round_fun 3.0001 2 . should_equal 3.0 + round_fun 3.1414 2 . should_equal 3.14 + round_fun 3.1415 2 . should_equal 3.14 + round_fun 3.1416 2 . should_equal 3.14 + round_fun 3.9999 2 . should_equal 4.0 + + round_fun 3.0001 3 . should_equal 3.0 + round_fun 3.1414 3 . should_equal 3.141 + round_fun 3.1415 3 . should_equal 3.142 + round_fun 3.1416 3 . should_equal 3.142 + round_fun 3.9999 3 . should_equal 4.0 + + Test.specify "Can round negative decimals to a specified number of decimal places" <| + round_fun -3.0001 2 . should_equal -3.0 + round_fun -3.1414 2 . should_equal -3.14 + round_fun -3.1415 2 . should_equal -3.14 + round_fun -3.1416 2 . should_equal -3.14 + round_fun -3.9999 2 . should_equal -4.0 + + round_fun -3.0001 3 . should_equal -3.0 + round_fun -3.1414 3 . should_equal -3.141 + round_fun -3.1415 3 . should_equal -3.142 + round_fun -3.1416 3 . should_equal -3.142 + round_fun -3.9999 3 . should_equal -4.0 + + Test.specify "Can round positive decimals to a specified negative number of decimal places" <| + round_fun 1234.0 -1 . should_equal 1230 + round_fun 1234.0 -2 . should_equal 1200 + round_fun 1234.0 -3 . should_equal 1000 + round_fun 1234.0 -4 . should_equal 0 + + round_fun 1499.0 -1 . should_equal 1500 + round_fun 1499.0 -2 . should_equal 1500 + round_fun 1499.0 -3 . should_equal 1000 + + round_fun 1495.0 -1 . should_equal 1500 + round_fun 1494.0 -1 . should_equal 1490 + round_fun 1495.0 -2 . should_equal 1500 + round_fun 1494.0 -2 . should_equal 1500 + + Test.specify "Can round negative decimals to a specified negative number of decimal places" <| + round_fun -1234.0 -1 . should_equal -1230 + round_fun -1234.0 -2 . should_equal -1200 + round_fun -1234.0 -3 . should_equal -1000 + round_fun -1234.0 -4 . should_equal 0 + + round_fun -1499.0 -1 . should_equal -1500 + round_fun -1499.0 -2 . should_equal -1500 + round_fun -1499.0 -3 . should_equal -1000 + + round_fun -1495.0 -1 . should_equal -1500 + round_fun -1494.0 -1 . should_equal -1490 + round_fun -1495.0 -2 . should_equal -1500 + round_fun -1494.0 -2 . should_equal -1500 + + Test.specify "Banker's rounding handles half-way values correctly" <| + round_fun -3.5 use_bankers=True . should_equal -4 + round_fun -2.5 use_bankers=True . should_equal -2 + round_fun -1.5 use_bankers=True . should_equal -2 + round_fun -0.5 use_bankers=True . should_equal 0 + round_fun 0.5 use_bankers=True . should_equal 0 + round_fun 1.5 use_bankers=True . should_equal 2 + round_fun 2.5 use_bankers=True . should_equal 2 + round_fun 3.5 use_bankers=True . should_equal 4 + + round_fun 0.235 2 use_bankers=True . should_equal 0.24 + round_fun 0.225 2 use_bankers=True . should_equal 0.22 + round_fun -0.235 2 use_bankers=True . should_equal -0.24 + round_fun -0.225 2 use_bankers=True . should_equal -0.22 + + round_fun 12350.0 -2 use_bankers=True . should_equal 12400 + round_fun 12250.0 -2 use_bankers=True . should_equal 12200 + round_fun -12350.0 -2 use_bankers=True . should_equal -12400 + round_fun -12250.0 -2 use_bankers=True . should_equal -12200 + + Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| + round_fun 3.0 use_bankers=True . should_equal 3 + round_fun 3.00001 use_bankers=True . should_equal 3 + round_fun 3.3 use_bankers=True . should_equal 3 + round_fun 3.49999 use_bankers=True . should_equal 3 + round_fun 3.50001 use_bankers=True . should_equal 4 + round_fun 3.99999 use_bankers=True . should_equal 4 + + round_fun -3.0 . should_equal -3 + round_fun -3.00001 . should_equal -3 + round_fun -3.3 . should_equal -3 + round_fun -3.49999 . should_equal -3 + round_fun -3.50001 . should_equal -4 + round_fun -3.99999 . should_equal -4 + + Test.specify "Can round correctly near the precision limit" <| + round_fun 1.22222222225 10 . should_equal 1.2222222223 + round_fun 1.222222222225 11 . should_equal 1.22222222223 + round_fun 1.2222222222225 12 . should_equal 1.222222222223 + round_fun 1.22222222222225 13 . should_equal 1.2222222222223 + round_fun 1.222222222222225 14 . should_equal 1.22222222222223 + + round_fun -1.22222222225 10 . should_equal -1.2222222223 + round_fun -1.222222222225 11 . should_equal -1.22222222223 + round_fun -1.2222222222225 12 . should_equal -1.222222222223 + round_fun -1.22222222222225 13 . should_equal -1.2222222222223 + round_fun -1.222222222222225 14 . should_equal -1.22222222222223 + + round_fun 1.22222222235 10 . should_equal 1.2222222224 + round_fun 1.222222222235 11 . should_equal 1.22222222224 + round_fun 1.2222222222235 12 . should_equal 1.222222222224 + round_fun 1.22222222222235 13 . should_equal 1.2222222222224 + round_fun 1.222222222222235 14 . should_equal 1.22222222222224 + + round_fun -1.22222222235 10 . should_equal -1.2222222224 + round_fun -1.222222222235 11 . should_equal -1.22222222224 + round_fun -1.2222222222235 12 . should_equal -1.222222222224 + round_fun -1.22222222222235 13 . should_equal -1.2222222222224 + round_fun -1.222222222222235 14 . should_equal -1.22222222222224 + + Test.specify "Can round correctly near the precision limit, using banker's rounding" <| + round_fun 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 + round_fun 1.222222222225 11 use_bankers=True . should_equal 1.22222222222 + round_fun 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 + round_fun 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 + round_fun 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 + + round_fun -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 + round_fun -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 + round_fun -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 + round_fun -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 + round_fun -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 + + round_fun 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 + round_fun 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 + round_fun 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 + round_fun 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 + round_fun 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 + + round_fun -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 + round_fun -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 + round_fun -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 + round_fun -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 + round_fun -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 + + Test.specify "Decimal places out of range" <| + round_fun 3.1 16 . should_fail_with Illegal_Argument + round_fun 3.1 -16 . should_fail_with Illegal_Argument + + Test.specify "Floating point imperfect representation counter-examples" <| + round_fun 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 + round_fun 37.785 2 . should_equal 37.79 + + Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" + round_fun 0 . should_equal 0 + round_fun 3 . should_equal 3 + round_fun -3 . should_equal -3 + round_fun 3 0 . should_equal 3 + round_fun -3 0 . should_equal -3 + round_fun 3 1 . should_equal 3 + round_fun -3 1 . should_equal -3 + + Test.specify "Can round integers to a specified number of negative places correctly" + round_fun 0 -1 . should_equal 0 + round_fun 4 -1 . should_equal 0 + round_fun 5 -1 . should_equal 10 + round_fun 6 -1 . should_equal 10 + round_fun 9 -1 . should_equal 10 + round_fun 10 -1 . should_equal 10 + round_fun 11 -1 . should_equal 10 + round_fun 24 -1 . should_equal 20 + round_fun 25 -1 . should_equal 30 + round_fun 29 -1 . should_equal 30 + round_fun 30 -1 . should_equal 30 + round_fun 31 -1 . should_equal 30 + + round_fun 2000 -3 . should_equal 2000 + round_fun 2001 -3 . should_equal 2000 + round_fun 2412 -3 . should_equal 2000 + round_fun 2499 -3 . should_equal 2000 + round_fun 2500 -3 . should_equal 3000 + round_fun 2501 -3 . should_equal 3000 + round_fun 2511 -3 . should_equal 3000 + round_fun 2907 -3 . should_equal 3000 + round_fun 2999 -3 . should_equal 3000 + round_fun 3000 -3 . should_equal 3000 + round_fun 3001 -3 . should_equal 3000 + round_fun 3098 -3 . should_equal 3000 + round_fun 3101 -3 . should_equal 3000 + + Test.specify "Can round negative integers to a specified number of negative places correctly" + round_fun -4 -1 . should_equal 0 + round_fun -5 -1 . should_equal -10 + round_fun -6 -1 . should_equal -10 + round_fun -9 -1 . should_equal -10 + round_fun -10 -1 . should_equal -10 + round_fun -11 -1 . should_equal -10 + round_fun -24 -1 . should_equal -20 + round_fun -25 -1 . should_equal -30 + round_fun -29 -1 . should_equal -30 + round_fun -30 -1 . should_equal -30 + round_fun -31 -1 . should_equal -30 + + round_fun -2000 -3 . should_equal -2000 + round_fun -2001 -3 . should_equal -2000 + round_fun -2412 -3 . should_equal -2000 + round_fun -2499 -3 . should_equal -2000 + round_fun -2500 -3 . should_equal -3000 + round_fun -2501 -3 . should_equal -3000 + round_fun -2511 -3 . should_equal -3000 + round_fun -2907 -3 . should_equal -3000 + round_fun -2999 -3 . should_equal -3000 + round_fun -3000 -3 . should_equal -3000 + round_fun -3001 -3 . should_equal -3000 + round_fun -3098 -3 . should_equal -3000 + round_fun -3101 -3 . should_equal -3000 + + Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| + round_fun 12300 -2 use_bankers=True . should_equal 12300 + round_fun 12301 -2 use_bankers=True . should_equal 12300 + round_fun 12330 -2 use_bankers=True . should_equal 12300 + round_fun 12349 -2 use_bankers=True . should_equal 12300 + round_fun 12350 -2 use_bankers=True . should_equal 12400 + round_fun 12351 -2 use_bankers=True . should_equal 12400 + round_fun 12370 -2 use_bankers=True . should_equal 12400 + round_fun 12430 -2 use_bankers=True . should_equal 12400 + round_fun 12470 -2 use_bankers=True . should_equal 12500 + + round_fun 12249 -2 use_bankers=True . should_equal 12200 + round_fun 12250 -2 use_bankers=True . should_equal 12200 + round_fun 12251 -2 use_bankers=True . should_equal 12300 + + round_fun -12300 -2 use_bankers=True . should_equal -12300 + round_fun -12301 -2 use_bankers=True . should_equal -12300 + round_fun -12330 -2 use_bankers=True . should_equal -12300 + round_fun -12349 -2 use_bankers=True . should_equal -12300 + round_fun -12350 -2 use_bankers=True . should_equal -12400 + round_fun -12351 -2 use_bankers=True . should_equal -12400 + round_fun -12370 -2 use_bankers=True . should_equal -12400 + round_fun -12430 -2 use_bankers=True . should_equal -12400 + round_fun -12470 -2 use_bankers=True . should_equal -12500 + + round_fun -12249 -2 use_bankers=True . should_equal -12200 + round_fun -12250 -2 use_bankers=True . should_equal -12200 + round_fun -12251 -2 use_bankers=True . should_equal -12300 + +main = Test_Suite.run_main (spec "Number " .round) From b895d77cf0e6e9e569ee55b5c6a72e65a7c45893 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 11:49:45 -0400 Subject: [PATCH 31/36] runnable from COS, after moving shared --- .../lib/Standard/Test/0.0.0-dev/src/Shared}/Round_Spec.enso | 2 +- .../src/Common_Table_Operations/Column_Operations_Spec.enso | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) rename {test/Tests/src/Data => distribution/lib/Standard/Test/0.0.0-dev/src/Shared}/Round_Spec.enso (99%) diff --git a/test/Tests/src/Data/Round_Spec.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso similarity index 99% rename from test/Tests/src/Data/Round_Spec.enso rename to distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso index 71193498bf62..beeee5bf558d 100644 --- a/test/Tests/src/Data/Round_Spec.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso @@ -12,7 +12,7 @@ import Standard.Test.Extensions polyglot java import java.math.BigInteger spec prefix round_fun = - Test.group prefix+"Rounding numeric tests" <| + Test.group prefix+"Rounding numeric testsasdfasdf" <| Test.specify "Can round positive decimals correctly" <| round_fun 3.0 . should_equal 3 round_fun 3.00001 . should_equal 3 diff --git a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso index ccf59e40a91f..bdb2b9c6dcbe 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso @@ -6,6 +6,7 @@ import Standard.Base.Errors.Illegal_Argument.Illegal_Argument import Standard.Base.Meta.Type import Standard.Database.Data.Column.Column import Standard.Database.Internal.Replace_Params.Replace_Params +import Standard.Test.Shared.Round_Spec from Standard.Table import Value_Type from Standard.Table.Data.Type.Value_Type import Bits @@ -30,6 +31,8 @@ spec setup = result.to_vector.at 0 do_round n dp=0 use_bankers=False = do_op n (_.round dp use_bankers) + Round_Spec.spec prefix do_round + ## Runs the provided callback with a few combinations of columns, where some of them are made Mixed (but still contain only the original values). If the backend does not support mixed columns, the callback is run only From c328a2695dbd9b362206fad7f4bc4f6728cf58e9 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 12:28:18 -0400 Subject: [PATCH 32/36] from Number as well --- test/Tests/src/Data/Numbers_Spec.enso | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index bb425c0dad31..98950e286b72 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -3,6 +3,7 @@ import Standard.Base.Errors.Common.Arithmetic_Error import Standard.Base.Errors.Common.Incomparable_Values import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument +import Standard.Test.Shared.Round_Spec from Standard.Base.Data.Numbers import Number_Parse_Error @@ -24,6 +25,8 @@ spec = hundred_factorial = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 very_negative = -99223372036854775808 + Round_Spec.spec "Number " .round + Test.group "Integers" <| Test.specify "should be of unbound size when multiplied" <| From 0b05c8a73d943e4e846d419d39b898f5e10d1f04 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 12:31:18 -0400 Subject: [PATCH 33/36] remove old --- .../Column_Operations_Spec.enso | 272 ---------------- test/Tests/src/Data/Numbers_Spec.enso | 303 +----------------- 2 files changed, 1 insertion(+), 574 deletions(-) diff --git a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso index bdb2b9c6dcbe..6efb295f8303 100644 --- a/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso +++ b/test/Table_Tests/src/Common_Table_Operations/Column_Operations_Spec.enso @@ -744,278 +744,6 @@ spec setup = x.round . to_vector . should_equal [1, -2, 4] x.truncate . to_vector . should_equal [1, -2, 3] - Test.group prefix+"Rounding numeric tests" <| - Test.specify "Can round positive decimals correctly" <| - do_round 3.0 . should_equal 3 - do_round 3.00001 . should_equal 3 - do_round 3.3 . should_equal 3 - do_round 3.49999 . should_equal 3 - do_round 3.5 . should_equal 4 - do_round 3.50001 . should_equal 4 - do_round 3.99999 . should_equal 4 - - Test.specify "Can round negative decimals correctly" <| - do_round -3.0 . should_equal -3 - do_round -3.00001 . should_equal -3 - do_round -3.3 . should_equal -3 - do_round -3.49999 . should_equal -3 - do_round -3.5 . should_equal -4 - do_round -3.50001 . should_equal -4 - do_round -3.99999 . should_equal -4 - - Test.specify "Explicit and implicit 0 decimal places work the same" <| - do_round 3.00001 0 . should_equal 3 - do_round 3.3 0 . should_equal 3 - do_round 3.00001 . should_equal 3 - do_round 3.3 . should_equal 3 - - Test.specify "Can round zero and small decimals correctly" <| - do_round 0.0 . should_equal 0 - do_round 0.00001 . should_equal 0 - do_round -0.00001 . should_equal 0 - - Test.specify "Can round positive decimals to a specified number of decimal places" <| - do_round 3.0001 2 . should_equal 3.0 - do_round 3.1414 2 . should_equal 3.14 - do_round 3.1415 2 . should_equal 3.14 - do_round 3.1416 2 . should_equal 3.14 - do_round 3.9999 2 . should_equal 4.0 - - do_round 3.0001 3 . should_equal 3.0 - do_round 3.1414 3 . should_equal 3.141 - do_round 3.1415 3 . should_equal 3.142 - do_round 3.1416 3 . should_equal 3.142 - do_round 3.9999 3 . should_equal 4.0 - - Test.specify "Can round negative decimals to a specified number of decimal places" <| - do_round -3.0001 2 . should_equal -3.0 - do_round -3.1414 2 . should_equal -3.14 - do_round -3.1415 2 . should_equal -3.14 - do_round -3.1416 2 . should_equal -3.14 - do_round -3.9999 2 . should_equal -4.0 - - do_round -3.0001 3 . should_equal -3.0 - do_round -3.1414 3 . should_equal -3.141 - do_round -3.1415 3 . should_equal -3.142 - do_round -3.1416 3 . should_equal -3.142 - do_round -3.9999 3 . should_equal -4.0 - - Test.specify "Can round positive decimals to a specified negative number of decimal places" <| - do_round 1234.0 -1 . should_equal 1230 - do_round 1234.0 -2 . should_equal 1200 - do_round 1234.0 -3 . should_equal 1000 - do_round 1234.0 -4 . should_equal 0 - - do_round 1499.0 -1 . should_equal 1500 - do_round 1499.0 -2 . should_equal 1500 - do_round 1499.0 -3 . should_equal 1000 - - do_round 1495.0 -1 . should_equal 1500 - do_round 1494.0 -1 . should_equal 1490 - do_round 1495.0 -2 . should_equal 1500 - do_round 1494.0 -2 . should_equal 1500 - - Test.specify "Can round negative decimals to a specified negative number of decimal places" <| - do_round -1234.0 -1 . should_equal -1230 - do_round -1234.0 -2 . should_equal -1200 - do_round -1234.0 -3 . should_equal -1000 - do_round -1234.0 -4 . should_equal 0 - - do_round -1499.0 -1 . should_equal -1500 - do_round -1499.0 -2 . should_equal -1500 - do_round -1499.0 -3 . should_equal -1000 - - do_round -1495.0 -1 . should_equal -1500 - do_round -1494.0 -1 . should_equal -1490 - do_round -1495.0 -2 . should_equal -1500 - do_round -1494.0 -2 . should_equal -1500 - - Test.specify "Banker's rounding handles half-way values correctly" <| - do_round -3.5 use_bankers=True . should_equal -4 - do_round -2.5 use_bankers=True . should_equal -2 - do_round -1.5 use_bankers=True . should_equal -2 - do_round -0.5 use_bankers=True . should_equal 0 - do_round 0.5 use_bankers=True . should_equal 0 - do_round 1.5 use_bankers=True . should_equal 2 - do_round 2.5 use_bankers=True . should_equal 2 - do_round 3.5 use_bankers=True . should_equal 4 - - do_round 0.235 2 use_bankers=True . should_equal 0.24 - do_round 0.225 2 use_bankers=True . should_equal 0.22 - do_round -0.235 2 use_bankers=True . should_equal -0.24 - do_round -0.225 2 use_bankers=True . should_equal -0.22 - - do_round 12350.0 -2 use_bankers=True . should_equal 12400 - do_round 12250.0 -2 use_bankers=True . should_equal 12200 - do_round -12350.0 -2 use_bankers=True . should_equal -12400 - do_round -12250.0 -2 use_bankers=True . should_equal -12200 - - Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| - do_round 3.0 use_bankers=True . should_equal 3 - do_round 3.00001 use_bankers=True . should_equal 3 - do_round 3.3 use_bankers=True . should_equal 3 - do_round 3.49999 use_bankers=True . should_equal 3 - do_round 3.50001 use_bankers=True . should_equal 4 - do_round 3.99999 use_bankers=True . should_equal 4 - - do_round -3.0 . should_equal -3 - do_round -3.00001 . should_equal -3 - do_round -3.3 . should_equal -3 - do_round -3.49999 . should_equal -3 - do_round -3.50001 . should_equal -4 - do_round -3.99999 . should_equal -4 - - Test.specify "Can round correctly near the precision limit" <| - do_round 1.22222222225 10 . should_equal 1.2222222223 - do_round 1.222222222225 11 . should_equal 1.22222222223 - do_round 1.2222222222225 12 . should_equal 1.222222222223 - do_round 1.22222222222225 13 . should_equal 1.2222222222223 - do_round 1.222222222222225 14 . should_equal 1.22222222222223 - - do_round -1.22222222225 10 . should_equal -1.2222222223 - do_round -1.222222222225 11 . should_equal -1.22222222223 - do_round -1.2222222222225 12 . should_equal -1.222222222223 - do_round -1.22222222222225 13 . should_equal -1.2222222222223 - do_round -1.222222222222225 14 . should_equal -1.22222222222223 - - do_round 1.22222222235 10 . should_equal 1.2222222224 - do_round 1.222222222235 11 . should_equal 1.22222222224 - do_round 1.2222222222235 12 . should_equal 1.222222222224 - do_round 1.22222222222235 13 . should_equal 1.2222222222224 - do_round 1.222222222222235 14 . should_equal 1.22222222222224 - - do_round -1.22222222235 10 . should_equal -1.2222222224 - do_round -1.222222222235 11 . should_equal -1.22222222224 - do_round -1.2222222222235 12 . should_equal -1.222222222224 - do_round -1.22222222222235 13 . should_equal -1.2222222222224 - do_round -1.222222222222235 14 . should_equal -1.22222222222224 - - Test.specify "Can round correctly near the precision limit, using banker's rounding" <| - do_round 1.22222222225 10 use_bankers=True . should_equal 1.2222222222 - do_round 1.222222222225 11 use_bankers=True . should_equal 1.22222222222 - do_round 1.2222222222225 12 use_bankers=True . should_equal 1.222222222222 - do_round 1.22222222222225 13 use_bankers=True . should_equal 1.2222222222222 - do_round 1.222222222222225 14 use_bankers=True . should_equal 1.22222222222222 - - do_round -1.22222222225 10 use_bankers=True . should_equal -1.2222222222 - do_round -1.222222222225 11 use_bankers=True . should_equal -1.22222222222 - do_round -1.2222222222225 12 use_bankers=True . should_equal -1.222222222222 - do_round -1.22222222222225 13 use_bankers=True . should_equal -1.2222222222222 - do_round -1.222222222222225 14 use_bankers=True . should_equal -1.22222222222222 - - do_round 1.22222222235 10 use_bankers=True . should_equal 1.2222222224 - do_round 1.222222222235 11 use_bankers=True . should_equal 1.22222222224 - do_round 1.2222222222235 12 use_bankers=True . should_equal 1.222222222224 - do_round 1.22222222222235 13 use_bankers=True . should_equal 1.2222222222224 - do_round 1.222222222222235 14 use_bankers=True . should_equal 1.22222222222224 - - do_round -1.22222222235 10 use_bankers=True . should_equal -1.2222222224 - do_round -1.222222222235 11 use_bankers=True . should_equal -1.22222222224 - do_round -1.2222222222235 12 use_bankers=True . should_equal -1.222222222224 - do_round -1.22222222222235 13 use_bankers=True . should_equal -1.2222222222224 - do_round -1.222222222222235 14 use_bankers=True . should_equal -1.22222222222224 - - Test.specify "Decimal places out of range" <| - do_round 3.1 16 . should_fail_with Illegal_Argument - do_round 3.1 -16 . should_fail_with Illegal_Argument - - Test.specify "Floating point imperfect representation counter-examples" <| - do_round 1.225 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 - do_round 37.785 2 . should_equal 37.79 - - Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" - do_round 0 . should_equal 0 - do_round 3 . should_equal 3 - do_round -3 . should_equal -3 - do_round 3 0 . should_equal 3 - do_round -3 0 . should_equal -3 - do_round 3 1 . should_equal 3 - do_round -3 1 . should_equal -3 - - Test.specify "Can round integers to a specified number of negative places correctly" - do_round 0 -1 . should_equal 0 - do_round 4 -1 . should_equal 0 - do_round 5 -1 . should_equal 10 - do_round 6 -1 . should_equal 10 - do_round 9 -1 . should_equal 10 - do_round 10 -1 . should_equal 10 - do_round 11 -1 . should_equal 10 - do_round 24 -1 . should_equal 20 - do_round 25 -1 . should_equal 30 - do_round 29 -1 . should_equal 30 - do_round 30 -1 . should_equal 30 - do_round 31 -1 . should_equal 30 - - do_round 2000 -3 . should_equal 2000 - do_round 2001 -3 . should_equal 2000 - do_round 2412 -3 . should_equal 2000 - do_round 2499 -3 . should_equal 2000 - do_round 2500 -3 . should_equal 3000 - do_round 2501 -3 . should_equal 3000 - do_round 2511 -3 . should_equal 3000 - do_round 2907 -3 . should_equal 3000 - do_round 2999 -3 . should_equal 3000 - do_round 3000 -3 . should_equal 3000 - do_round 3001 -3 . should_equal 3000 - do_round 3098 -3 . should_equal 3000 - do_round 3101 -3 . should_equal 3000 - - Test.specify "Can round negative integers to a specified number of negative places correctly" - do_round -4 -1 . should_equal 0 - do_round -5 -1 . should_equal -10 - do_round -6 -1 . should_equal -10 - do_round -9 -1 . should_equal -10 - do_round -10 -1 . should_equal -10 - do_round -11 -1 . should_equal -10 - do_round -24 -1 . should_equal -20 - do_round -25 -1 . should_equal -30 - do_round -29 -1 . should_equal -30 - do_round -30 -1 . should_equal -30 - do_round -31 -1 . should_equal -30 - - do_round -2000 -3 . should_equal -2000 - do_round -2001 -3 . should_equal -2000 - do_round -2412 -3 . should_equal -2000 - do_round -2499 -3 . should_equal -2000 - do_round -2500 -3 . should_equal -3000 - do_round -2501 -3 . should_equal -3000 - do_round -2511 -3 . should_equal -3000 - do_round -2907 -3 . should_equal -3000 - do_round -2999 -3 . should_equal -3000 - do_round -3000 -3 . should_equal -3000 - do_round -3001 -3 . should_equal -3000 - do_round -3098 -3 . should_equal -3000 - do_round -3101 -3 . should_equal -3000 - - Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| - do_round 12300 -2 use_bankers=True . should_equal 12300 - do_round 12301 -2 use_bankers=True . should_equal 12300 - do_round 12330 -2 use_bankers=True . should_equal 12300 - do_round 12349 -2 use_bankers=True . should_equal 12300 - do_round 12350 -2 use_bankers=True . should_equal 12400 - do_round 12351 -2 use_bankers=True . should_equal 12400 - do_round 12370 -2 use_bankers=True . should_equal 12400 - do_round 12430 -2 use_bankers=True . should_equal 12400 - do_round 12470 -2 use_bankers=True . should_equal 12500 - - do_round 12249 -2 use_bankers=True . should_equal 12200 - do_round 12250 -2 use_bankers=True . should_equal 12200 - do_round 12251 -2 use_bankers=True . should_equal 12300 - - do_round -12300 -2 use_bankers=True . should_equal -12300 - do_round -12301 -2 use_bankers=True . should_equal -12300 - do_round -12330 -2 use_bankers=True . should_equal -12300 - do_round -12349 -2 use_bankers=True . should_equal -12300 - do_round -12350 -2 use_bankers=True . should_equal -12400 - do_round -12351 -2 use_bankers=True . should_equal -12400 - do_round -12370 -2 use_bankers=True . should_equal -12400 - do_round -12430 -2 use_bankers=True . should_equal -12400 - do_round -12470 -2 use_bankers=True . should_equal -12500 - - do_round -12249 -2 use_bankers=True . should_equal -12200 - do_round -12250 -2 use_bankers=True . should_equal -12200 - do_round -12251 -2 use_bankers=True . should_equal -12300 - Test.group prefix+"Text Column Operations" <| Test.specify "should handle operations like starts_with, ends_with, contains" <| with_mixed_columns_if_supported [["s1", ["foobar", "bar", "baz", "BAB", Nothing]], ["s2", ["foo", "ar", "a", "b", Nothing]]] t3-> diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 98950e286b72..c0aa1a9556cb 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -477,308 +477,7 @@ spec = Number.nan . equals Number.nan . should_fail_with Incomparable_Values Number.nan . equals 0 . should_fail_with Incomparable_Values - Test.group "Decimal.round" <| - - Test.specify "Can round positive decimals correctly" <| - 3.0 . round . should_equal 3 - 3.00001 . round . should_equal 3 - 3.3 . round . should_equal 3 - 3.49999 . round . should_equal 3 - 3.5 . round . should_equal 4 - 3.50001 . round . should_equal 4 - 3.99999 . round . should_equal 4 - - Test.specify "Can round negative decimals correctly" <| - -3.0 . round . should_equal -3 - -3.00001 . round . should_equal -3 - -3.3 . round . should_equal -3 - -3.49999 . round . should_equal -3 - -3.5 . round . should_equal -4 - -3.50001 . round . should_equal -4 - -3.99999 . round . should_equal -4 - - Test.specify "Explicit and implicit 0 decimal places work the same" <| - 3.00001 . round 0 . should_equal 3 - 3.3 . round 0 . should_equal 3 - 3.00001 . round . should_equal 3 - 3.3 . round . should_equal 3 - - Test.specify "Can round zero and small decimals correctly" <| - 0.0 . round . should_equal 0 - 0.00001 . round . should_equal 0 - -0.00001 . round . should_equal 0 - - Test.specify "Can round positive decimals to a specified number of decimal places" <| - 3.0001 . round 2 . should_equal 3.0 - 3.1414 . round 2 . should_equal 3.14 - 3.1415 . round 2 . should_equal 3.14 - 3.1416 . round 2 . should_equal 3.14 - 3.9999 . round 2 . should_equal 4.0 - - 3.0001 . round 3 . should_equal 3.0 - 3.1414 . round 3 . should_equal 3.141 - 3.1415 . round 3 . should_equal 3.142 - 3.1416 . round 3 . should_equal 3.142 - 3.9999 . round 3 . should_equal 4.0 - - Test.specify "Can round negative decimals to a specified number of decimal places" <| - -3.0001 . round 2 . should_equal -3.0 - -3.1414 . round 2 . should_equal -3.14 - -3.1415 . round 2 . should_equal -3.14 - -3.1416 . round 2 . should_equal -3.14 - -3.9999 . round 2 . should_equal -4.0 - - -3.0001 . round 3 . should_equal -3.0 - -3.1414 . round 3 . should_equal -3.141 - -3.1415 . round 3 . should_equal -3.142 - -3.1416 . round 3 . should_equal -3.142 - -3.9999 . round 3 . should_equal -4.0 - - Test.specify "Can round positive decimals to a specified negative number of decimal places" <| - 1234.0 . round -1 . should_equal 1230 - 1234.0 . round -2 . should_equal 1200 - 1234.0 . round -3 . should_equal 1000 - 1234.0 . round -4 . should_equal 0 - - 1499.0 . round -1 . should_equal 1500 - 1499.0 . round -2 . should_equal 1500 - 1499.0 . round -3 . should_equal 1000 - - 1495.0 . round -1 . should_equal 1500 - 1494.0 . round -1 . should_equal 1490 - 1495.0 . round -2 . should_equal 1500 - 1494.0 . round -2 . should_equal 1500 - - Test.specify "Can round negative decimals to a specified negative number of decimal places" <| - -1234.0 . round -1 . should_equal -1230 - -1234.0 . round -2 . should_equal -1200 - -1234.0 . round -3 . should_equal -1000 - -1234.0 . round -4 . should_equal 0 - - -1499.0 . round -1 . should_equal -1500 - -1499.0 . round -2 . should_equal -1500 - -1499.0 . round -3 . should_equal -1000 - - -1495.0 . round -1 . should_equal -1500 - -1494.0 . round -1 . should_equal -1490 - -1495.0 . round -2 . should_equal -1500 - -1494.0 . round -2 . should_equal -1500 - - Test.specify "Banker's rounding handles half-way values correctly" <| - -3.5 . round use_bankers=True . should_equal -4 - -2.5 . round use_bankers=True . should_equal -2 - -1.5 . round use_bankers=True . should_equal -2 - -0.5 . round use_bankers=True . should_equal 0 - 0.5 . round use_bankers=True . should_equal 0 - 1.5 . round use_bankers=True . should_equal 2 - 2.5 . round use_bankers=True . should_equal 2 - 3.5 . round use_bankers=True . should_equal 4 - - 0.235 . round 2 use_bankers=True . should_equal 0.24 - 0.225 . round 2 use_bankers=True . should_equal 0.22 - -0.235 . round 2 use_bankers=True . should_equal -0.24 - -0.225 . round 2 use_bankers=True . should_equal -0.22 - - 12350.0 . round -2 use_bankers=True . should_equal 12400 - 12250.0 . round -2 use_bankers=True . should_equal 12200 - -12350.0 . round -2 use_bankers=True . should_equal -12400 - -12250.0 . round -2 use_bankers=True . should_equal -12200 - - Test.specify "Banker's rounding handles non-half-way values just like normal rounding" <| - 3.0 . round use_bankers=True . should_equal 3 - 3.00001 . round use_bankers=True . should_equal 3 - 3.3 . round use_bankers=True . should_equal 3 - 3.49999 . round use_bankers=True . should_equal 3 - 3.50001 . round use_bankers=True . should_equal 4 - 3.99999 . round use_bankers=True . should_equal 4 - - -3.0 . round . should_equal -3 - -3.00001 . round . should_equal -3 - -3.3 . round . should_equal -3 - -3.49999 . round . should_equal -3 - -3.50001 . round . should_equal -4 - -3.99999 . round . should_equal -4 - - Test.specify "Returns the correct type" <| - 231.2 . round 1 . should_be_a Decimal - 231.2 . round 0 . should_be_a Integer - 231.2 . round . should_be_a Integer - 231.2 . round -1 . should_be_a Integer - - Test.specify "Can round correctly near the precision limit" <| - 1.22222222225 . round 10 . should_equal 1.2222222223 - 1.222222222225 . round 11 . should_equal 1.22222222223 - 1.2222222222225 . round 12 . should_equal 1.222222222223 - 1.22222222222225 . round 13 . should_equal 1.2222222222223 - 1.222222222222225 . round 14 . should_equal 1.22222222222223 - 1.2222222222222225 . round 15 . should_equal 1.222222222222223 - - -1.22222222225 . round 10 . should_equal -1.2222222223 - -1.222222222225 . round 11 . should_equal -1.22222222223 - -1.2222222222225 . round 12 . should_equal -1.222222222223 - -1.22222222222225 . round 13 . should_equal -1.2222222222223 - -1.222222222222225 . round 14 . should_equal -1.22222222222223 - -1.2222222222222225 . round 15 . should_equal -1.222222222222223 - - 1.22222222235 . round 10 . should_equal 1.2222222224 - 1.222222222235 . round 11 . should_equal 1.22222222224 - 1.2222222222235 . round 12 . should_equal 1.222222222224 - 1.22222222222235 . round 13 . should_equal 1.2222222222224 - 1.222222222222235 . round 14 . should_equal 1.22222222222224 - 1.2222222222222235 . round 15 . should_equal 1.222222222222224 - - -1.22222222235 . round 10 . should_equal -1.2222222224 - -1.222222222235 . round 11 . should_equal -1.22222222224 - -1.2222222222235 . round 12 . should_equal -1.222222222224 - -1.22222222222235 . round 13 . should_equal -1.2222222222224 - -1.222222222222235 . round 14 . should_equal -1.22222222222224 - -1.2222222222222235 . round 15 . should_equal -1.222222222222224 - - Test.specify "Can round correctly near the precision limit, using banker's rounding" <| - 1.22222222225 . round 10 use_bankers=True . should_equal 1.2222222222 - 1.222222222225 . round 11 use_bankers=True . should_equal 1.22222222222 - 1.2222222222225 . round 12 use_bankers=True . should_equal 1.222222222222 - 1.22222222222225 . round 13 use_bankers=True . should_equal 1.2222222222222 - 1.222222222222225 . round 14 use_bankers=True . should_equal 1.22222222222222 - 1.2222222222222225 . round 15 use_bankers=True . should_equal 1.222222222222222 - - -1.22222222225 . round 10 use_bankers=True . should_equal -1.2222222222 - -1.222222222225 . round 11 use_bankers=True . should_equal -1.22222222222 - -1.2222222222225 . round 12 use_bankers=True . should_equal -1.222222222222 - -1.22222222222225 . round 13 use_bankers=True . should_equal -1.2222222222222 - -1.222222222222225 . round 14 use_bankers=True . should_equal -1.22222222222222 - -1.2222222222222225 . round 15 use_bankers=True . should_equal -1.222222222222222 - - 1.22222222235 . round 10 use_bankers=True . should_equal 1.2222222224 - 1.222222222235 . round 11 use_bankers=True . should_equal 1.22222222224 - 1.2222222222235 . round 12 use_bankers=True . should_equal 1.222222222224 - 1.22222222222235 . round 13 use_bankers=True . should_equal 1.2222222222224 - 1.222222222222235 . round 14 use_bankers=True . should_equal 1.22222222222224 - 1.2222222222222235 . round 15 use_bankers=True . should_equal 1.222222222222224 - - -1.22222222235 . round 10 use_bankers=True . should_equal -1.2222222224 - -1.222222222235 . round 11 use_bankers=True . should_equal -1.22222222224 - -1.2222222222235 . round 12 use_bankers=True . should_equal -1.222222222224 - -1.22222222222235 . round 13 use_bankers=True . should_equal -1.2222222222224 - -1.222222222222235 . round 14 use_bankers=True . should_equal -1.22222222222224 - -1.2222222222222235 . round 15 use_bankers=True . should_equal -1.222222222222224 - - Test.specify "Input out of range" <| - 100000000000000.0 . round . should_fail_with Illegal_Argument - -100000000000000.0 . round . should_fail_with Illegal_Argument - 100000000000000.0 . round -2 . should_fail_with Illegal_Argument - -100000000000000.0 . round -2 . should_fail_with Illegal_Argument - 99999999999999.0 . round -2 . should_equal 100000000000000 - -99999999999999.0 . round -2 . should_equal -100000000000000 - - Test.specify "Decimal places out of range" <| - 3.1 . round 16 . should_fail_with Illegal_Argument - 3.1 . round -16 . should_fail_with Illegal_Argument - - Test.specify "NaN/Inf" <| - Number.nan . round . should_fail_with Illegal_Argument - Number.positive_infinity . round . should_fail_with Illegal_Argument - Number.negative_infinity . round . should_fail_with Illegal_Argument - - Test.specify "Floating point imperfect representation counter-examples" <| - 1.225 . round 2 use_bankers=True . should_equal 1.22 # Actual result 1.23 - 37.785 . round 2 . should_equal 37.79 - - Test.group "Integer.round" <| - - Test.specify "Can round small integers to a specified number of decimal places correctly (value is unchanged)" - 0 . round . should_equal 0 - 3 . round . should_equal 3 - -3 . round . should_equal -3 - 3 . round 0 . should_equal 3 - -3 . round 0 . should_equal -3 - 3 . round 1 . should_equal 3 - -3 . round 1 . should_equal -3 - - Test.specify "Can round integers to a specified number of negative places correctly" - 0 . round -1 . should_equal 0 - 4 . round -1 . should_equal 0 - 5 . round -1 . should_equal 10 - 6 . round -1 . should_equal 10 - 9 . round -1 . should_equal 10 - 10 . round -1 . should_equal 10 - 11 . round -1 . should_equal 10 - 24 . round -1 . should_equal 20 - 25 . round -1 . should_equal 30 - 29 . round -1 . should_equal 30 - 30 . round -1 . should_equal 30 - 31 . round -1 . should_equal 30 - - 2000 . round -3 . should_equal 2000 - 2001 . round -3 . should_equal 2000 - 2412 . round -3 . should_equal 2000 - 2499 . round -3 . should_equal 2000 - 2500 . round -3 . should_equal 3000 - 2501 . round -3 . should_equal 3000 - 2511 . round -3 . should_equal 3000 - 2907 . round -3 . should_equal 3000 - 2999 . round -3 . should_equal 3000 - 3000 . round -3 . should_equal 3000 - 3001 . round -3 . should_equal 3000 - 3098 . round -3 . should_equal 3000 - 3101 . round -3 . should_equal 3000 - - Test.specify "Can round negative integers to a specified number of negative places correctly" - -4 . round -1 . should_equal 0 - -5 . round -1 . should_equal -10 - -6 . round -1 . should_equal -10 - -9 . round -1 . should_equal -10 - -10 . round -1 . should_equal -10 - -11 . round -1 . should_equal -10 - -24 . round -1 . should_equal -20 - -25 . round -1 . should_equal -30 - -29 . round -1 . should_equal -30 - -30 . round -1 . should_equal -30 - -31 . round -1 . should_equal -30 - - -2000 . round -3 . should_equal -2000 - -2001 . round -3 . should_equal -2000 - -2412 . round -3 . should_equal -2000 - -2499 . round -3 . should_equal -2000 - -2500 . round -3 . should_equal -3000 - -2501 . round -3 . should_equal -3000 - -2511 . round -3 . should_equal -3000 - -2907 . round -3 . should_equal -3000 - -2999 . round -3 . should_equal -3000 - -3000 . round -3 . should_equal -3000 - -3001 . round -3 . should_equal -3000 - -3098 . round -3 . should_equal -3000 - -3101 . round -3 . should_equal -3000 - - Test.specify "Can round negative integers to a specified number of negative places with banker's rounding correctly" <| - 12300 . round -2 use_bankers=True . should_equal 12300 - 12301 . round -2 use_bankers=True . should_equal 12300 - 12330 . round -2 use_bankers=True . should_equal 12300 - 12349 . round -2 use_bankers=True . should_equal 12300 - 12350 . round -2 use_bankers=True . should_equal 12400 - 12351 . round -2 use_bankers=True . should_equal 12400 - 12370 . round -2 use_bankers=True . should_equal 12400 - 12430 . round -2 use_bankers=True . should_equal 12400 - 12470 . round -2 use_bankers=True . should_equal 12500 - - 12249 . round -2 use_bankers=True . should_equal 12200 - 12250 . round -2 use_bankers=True . should_equal 12200 - 12251 . round -2 use_bankers=True . should_equal 12300 - - -12300 . round -2 use_bankers=True . should_equal -12300 - -12301 . round -2 use_bankers=True . should_equal -12300 - -12330 . round -2 use_bankers=True . should_equal -12300 - -12349 . round -2 use_bankers=True . should_equal -12300 - -12350 . round -2 use_bankers=True . should_equal -12400 - -12351 . round -2 use_bankers=True . should_equal -12400 - -12370 . round -2 use_bankers=True . should_equal -12400 - -12430 . round -2 use_bankers=True . should_equal -12400 - -12470 . round -2 use_bankers=True . should_equal -12500 - - -12249 . round -2 use_bankers=True . should_equal -12200 - -12250 . round -2 use_bankers=True . should_equal -12200 - -12251 . round -2 use_bankers=True . should_equal -12300 - + Test.group "rounding" <| Test.specify "Handles incorrect argument types" <| Test.expect_panic_with (123 . round "two") Type_Error Test.expect_panic_with (123 . round use_bankers="no") Type_Error From e05c0acc687b5ecfa9c1a287a8a4af4a3abb3db0 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 12:48:14 -0400 Subject: [PATCH 34/36] type_error tests --- .../lib/Standard/Database/0.0.0-dev/src/Data/Column.enso | 4 ++-- .../lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso | 5 +++++ test/Tests/src/Data/Numbers_Spec.enso | 5 ----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso index 41607adea4ab..4d1df64bb15b 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Column.enso @@ -626,7 +626,7 @@ type Column example_round = Examples.decimal_column.round 2 round : Integer -> Boolean -> Column | Illegal_Argument | Invalid_Value_Type - round self decimal_places=0 use_bankers=False = + round self (decimal_places:Integer = 0) (use_bankers:Boolean = False) = Rounding_Helpers.check_decimal_places decimal_places <| Value_Type.expect_numeric self <| if self.should_use_builtin_round decimal_places use_bankers then self.round_builtin decimal_places else self.short_circuit_special_floating_point <| @@ -650,7 +650,7 @@ type Column not_bankers = use_bankers.not # Don't use for negative decimal places, if the backend doesn't support it negative_dp_ok = decimal_places >= 0 || self.connection.dialect.supports_negative_round_decimal_places - # Don't use for floating-point inputs if deciaml_places != 0, for Postgres + # Don't use for floating-point inputs if decimal_places != 0, for Postgres dp_param_ok = (self.connection.dialect.rounding_decimal_places_not_allowed_for_floats && self.value_type.is_floating_point && decimal_places != 0).not not_bankers && negative_dp_ok && dp_param_ok diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso index beeee5bf558d..6b4f484aadb0 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso @@ -284,4 +284,9 @@ spec prefix round_fun = round_fun -12250 -2 use_bankers=True . should_equal -12200 round_fun -12251 -2 use_bankers=True . should_equal -12300 + Test.specify "Handles incorrect argument types" <| + Test.expect_panic_with (round_fun 123 "two") Type_Error + Test.expect_panic_with (round_fun 123 use_bankers="no") Type_Error + Test.expect_panic_with (round_fun 123 use_bankers=0) Type_Error + main = Test_Suite.run_main (spec "Number " .round) diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index c0aa1a9556cb..1f0611a0a807 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -478,11 +478,6 @@ spec = Number.nan . equals 0 . should_fail_with Incomparable_Values Test.group "rounding" <| - Test.specify "Handles incorrect argument types" <| - Test.expect_panic_with (123 . round "two") Type_Error - Test.expect_panic_with (123 . round use_bankers="no") Type_Error - Test.expect_panic_with (123 . round use_bankers=0) Type_Error - Test.specify "Returns the correct type" <| 231 . round 1 . should_be_a Integer 231 . round 0 . should_be_a Integer From d59f2738cae57dd840fc5c5331861adf3f7ee434 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 12:57:24 -0400 Subject: [PATCH 35/36] unused --- .../lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso | 4 ---- 1 file changed, 4 deletions(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso index 6b4f484aadb0..b6d601e3f7e8 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso @@ -1,11 +1,7 @@ from Standard.Base import all -import Standard.Base.Errors.Common.Arithmetic_Error -import Standard.Base.Errors.Common.Incomparable_Values import Standard.Base.Errors.Common.Type_Error import Standard.Base.Errors.Illegal_Argument.Illegal_Argument -from Standard.Base.Data.Numbers import Number_Parse_Error - from Standard.Test import Test, Test_Suite import Standard.Test.Extensions From 460eeb46dd97a567fc6eef99a0c5b40a1b463cf4 Mon Sep 17 00:00:00 2001 From: Gregory Travis Date: Tue, 15 Aug 2023 12:57:55 -0400 Subject: [PATCH 36/36] cleanup --- .../lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso index b6d601e3f7e8..5424fd5d01e7 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Shared/Round_Spec.enso @@ -8,7 +8,7 @@ import Standard.Test.Extensions polyglot java import java.math.BigInteger spec prefix round_fun = - Test.group prefix+"Rounding numeric testsasdfasdf" <| + Test.group prefix+"Rounding numeric tests" <| Test.specify "Can round positive decimals correctly" <| round_fun 3.0 . should_equal 3 round_fun 3.00001 . should_equal 3