Fix date comparisons and test sorting of tables and vectors with dates (#3745)

Implements https://www.pivotaltracker.com/story/show/183402892

# Important Notes
- Fixes inconsistent `compare_to` vs `==` behaviour in date/time types and adds test for that.
- Adds test for `Table.order_by` on dates and custom types.
- Fixes an issue with `Table.order_by` for custom types.
- Unifies how incomparable objects are reported by `Table.order_by` and `Vector.sort`.
- Adds benchmarks comparing `Table.order_by` and `Vector.sort` performance.
This commit is contained in:
Radosław Waśko 2022-09-29 10:48:00 +02:00 committed by GitHub
parent 835ac05218
commit 61a4120cfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 201 additions and 86 deletions

View File

@ -1,4 +1,4 @@
from Standard.Base import Boolean, True, False, Nothing, Vector, Number, Any, Error, Array, Panic, Illegal_Argument_Error_Data, Illegal_Argument_Error, Unsupported_Argument_Types, Unsupported_Argument_Types_Data
from Standard.Base import Boolean, True, False, Nothing, Vector, Number, Any, Error, Array, Panic, Illegal_Argument_Error_Data, Illegal_Argument_Error, Unsupported_Argument_Types, Unsupported_Argument_Types_Data, Incomparable_Values_Error
from Standard.Base.Data.Vector import Empty_Error
@ -130,10 +130,10 @@ compute_bulk data statistics=[Count, Sum] =
statistics.map_with_index i->s->case s of
Count -> count_min_max_values.count
Minimum ->
if count_min_max_values.comparatorError then (Error.throw Vector.Incomparable_Values_Error) else
if count_min_max_values.comparatorError then (Error.throw Incomparable_Values_Error) else
count_min_max_values.minimum
Maximum ->
if count_min_max_values.comparatorError then (Error.throw Vector.Incomparable_Values_Error) else
if count_min_max_values.comparatorError then (Error.throw Incomparable_Values_Error) else
count_min_max_values.maximum
Covariance s -> calculate_correlation_statistics data s . covariance
Pearson s -> calculate_correlation_statistics data s . pearsonCorrelation
@ -248,7 +248,7 @@ rank_data input method=Rank_Method.Average =
report_nullpointer caught_panic = Error.throw (Illegal_Argument_Error_Data caught_panic.payload.cause.getMessage)
handle_nullpointer = Panic.catch NullPointerException handler=report_nullpointer
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Vector.Incomparable_Values_Error)
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Incomparable_Values_Error)
handle_classcast <| handle_nullpointer <|
java_ranks = Rank.rank input.to_array Comparator.new java_method

View File

@ -495,18 +495,19 @@ type Date
(Date.new 2000).compare_to (Date.new 2001)
compare_to : Date -> Ordering
compare_to self that =
sign = Time_Utils.compare_to_localdate self that
Ordering.from_sign sign
compare_to self that = case that of
Date ->
sign = Time_Utils.compare_to_localdate self that
Ordering.from_sign sign
_ -> Error.throw (Type_Error_Data Date that "that")
## Compares two Dates for equality.
== : Date -> Boolean
== self that =
case that of
Date ->
sign = Time_Utils.compare_to_localdate self that
0 == sign
_ -> False
== self that = case that of
Date ->
sign = Time_Utils.compare_to_localdate self that
0 == sign
_ -> False
## PRIVATE
week_days_between start end =

View File

@ -557,16 +557,16 @@ type Date_Time
(Date_Time.new 2000).compare_to (Date_Time.new 2001)
compare_to : Time -> Ordering
compare_to self that =
sign = Time_Utils.compare_to_zoneddatetime self that
Ordering.from_sign sign
compare_to self that = case that of
Date_Time ->
sign = Time_Utils.compare_to_zoneddatetime self that
Ordering.from_sign sign
_ -> Error.throw (Type_Error_Data Date_Time that "that")
## Compares two Date_Time for equality.
== : Date_Time -> Boolean
== self that =
case that of
Date_Time ->
sign = Time_Utils.compare_to_zoneddatetime self that
0 == sign
_ ->
False
== self that = case that of
Date_Time ->
sign = Time_Utils.compare_to_zoneddatetime self that
0 == sign
_ -> False

View File

