diff --git a/dora/stdlib/collections.dora b/dora/stdlib/collections.dora index 3442341d..4281874c 100644 --- a/dora/stdlib/collections.dora +++ b/dora/stdlib/collections.dora @@ -520,7 +520,7 @@ impl[T] Queue[T] { } } -@pub class HashMap[K: Hash + Equals, V] { +@pub class HashMap[K: Hash + Identity + Equals, V] { inserted_and_deleted: BitSet, keys: Array[K], values: Array[V], @@ -528,7 +528,7 @@ impl[T] Queue[T] { cap: Int64, } -impl[K: Hash + Equals, V] HashMap[K, V] { +impl[K: Hash + Identity + Equals, V] HashMap[K, V] { @pub @static fun new(entries: (K, V)...): HashMap[K, V] { // BitSet.size == capacity * 2 // [bit 0: inserted; bit 1: deleted] * capacity @@ -561,7 +561,7 @@ impl[K: Hash + Equals, V] HashMap[K, V] { ... .isLive(idx) { let current_key = self.keys.get(idx); - if current_key.hash() == hash && current_key.equals(key) { + if current_key.hash() == hash && (current_key.identicalTo(key) || current_key.equals(key)) { let old_value = self.values.get(idx); self.values.set(idx, value); return Some[V](old_value); @@ -607,7 +607,7 @@ impl[K: Hash + Equals, V] HashMap[K, V] { ... .isLive(idx) { let current_key = self.keys.get(idx); - if current_key.hash() == hash && current_key.equals(key) { + if current_key.hash() == hash && (current_key.identicalTo(key) || current_key.equals(key)) { return true; } idx = (idx + 1i64) & (self.cap - 1i64); @@ -639,7 +639,7 @@ impl[K: Hash + Equals, V] HashMap[K, V] { ... .isLive(idx) { let current_key = self.keys.get(idx); - if current_key.hash() == hash && current_key.equals(key) { + if current_key.hash() == hash && (current_key.identicalTo(key) || current_key.equals(key)) { return Option[V]::Some(self.values.get(idx)); } idx = (idx + 1i64) & (self.cap - 1i64); @@ -667,7 +667,7 @@ impl[K: Hash + Equals, V] HashMap[K, V] { ... .isLive(idx) { let current_key = self.keys.get(idx); - if current_key.hash() == hash && current_key.equals(key) { + if current_key.hash() == hash && (current_key.identicalTo(key) || current_key.equals(key)) { let value = self.values.get(idx); self.inserted_and_deleted.insert(2i64 * idx + 1i64); @@ -780,12 +780,12 @@ impl[K: Hash + Equals, V] HashMap[K, V] { @pub fun iterator(): HashMapIterator[K, V] = HashMapIterator[K, V]::new(self); } -@pub class HashMapIterator[K: Hash + Equals, V] { +@pub class HashMapIterator[K: Hash + Identity + Equals, V] { map: HashMap[K, V], idx: Int64, } -impl[K: Hash + Equals, V] HashMapIterator[K, V] { +impl[K: Hash + Identity + Equals, V] HashMapIterator[K, V] { @pub @static fun new(map: HashMap[K, V]): HashMapIterator[K, V] = HashMapIterator[K, V](map, 0); @pub fun next(): Option[(K, V)] { @@ -804,11 +804,11 @@ impl[K: Hash + Equals, V] HashMapIterator[K, V] { } } -@pub class HashSet[K: Hash + Equals] { +@pub class HashSet[K: Hash + Identity + Equals] { map: HashMap[K, ()], } -impl[K: Hash + Equals] HashSet[K] { +impl[K: Hash + Identity + Equals] HashSet[K] { @pub @static fun new(keys: K...): HashSet[K] { let map: HashMap[K, ()] = HashMap[K, ()]::new(); diff --git a/dora/stdlib/stdlib.dora b/dora/stdlib/stdlib.dora index 59a55db9..9549cf7b 100644 --- a/dora/stdlib/stdlib.dora +++ b/dora/stdlib/stdlib.dora @@ -1,5 +1,5 @@ @pub use collections.{Array, BitSet, BitVec, HashMap, HashSet, List, Queue}; -@pub use traits.{Comparable, Default, Equals, Hash, Iterator, Zero}; +@pub use traits.{Comparable, Default, Equals, Hash, Identity, Iterator, Zero}; @pub use primitives.{Unit, Bool, Char, Float32, Float64, Int32, Int64, Option, range, Result}; @pub use rand.Random; @pub use string.{CodepointIterator, String, StringBuffer, Stringable}; diff --git a/tests/generic/generic-hash-map-field.dora b/tests/generic/generic-hash-map-field.dora index 17317a71..5e275a8d 100644 --- a/tests/generic/generic-hash-map-field.dora +++ b/tests/generic/generic-hash-map-field.dora @@ -1,5 +1,9 @@ class MyOwnType +impl std::Identity for MyOwnType { + fun identicalTo(other: MyOwnType): Bool = true; +} + impl std::Equals for MyOwnType { fun equals(other: MyOwnType): Bool = true; } diff --git a/tests/stdlib/hashmap-contains-infinity.dora b/tests/stdlib/hashmap-contains-infinity.dora new file mode 100644 index 00000000..16d7ad2d --- /dev/null +++ b/tests/stdlib/hashmap-contains-infinity.dora @@ -0,0 +1,24 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let map = std::HashMap[Float64, String]::new((Float64::infinityPositive(), "a"), (Float64::infinityNegative(), "b")); + + assert(map.size() == 2); + assert(map.contains(Float64::infinityPositive())); + assert(map.get(Float64::infinityPositive()).getOrPanic() == "a"); + assert(map.contains(Float64::infinityNegative())); + assert(map.get(Float64::infinityNegative()).getOrPanic() == "b"); +} + +fun float32(): Unit { + let map = std::HashMap[Float32, String]::new((Float32::infinityPositive(), "a"), (Float32::infinityNegative(), "b")); + + assert(map.size() == 2); + assert(map.contains(Float32::infinityPositive())); + assert(map.get(Float32::infinityPositive()).getOrPanic() == "a"); + assert(map.contains(Float32::infinityNegative())); + assert(map.get(Float32::infinityNegative()).getOrPanic() == "b"); +} diff --git a/tests/stdlib/hashmap-contains-nan.dora b/tests/stdlib/hashmap-contains-nan.dora new file mode 100644 index 00000000..6bbab636 --- /dev/null +++ b/tests/stdlib/hashmap-contains-nan.dora @@ -0,0 +1,20 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let map = std::HashMap[Float64, String]::new((Float64::notANumber(), "a")); + + assert(map.size() == 1); + assert(map.contains(Float64::notANumber())); + assert(map.get(Float64::notANumber()).getOrPanic() == "a"); +} + +fun float32(): Unit { + let map = std::HashMap[Float32, String]::new((Float32::notANumber(), "a")); + + assert(map.size() == 1); + assert(map.contains(Float32::notANumber())); + assert(map.get(Float32::notANumber()).getOrPanic() == "a"); +} diff --git a/tests/stdlib/hashmap-contains-zero.dora b/tests/stdlib/hashmap-contains-zero.dora new file mode 100644 index 00000000..b7ed8724 --- /dev/null +++ b/tests/stdlib/hashmap-contains-zero.dora @@ -0,0 +1,24 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let map = std::HashMap[Float64, String]::new((0.0, "a")); + + assert(map.size() == 1); + assert(map.contains(0.0)); + assert(map.get(0.0).getOrPanic() == "a"); + assert(map.contains(-0.0)); // FIXME: this only works by coincidence, because the hash function sucks + assert(map.get(-0.0).getOrPanic() == "a"); // FIXME: this only works by coincidence, because the hash function sucks +} + +fun float32(): Unit { + let map = std::HashMap[Float32, String]::new((0.0f32, "a")); + + assert(map.size() == 1); + assert(map.contains(0.0f32)); + assert(map.get(0.0f32).getOrPanic() == "a"); + assert(map.contains(-0.0f32).not()); // FIXME: should be true instead + // assert(map.get(-0.0f32).getOrPanic() == "a"); // FIXME: should be true instead +} diff --git a/tests/stdlib/hashmap-contains.dora b/tests/stdlib/hashmap-contains.dora new file mode 100644 index 00000000..db7e5acc --- /dev/null +++ b/tests/stdlib/hashmap-contains.dora @@ -0,0 +1,16 @@ +fun main(): Unit { + let map = std::HashMap[Int32, String]::new((1i32, "a"), (2i32, "b"), (3i32, "c"), (4i32, "d")); + + assert(map.size() == 4); + + assert(map.contains(1i32)); + assert(map.contains(2i32)); + assert(map.contains(3i32)); + assert(map.contains(4i32)); + assert(map.contains(0i32).not()); + + assert(map.get(1i32).getOrPanic() == "a"); + assert(map.get(2i32).getOrPanic() == "b"); + assert(map.get(3i32).getOrPanic() == "c"); + assert(map.get(4i32).getOrPanic() == "d"); +} diff --git a/tests/stdlib/hashmap1.dora b/tests/stdlib/hashmap-iterator.dora similarity index 100% rename from tests/stdlib/hashmap1.dora rename to tests/stdlib/hashmap-iterator.dora diff --git a/tests/stdlib/hashmap3.dora b/tests/stdlib/hashmap3.dora deleted file mode 100644 index 3251403c..00000000 --- a/tests/stdlib/hashmap3.dora +++ /dev/null @@ -1,15 +0,0 @@ -fun main(): Unit { - let map = std::HashMap[Int32, String]::new((1i32, "a"), (2i32, "b"), (3i32, "c"), (4i32, "d")); - assert(map.size() == 4i64); - - assert(map.contains(1i32)); - assert(map.contains(2i32)); - assert(map.contains(3i32)); - assert(map.contains(4i32)); - assert(map.contains(0i32).not()); - - assert(map.get(1i32).getOrPanic() == "a"); - assert(map.get(2i32).getOrPanic() == "b"); - assert(map.get(3i32).getOrPanic() == "c"); - assert(map.get(4i32).getOrPanic() == "d"); -} diff --git a/tests/stdlib/hashset-contains-infinity.dora b/tests/stdlib/hashset-contains-infinity.dora new file mode 100644 index 00000000..bebce3fd --- /dev/null +++ b/tests/stdlib/hashset-contains-infinity.dora @@ -0,0 +1,20 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let set = std::HashSet[Float64]::new(Float64::infinityPositive(), Float64::infinityNegative()); + + assert(set.size() == 2); + assert(set.contains(Float64::infinityPositive())); + assert(set.contains(Float64::infinityNegative())); +} + +fun float32(): Unit { + let set = std::HashSet[Float32]::new(Float32::infinityPositive(), Float32::infinityNegative()); + + assert(set.size() == 2); + assert(set.contains(Float32::infinityPositive())); + assert(set.contains(Float32::infinityNegative())); +} diff --git a/tests/stdlib/hashset-contains-nan.dora b/tests/stdlib/hashset-contains-nan.dora new file mode 100644 index 00000000..3613509f --- /dev/null +++ b/tests/stdlib/hashset-contains-nan.dora @@ -0,0 +1,18 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let set = std::HashSet[Float64]::new(Float64::notANumber()); + + assert(set.size() == 1); + assert(set.contains(Float64::notANumber())); +} + +fun float32(): Unit { + let set = std::HashSet[Float32]::new(Float32::notANumber()); + + assert(set.size() == 1); + assert(set.contains(Float32::notANumber())); +} diff --git a/tests/stdlib/hashset-contains-zero.dora b/tests/stdlib/hashset-contains-zero.dora new file mode 100644 index 00000000..581ef099 --- /dev/null +++ b/tests/stdlib/hashset-contains-zero.dora @@ -0,0 +1,20 @@ +fun main(): Unit { + float64(); + float32(); +} + +fun float64(): Unit { + let set = std::HashSet[Float64]::new(0.0); + + assert(set.size() == 1); + assert(set.contains(0.0)); + assert(set.contains(-0.0)); // FIXME: this only works by coincidence, because the hash function sucks +} + +fun float32(): Unit { + let set = std::HashSet[Float32]::new(0.0f32); + + assert(set.size() == 1); + assert(set.contains(0.0f32)); + assert(set.contains(-0.0f32).not()); // FIXME: should be true instead +} diff --git a/tests/stdlib/hashset-contains.dora b/tests/stdlib/hashset-contains.dora new file mode 100644 index 00000000..c2ab4568 --- /dev/null +++ b/tests/stdlib/hashset-contains.dora @@ -0,0 +1,9 @@ +fun main(): Unit { + let set = std::HashSet[Int32]::new(1i32, 10'000i32, 7i32); + + assert(set.size() == 3); + assert(set.contains(1i32)); + assert(set.contains(10'000i32)); + assert(set.contains(7i32)); + assert(set.contains(0i32).not()); +} diff --git a/tests/stdlib/hashset3.dora b/tests/stdlib/hashset3.dora deleted file mode 100644 index 69496a13..00000000 --- a/tests/stdlib/hashset3.dora +++ /dev/null @@ -1,8 +0,0 @@ -fun main(): Unit { - let set = std::HashSet[Int32]::new(1i32, 10'000i32, 7i32); - assert(set.size() == 3i64); - assert(set.contains(1i32)); - assert(set.contains(10'000i32)); - assert(set.contains(7i32)); - assert(set.contains(0i32).not()); -}