@ -323,16 +323,16 @@ type Time_Of_Day
time_2 = Time_Of_Day.new minute=50
time_1.compare_to time_2
compare_to : Time_Of_Day -> Ordering
compare_to self that =
sign = Time_Utils.compare_to_localtime self that
Ordering.from_sign sign
compare_to self that = case that of
Time_Of_Day ->
sign = Time_Utils.compare_to_localtime self that
Ordering.from_sign sign
_ -> Error.throw (Type_Error_Data Time_Of_Day that "that")
## Compares two Time_Of_Day for equality.
== : Date -> Boolean
== self that =
case that of
Time_Of_Day ->
sign = Time_Utils.compare_to_localtime self that
0 == sign
_ ->
False
== self that = case that of
Time_Of_Day ->
sign = Time_Utils.compare_to_localtime self that
0 == sign
_ -> False

View File

@ -5,6 +5,7 @@ import Standard.Base.Runtime.Unsafe
polyglot java import java.lang.IndexOutOfBoundsException
polyglot java import org.enso.base.Array_Builder
polyglot java import java.lang.ClassCastException
## Creates a new vector of the given length, initializing elements using
the provided constructor function.
@ -858,7 +859,7 @@ type Vector a
Sorting a vector of `Pair`s on the first element, descending.
[Pair 1 2, Pair -1 8].sort (_.first) (order = Sort_Direction.Descending)
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Direction -> Vector Any
sort : (Any -> Any) -> (Any -> Any -> Ordering) -> Sort_Direction -> Vector Any ! Incomparable_Values_Error
sort self (on = x -> x) (by = (_.compare_to _)) (order = Sort_Direction.Ascending) =
## Prepare the destination array that will underlie the vector. We do
not want to sort in place on the original vector, as `sort` is not
@ -874,9 +875,9 @@ type Vector a
compare = if order == Sort_Direction.Ascending then comp_ascending else
comp_descending
new_vec_arr.sort compare
Vector.from_polyglot_array new_vec_arr
handle_incomparable_value <|
new_vec_arr.sort compare
Vector.from_polyglot_array new_vec_arr
## UNSTABLE
Keeps only unique elements within the Vector, removing any duplicates.
@ -1102,18 +1103,16 @@ type Empty_Error
to_display_text : Text
to_display_text self = "The vector is empty."
## UNSTABLE
An error indicating that the vector contains incomparable types.
type Incomparable_Values_Error
## ADVANCED
Catches possible errors from comparing values and throws an
Incomparable_Values_Error if any occur.
handle_incomparable_value ~function =
handle t = Panic.catch t handler=(_-> Error.throw Incomparable_Values_Error)
handle No_Such_Method_Error_Data <| handle Type_Error_Data <| handle Unsupported_Argument_Types_Data <| function
handle ClassCastException <|
handle No_Such_Method_Error_Data <|
handle Type_Error_Data <|
handle Unsupported_Argument_Types_Data <|
function
## PRIVATE
Creates a new vector where for each range, a corresponding section of the

View File

@ -771,3 +771,9 @@ type Unsupported_File_Type
to_display_text : Text
to_display_text self =
"The "+self.filename+" has a type that is not supported."
## UNSTABLE
An error indicating that some elements are incomparable.
type Incomparable_Values_Error

View File

@ -529,7 +529,7 @@ type Table
descending order.
table.order_by (Sort_Column_Selector.By_Index [1, Sort_Column.Index -7 Sort_Direction.Descending])
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table ! Incomparable_Values_Error
order_by self (columns = (Sort_Column_Selector.By_Name [(Sort_Column.Name (self.columns.at 0 . name))])) text_ordering=Text_Ordering.Default on_problems=Report_Warning = Panic.handle_wrapped_dataflow_error <|
problem_builder = Problem_Builder.new
columns_for_ordering = Table_Helpers.prepare_order_by self.columns columns problem_builder

View File

@ -650,7 +650,7 @@ type Table
table = Examples.inventory_table
table.order_by (Sort_Column_Selector.By_Name ["total_stock", Sort_Column.Name "sold_stock" Sort_Direction.Descending])
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table
order_by : Sort_Column_Selector -> Text_Ordering -> Problem_Behavior -> Table ! Incomparable_Values_Error
order_by self (columns = (Sort_Column_Selector.By_Name [(Sort_Column.Name (self.columns.at 0 . name))])) text_ordering=Text_Ordering.Default on_problems=Report_Warning =
problem_builder = Problem_Builder.new
columns_for_ordering = Table_Helpers.prepare_order_by self.columns columns problem_builder
@ -658,7 +658,7 @@ type Table
selected_columns = columns_for_ordering.map c->c.column.java_column
ordering = columns_for_ordering.map c->c.associated_selector.direction.to_sign
comparator = Comparator.for_text_ordering text_ordering
java_table = Illegal_Argument_Error.handle_java_exception <|
java_table = Illegal_Argument_Error.handle_java_exception <| Vector.handle_incomparable_value <|
self.java_table.orderBy selected_columns.to_array ordering.to_array comparator
Table_Data java_table

View File

@ -1,11 +1,14 @@
package org.enso.base;
import org.graalvm.polyglot.Value;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.function.Function;
public class ObjectComparator implements Comparator<Object> {
private static ObjectComparator INSTANCE;
@ -17,7 +20,7 @@ public class ObjectComparator implements Comparator<Object> {
* passed to allow calling back from Java.
* @return Comparator object.
*/
public static ObjectComparator getInstance(BiFunction<Object, Object, Long> fallbackComparator) {
public static ObjectComparator getInstance(Function<Object, Function<Object, Value>> fallbackComparator) {
if (INSTANCE == null) {
INSTANCE = new ObjectComparator(fallbackComparator);
}
@ -25,22 +28,22 @@ public class ObjectComparator implements Comparator<Object> {
return INSTANCE;
}
private final BiFunction<Object, Object, Long> fallbackComparator;
private final BiFunction<String, String, Long> textComparator;
private final Function<Object, Function<Object, Value>> fallbackComparator;
private final Function<String, Function<String, Value>> textComparator;
public ObjectComparator() {
this(
(a, b) -> {
(a) -> (b) -> {
throw new ClassCastException("Incomparable keys.");
});
}
public ObjectComparator(BiFunction<Object, Object, Long> fallbackComparator) {
this(fallbackComparator, (a, b) -> Long.valueOf(Text_Utils.compare_normalized(a, b)));
public ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator) {
this(fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized(a, b)));
}
private ObjectComparator(BiFunction<Object, Object, Long> fallbackComparator, BiFunction<String, String, Long> textComparator) {
private ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator, Function<String, Function<String, Value>> textComparator) {
this.fallbackComparator = fallbackComparator;
this.textComparator = textComparator;
}
@ -51,7 +54,7 @@ public class ObjectComparator implements Comparator<Object> {
* @return Comparator object.
*/
public ObjectComparator withCaseInsensitivity(Locale locale) {
return new ObjectComparator(this.fallbackComparator, (a, b) -> Long.valueOf(Text_Utils.compare_normalized_ignoring_case(a, b, locale)));
return new ObjectComparator(this.fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized_ignoring_case(a, b, locale)));
}
/**
@ -59,7 +62,7 @@ public class ObjectComparator implements Comparator<Object> {
* @param textComparator custom comparator for Text.
* @return Comparator object.
*/
public ObjectComparator withCustomTextComparator(BiFunction<String, String, Long> textComparator) {
public ObjectComparator withCustomTextComparator(Function<String, Function<String, Value>> textComparator) {
return new ObjectComparator(this.fallbackComparator, textComparator);
}
@ -118,7 +121,7 @@ public class ObjectComparator implements Comparator<Object> {
// Text
if (thisValue instanceof String thisString && thatValue instanceof String thatString) {
return textComparator.apply(thisString, thatString).intValue();
return convertComparatorResult(textComparator.apply(thisString).apply(thatString));
}
// DateTimes
@ -126,15 +129,10 @@ public class ObjectComparator implements Comparator<Object> {
if (thatValue instanceof LocalDate thatDate) {
return thisDate.compareTo(thatDate);
}
if (thatValue instanceof LocalDateTime thatDateTime) {
return thisDate.atStartOfDay().compareTo(thatDateTime);
}
}
if (thisValue instanceof LocalDateTime thisDateTime) {
if (thatValue instanceof LocalDate thatDate) {
return thisDateTime.compareTo(thatDate.atStartOfDay());
}
if (thatValue instanceof LocalDateTime thatDateTime) {
if (thisValue instanceof ZonedDateTime thisDateTime) {
if (thatValue instanceof ZonedDateTime thatDateTime) {
return thisDateTime.compareTo(thatDateTime);
}
}
@ -147,6 +145,14 @@ public class ObjectComparator implements Comparator<Object> {
}
// Fallback to Enso
return fallbackComparator.apply(thisValue, thatValue).intValue();
return convertComparatorResult(fallbackComparator.apply(thisValue).apply(thatValue));
}
private static int convertComparatorResult(Value comparatorResult) {
if (comparatorResult.isNumber() && comparatorResult.fitsInInt()) {
return comparatorResult.asInt();
} else {
throw new ClassCastException("Comparator returned a non-integer value: " + comparatorResult);
}
}
}

View File

@ -0,0 +1,40 @@
from Standard.Base import all
from Standard.Base.Data.Index_Sub_Range import Sample
import Standard.Base.Data.Time.Duration
import Standard.Table
from Standard.Table import Sort_Column_Selector
from Standard.Table.Data.Aggregate_Column import all
import Standard.Test.Bench
type My
Data x
compare_to self other =
other.x . compare_to self.x
vector_size = 100000
iter_size = 20
num_iterations = 10
# The Benchmarks ==============================================================
main =
ints = (0.up_to vector_size).to_vector.take (Sample vector_size 42)
start = Date_Time.new 1990 1 1
dates = ints.map x->
start + x.seconds
objects = ints.map My.Data
ints_table = Table.new [['ints', ints]]
dates_table = Table.new [['dates', dates]]
objects_table = Table.new [['objects', objects]]
Bench.measure (ints_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by ints" iter_size num_iterations
Bench.measure (ints.sort) "Vector.sort ints" iter_size num_iterations
Bench.measure (dates_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by dates" iter_size num_iterations
Bench.measure (dates.sort) "Vector.sort dates" iter_size num_iterations
Bench.measure (objects_table.order_by (Sort_Column_Selector.By_Index [0])) "Table.order_by objects" iter_size num_iterations
Bench.measure (objects.sort) "Vector.sort objects" iter_size num_iterations

View File

@ -1,4 +1,5 @@
from Standard.Base import all
import Standard.Base.Runtime.Ref
import Standard.Test.Bench
@ -15,16 +16,16 @@ vector_size = 1000000
iter_size = 100
num_iterations = 10
make_sorted_ascending_vec : Integer -> Base.Vector.Vector
make_sorted_ascending_vec : Integer -> Vector.Vector
make_sorted_ascending_vec n = 0.up_to n+1 . to_vector
make_partially_sorted_vec : Integer -> Base.Vector.Vector
make_partially_sorted_vec : Integer -> Vector.Vector
make_partially_sorted_vec n =
random_gen = Random.new n
direction = Ref.new Sort_Direction.Ascending
last_num = Ref.new 0
run_length = Ref.new 0
Base.Vector.fill n <|
Vector.fill n <|
case (run_length.get) == 0 of
True ->
new_direction = if random_gen.nextDouble > 0 then Sort_Direction.Ascending else

View File

@ -102,18 +102,20 @@ spec =
times = ["times", [Time_Of_Day.new 18 00, Time_Of_Day.new 1 2 34, Nothing, Time_Of_Day.new]]
datetimes = ["datetimes", [Date_Time.new 2000, Date_Time.new 1999 1 2 3 4 5, Nothing, Date_Time.new 2022 8 27 11 22 25]]
mixed = ["mixed", [1, "a", Nothing, Date.new 2022 8 27]]
mixed_dates = ["mixed_dates", [Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40]]
just_nulls = ["just_nulls", [Nothing, Nothing, Nothing, Nothing]]
table = Table.new [strs, ints, doubles, doubles_and_ints, custom_objects, dates, times, datetimes, mixed, just_nulls]
table = Table.new [strs, ints, doubles, doubles_and_ints, custom_objects, dates, times, datetimes, mixed, mixed_dates, just_nulls]
table.at "strs" . storage_type . should_equal Storage.Text
table.at "ints" . storage_type . should_equal Storage.Integer
table.at "doubles" . storage_type . should_equal Storage.Decimal
table.at "doubles_and_ints" . storage_type . should_equal Storage.Decimal
table.at "custom_objects" . storage_type . should_equal Storage.Any
table.at "mixed" . storage_type . should_equal Storage.Any
table.at "dates" . storage_type . should_equal Storage.Date
table.at "times" . storage_type . should_equal Storage.Time_Of_Day
table.at "datetimes" . storage_type . should_equal Storage.Date_Time
table.at "mixed" . storage_type . should_equal Storage.Any
table.at "mixed_dates" . storage_type . should_equal Storage.Any
table.at "just_nulls" . storage_type . should_equal Storage.Any
pending_python_missing = if Polyglot.is_language_installed "python" . not then
@ -478,9 +480,13 @@ spec =
reals = [1.3, 4.6, 3.2, 5.2, 1.6]
bools = [False, False, True, True, False]
texts = ["foo", "foo", "bar", "baz", "spam"]
objs = [Cons 1 2, Cons 2 3, Cons 6 7, Cons 8 9, Cons 10 30]
dates = [Date.new 2020, Date.new 1999, Date.new 2000 10 3, Date.new 1999 12 31, Date.new 2000 2 7]
times = [Time_Of_Day.new 12, Time_Of_Day.new 1 30 40, Time_Of_Day.new 23 59 59, Time_Of_Day.new 12 30 0, Time_Of_Day.new 10 20 30]
datetimes = [Date_Time.new 2020 1 1 12, Date_Time.new 1999 1 1 1 30 40, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 1999 12 31 12 30 0, Date_Time.new 2000 10 3 10 20 30]
objs = [My_Data 100 2, My_Data 2 3, My_Data 6 7, My_Data 8 9, My_Data 10 30]
mixed_dates = [Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 1999 1 2, Date_Time.new 1999 1 2 3 40, Date.new 2000]
df = Table.new [['ord', ord], ['ints', ints], ['reals', reals], ['bools', bools], ['texts', texts], ['objs', objs]]
df = Table.new [['ord', ord], ['ints', ints], ['reals', reals], ['bools', bools], ['texts', texts], ['objs', objs], ['dates', dates], ['times', times], ['datetimes', datetimes], ['mixed_dates', mixed_dates]]
r = df.order_by (Sort_Column_Selector.By_Name ['ord'])
r.at 'ints' . to_vector . should_equal [1, 5, 3, 2, 4]
@ -495,9 +501,38 @@ spec =
r.at 'texts' . to_vector . should_equal ['foo', 'spam', 'bar', 'foo', 'baz']
df.at 'texts' . to_vector . should_equal texts
r.at 'objs' . to_vector . should_equal [Cons 1 2, Cons 10 30, Cons 6 7, Cons 2 3, Cons 8 9]
r.at 'objs' . to_vector . should_equal [My_Data 100 2, My_Data 10 30, My_Data 6 7, My_Data 2 3, My_Data 8 9]
df.at 'objs' . to_vector . should_equal objs
r.at 'dates' . to_vector . should_equal [Date.new 2020, Date.new 2000 2 7, Date.new 2000 10 3, Date.new 1999, Date.new 1999 12 31]
df.at 'dates' . to_vector . should_equal dates
r.at 'times' . to_vector . should_equal [Time_Of_Day.new 12, Time_Of_Day.new 10 20 30, Time_Of_Day.new 23 59 59, Time_Of_Day.new 1 30 40, Time_Of_Day.new 12 30 0]
df.at 'times' . to_vector . should_equal times
r.at 'datetimes' . to_vector . should_equal [Date_Time.new 2020 1 1 12, Date_Time.new 2000 10 3 10 20 30, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 1999 1 1 1 30 40, Date_Time.new 1999 12 31 12 30 0]
df.at 'datetimes' . to_vector . should_equal datetimes
# TODO move this test to Common_Table_Spec once we support dates there
r2 = df.order_by (Sort_Column_Selector.By_Name ['dates'])
r2.at 'dates' . to_vector . should_equal [Date.new 1999, Date.new 1999 12 31, Date.new 2000 2 7, Date.new 2000 10 3, Date.new 2020]
r2.at 'ints' . to_vector . should_equal [2, 4, 5, 3, 1]
r3 = df.order_by (Sort_Column_Selector.By_Name ['times'])
r3.at 'times' . to_vector . should_equal [Time_Of_Day.new 1 30 40, Time_Of_Day.new 10 20 30, Time_Of_Day.new 12, Time_Of_Day.new 12 30 0, Time_Of_Day.new 23 59 59]
r3.at 'ints' . to_vector . should_equal [2, 5, 1, 4, 3]
r4 = df.order_by (Sort_Column_Selector.By_Name ['datetimes'])
r4.at 'datetimes' . to_vector . should_equal [Date_Time.new 1999 1 1 1 30 40, Date_Time.new 1999 12 31 12 30 0, Date_Time.new 2000 10 3 10 20 30, Date_Time.new 2000 10 3 23 59 59, Date_Time.new 2020 1 1 12]
r4.at 'ints' . to_vector . should_equal [2, 4, 5, 3, 1]
r5 = df.order_by (Sort_Column_Selector.By_Name ['objs'])
r5.at 'objs' . to_vector . should_equal [My_Data 2 3, My_Data 6 7, My_Data 8 9, My_Data 10 30, My_Data 100 2]
r5.at 'ints' . to_vector . should_equal [2, 3, 4, 5, 1]
r6 = df.order_by (Sort_Column_Selector.By_Name ['mixed_dates'])
r6 . should_fail_with Incomparable_Values_Error
Test.group "Sorting Columns" <|
Test.specify 'should sort columns with specified ordering and missing placement' <|
c = Column.from_vector 'foo' [1, 7, Nothing, 4, 8, Nothing]

View File

@ -20,7 +20,7 @@ type No_Ord
# Tests
spec = Test.group "Object Comparator" <|
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Vector.Incomparable_Values_Error)
handle_classcast = Panic.catch ClassCastException handler=(_ -> Error.throw Incomparable_Values_Error)
default_comparator a b = handle_classcast <| Comparator.new.compare a b
case_insensitive a b = handle_classcast <| Comparator.for_text_ordering Text_Ordering.Case_Insensitive . compare a b
@ -58,7 +58,7 @@ spec = Test.group "Object Comparator" <|
((default_comparator (Ord.Value 1) (Ord.Value 1)) == 0) . should_equal True
Test.specify "should fail gracefully for incomparable items" <|
(default_comparator 1 True).should_fail_with Vector.Incomparable_Values_Error
(default_comparator (No_Ord.Value 1) (No_Ord.Value 2)).should_fail_with Vector.Incomparable_Values_Error
(default_comparator 1 True).should_fail_with Incomparable_Values_Error
(default_comparator (No_Ord.Value 1) (No_Ord.Value 2)).should_fail_with Incomparable_Values_Error
main = Test.Suite.run_main spec

View File

@ -150,11 +150,11 @@ spec =
Test.specify "should fail with Incomparable_Values_Error on custom type without compare_to" <|
no_ord_set.compute . should_equal 3
no_ord_set.compute Minimum . should_fail_with Vector.Incomparable_Values_Error
no_ord_set.compute Maximum . should_fail_with Vector.Incomparable_Values_Error
no_ord_set.compute Minimum . should_fail_with Incomparable_Values_Error
no_ord_set.compute Maximum . should_fail_with Incomparable_Values_Error
Test.specify "should fail with Incomparable_Values_Error on mixed Vectors" <|
[1, False].compute Minimum . should_fail_with Vector.Incomparable_Values_Error
[1, False].compute Minimum . should_fail_with Incomparable_Values_Error
Test.group "Rank Data" <|
Test.specify "can rank a Decimal data series" <|
@ -179,10 +179,10 @@ spec =
Test.specify "should fail with Incomparable_Values_Error on custom type without compare_to" <|
values = [No_Ord.Value 10, No_Ord.Value 2, No_Ord.Value 9]
rank_data values . should_fail_with Vector.Incomparable_Values_Error
rank_data values . should_fail_with Incomparable_Values_Error
Test.specify "should fail with Incomparable_Values_Error on mixed Vectors" <|
rank_data [1, "A"] . should_fail_with Vector.Incomparable_Values_Error
rank_data [1, "A"] . should_fail_with Incomparable_Values_Error
Test.specify "should fail with Illegal_Argument_Error on Vectors with Nothing" <|
rank_data [1, Nothing, 4] . should_fail_with Illegal_Argument_Error_Data

View File

@ -133,6 +133,12 @@ spec_with name create_new_date parse_date =
date_1>date_2 . should_be_true
date_1<date_2 . should_be_false
datetime = Date_Time.new 2021 1 2 12 40
date_1.compare_to datetime . should_fail_with Type_Error_Data
date_1<datetime . should_fail_with Type_Error_Data
date_1>=datetime . should_fail_with Type_Error_Data
date_1==datetime . should_be_false
Test.specify "should correctly determine the type of date" <|
new_date = create_new_date 2020 6 1
parsed_date = parse_date "2021-01-02"

View File

@ -315,6 +315,15 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time_1>time_2 . should_be_true
time_1<time_2 . should_be_false
tod = Time_Of_Day.new 4 0 10
time_1.compare_to tod . should_fail_with Type_Error_Data
time_1<tod . should_fail_with Type_Error_Data
time_1==tod . should_be_false
date = Date.new 2021 1 1
time_1.compare_to date . should_fail_with Type_Error_Data
time_1==date . should_be_false
Test.specify "should correctly determine the type of datetime" <|
new_datetime = create_new_datetime 2020 6 1 10 0 0
parsed_datetime = parse_datetime "2021-02-01T00:30:12.7102[UTC]"

View File

@ -117,6 +117,8 @@ specWith name create_new_time parse_time =
time_1>time_2 . should_be_true
time_1<time_2 . should_be_false
time_2.compare_to (Date_Time.new 1999 1 1 4 0 10) . should_fail_with Type_Error_Data
Test.specify "should correctly determine the type of timeofday" <|
new_timeofday = create_new_time 15 37 58
parsed_timeofday = parse_time "10:00:00"

View File

@ -387,6 +387,16 @@ spec = Test.group "Vectors" <|
empty_vec.sort . should_equal []
short_vec.sort . should_equal short_expected
["aa", "bb", "ba"].sort . should_equal ["aa", "ba", "bb"]
[Date.new 2000, Date.new 1999 10 11, Date.new 1990].sort . should_equal [Date.new 1990, Date.new 1999 10 11, Date.new 2000]
[Time_Of_Day.new 12, Time_Of_Day.new 10 30].sort . should_equal [Time_Of_Day.new 10 30, Time_Of_Day.new 12]
[Date_Time.new 2000 12 30 12 30, Date_Time.new 2000 12 30 12 00].sort . should_equal [Date_Time.new 2000 12 30 12 00, Date_Time.new 2000 12 30 12 30]
["aa", 2].sort . should_fail_with Incomparable_Values_Error
[2, Date.new 1999].sort . should_fail_with Incomparable_Values_Error
[Date_Time.new 1999 1 1 12 30, Date.new 1999].sort . should_fail_with Incomparable_Values_Error
[Date_Time.new 1999 1 1 12 30, Time_Of_Day.new 12 30].sort . should_fail_with Incomparable_Values_Error
Test.specify "should leave the original vector unchanged" <|
non_empty_vec = [2, 4, 2, 3, 2, 3]
sorted = non_empty_vec.sort
@ -473,9 +483,9 @@ spec = Test.group "Vectors" <|
[].distinct . should_equal []
Test.specify "should throw a clean error for incomparable types" <|
["a", 2].distinct . should_fail_with Vector.Incomparable_Values_Error
[2, "a", Integer, "a", 2].distinct . should_fail_with Vector.Incomparable_Values_Error
[Pair_Data 1 2, Pair_Data 3 4].distinct . should_fail_with Vector.Incomparable_Values_Error
["a", 2].distinct . should_fail_with Incomparable_Values_Error
[2, "a", Integer, "a", 2].distinct . should_fail_with Incomparable_Values_Error
[Pair_Data 1 2, Pair_Data 3 4].distinct . should_fail_with Incomparable_Values_Error
Test.specify "should correctly handle distinct with custom types like Atoms that implement compare_to" <|
[T.Value 1 2, T.Value 3 3, T.Value 1 2].distinct . should_equal [T.Value 1 2, T.Value 3 3]