// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Types
///|
priv struct Entry[K] {
mut prev : Int32
mut next : Entry[K]?
mut psl : Int32
hash : Int32
key : K
}
///|
/// Mutable linked hash set that maintains the order of insertion, not thread safe.
///
/// # Example
///
/// ```mbt check
/// test {
/// let set = @set.Set(["three", "eight", "one"])
/// @test.assert_eq(set.contains("two"), false)
/// @test.assert_eq(set.contains("three"), true)
/// set.add("three") // no effect since it already exists
/// set.add("two")
/// @test.assert_eq(set.contains("two"), true)
/// }
/// ```
struct Set[K] {
mut entries : FixedArray[Entry[K]?]
mut size : Int32 // active keys count
mut capacity : Int32 // current capacity
mut capacity_mask : Int32 // capacity_mask = capacity - 1, used to find idx
mut grow_at : Int32 // threshold that triggers grow
mut head : Entry[K]? // head of linked list
mut tail : Int32 // tail of linked list
}
// Implementations
// SAFETY NOTE for the unchecked probe accesses in this file.
//
// `capacity` is always a power of two and at least 1, `capacity_mask` is
// `capacity - 1`, and `entries.length()` is `capacity`. A probe index is
// therefore always in bounds: it starts as `hash & capacity_mask` and each
// step re-masks with `(idx + 1) & capacity_mask`. `grow` installs the new
// array, capacity and mask together before any rehash probe runs.
//
// Only those probe indices use `unsafe_get` / `unsafe_set`. `Set` also
// stores slot indices in the list itself -- `prev` on each entry and `tail`
// on the set -- and `copy` walks the whole list through them, from
// `entries[self.tail]` back along each `entries[prev]`. Every access through
// a stored index keeps the checked form. They are in bounds too, being
// former probe indices in a table that never shrinks, but that argument
// depends on the list being maintained correctly rather than on arithmetic
// alone, so it is not one to build unchecked access on. `shift_back` is
// unchecked, but on a local caller-based proof rather than on that
// invariant: `remove` and `remove_and_check` pass masked probe indices, and
// `retain`, which passes a stored index, performs its own checked read of
// that slot immediately before calling.
///|
let default_init_capacity = 8
///|
fn[K] new_set(capacity : Int32) -> Set[K] {
let capacity = capacity.next_power_of_two()
{
size: 0,
capacity,
capacity_mask: capacity - 1,
grow_at: calc_grow_threshold(capacity),
entries: FixedArray::make(capacity, None),
head: None,
tail: -1,
}
}
///|
fn capacity_for_length(length : Int32) -> Int32 {
let mut capacity = length.next_power_of_two()
if length > calc_grow_threshold(capacity) {
capacity *= 2
}
capacity
}
///|
/// Creates a hash set containing all elements from the given array, preserving insertion order.
/// The optional `capacity` is treated as a minimum initial capacity and will be
/// rounded up to the smallest power of 2 that can hold the requested capacity.
#alias(from_array)
#as_free_fn(from_array)
#alias(of, deprecated="Use from_array instead")
#as_free_fn(of, deprecated="Use from_array instead")
pub fn[K : Hash + Eq] Set::Set(arr : ArrayView[K], capacity? : Int32) -> Set[K] {
let length = arr.length()
let capacity = match capacity {
Some(capacity) => capacity.max(capacity_for_length(length))
None =>
if length == 0 {
default_init_capacity
} else {
capacity_for_length(length)
}
}
let m = new_set(capacity)
// arr.each(e => m.add(e))
// FIXME:(upstream)
// ArrayView::each depends on array package
// adding array package caused `unused package` array
// the root cause is that unused warning is not precise
for e in arr {
m.add(e)
}
m
}
///|
/// Insert a key into the hash set.
///
/// Parameters:
///
/// * `set` : The hash set to modify.
/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set : @set.Set[String] = Set([])
/// set.add("key")
/// inspect(set.contains("key"), content="true")
/// set.add("key") // no effect since it already exists
/// inspect(set.length(), content="1")
/// }
/// ```
#alias(insert, deprecated)
pub fn[K : Hash + Eq] Set::add(self : Set[K], key : K) -> Unit {
self.add_with_hash(key, Hash::hash(key))
}
///|
fn[K : Eq] Set::add_with_hash(self : Set[K], key : K, hash : Int32) -> Unit {
if self.size >= self.grow_at {
self.grow()
}
// SAFETY: masked probe index; see the note at the top of this file.
let (idx, psl) = for psl = 0, idx = hash & self.capacity_mask {
match self.entries.unsafe_get(idx) {
None => break (idx, psl)
Some(curr_entry) => {
if curr_entry.hash == hash && curr_entry.key == key {
return
}
if psl > curr_entry.psl {
self.push_away(idx, curr_entry)
break (idx, psl)
}
continue psl + 1, (idx + 1) & self.capacity_mask
}
}
}
let entry = { prev: self.tail, next: None, psl, key, hash, }
self.add_entry_to_tail(idx, entry)
}
///|
#owned(entry)
fn[K] Set::push_away(self : Set[K], idx : Int32, entry : Entry[K]) -> Unit {
// SAFETY: masked probe index; see the note at the top of this file.
for psl = entry.psl + 1, idx = (idx + 1) & self.capacity_mask, entry = entry {
match self.entries.unsafe_get(idx) {
None => {
entry.psl = psl
self.set_entry(entry, idx)
break
}
Some(curr_entry) =>
if psl > curr_entry.psl {
entry.psl = psl
self.set_entry(entry, idx)
continue curr_entry.psl + 1,
(idx + 1) & self.capacity_mask,
curr_entry
} else {
continue psl + 1, (idx + 1) & self.capacity_mask, entry
}
}
}
}
///|
#owned(entry)
fn[K] Set::set_entry(self : Set[K], entry : Entry[K], new_idx : Int32) -> Unit {
// Fix up the neighbor links before the store: the store consumes the owned
// `entry` reference, and touching `entry` after it would force the compiler
// to keep `entry` alive across the store with an extra incref/decref pair.
match entry.next {
None => self.tail = new_idx
Some(next) => next.prev = new_idx
}
self.entries.unsafe_set(new_idx, Some(entry))
}
///|
/// Insert a key into the hash set and returns whether the key was successfully added.
///
/// Parameters:
///
/// * `set` : The hash set to modify.
/// * `key` : The key to insert. Must implement `Hash` and `Eq` traits.
///
/// Returns `true` if the key was successfully added (i.e., it wasn't already present),
/// `false` if the key already existed in the set.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set : @set.Set[String] = Set([])
/// inspect(set.add_and_check("key"), content="true") // First insertion
/// inspect(set.add_and_check("key"), content="false") // Already exists
/// inspect(set.length(), content="1")
/// }
/// ```
pub fn[K : Hash + Eq] Set::add_and_check(self : Set[K], key : K) -> Bool {
if self.size >= self.grow_at {
self.grow()
}
let hash = Hash::hash(key)
// SAFETY: masked probe index; see the note at the top of this file.
let (idx, psl, added) = for psl = 0, idx = hash & self.capacity_mask {
match self.entries.unsafe_get(idx) {
None => break (idx, psl, true)
Some(curr_entry) => {
if curr_entry.hash == hash && curr_entry.key == key {
break (idx, psl, false)
}
if psl > curr_entry.psl {
self.push_away(idx, curr_entry)
break (idx, psl, true)
}
continue psl + 1, (idx + 1) & self.capacity_mask
}
}
}
if added {
let entry = { prev: self.tail, next: None, psl, key, hash, }
self.add_entry_to_tail(idx, entry)
}
added
}
///|
/// Check if the hash set contains a key.
pub fn[K : Hash + Eq] Set::contains(self : Set[K], key : K) -> Bool {
self.contains_with_hash(key, Hash::hash(key))
}
///|
/// `contains` with a precomputed `hash`. The set operations pass the hash
/// stored in the other set's entry: both sets hash `K` with the same `Hash`
/// impl, so the stored hash is exactly what `contains` would recompute.
#inline
fn[K : Eq] Set::contains_with_hash(
self : Set[K],
key : K,
hash : Int32,
) -> Bool {
// SAFETY: masked probe index; see the note at the top of this file.
for i = 0, idx = hash & self.capacity_mask {
guard self.entries.unsafe_get(idx) is Some(entry) else { break false }
if entry.hash == hash && entry.key == key {
break true
}
if i > entry.psl {
break false
}
continue i + 1, (idx + 1) & self.capacity_mask
}
}
///|
/// Remove a key from the hash set. If the key exists in the set, removes it
/// and adjusts the probe sequence length (PSL) of subsequent entries to
/// maintain the Robin Hood hashing invariant. If the key does not exist,
/// the set remains unchanged.
///
/// Parameters:
///
/// * `self` : The hash set to remove the key from.
/// * `key` : The key to remove from the set.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set = @set.Set(["a", "b"])
/// set.remove("a")
/// inspect(set.contains("a"), content="false")
/// inspect(set.length(), content="1")
/// }
/// ```
pub fn[K : Hash + Eq] Set::remove(self : Set[K], key : K) -> Unit {
let hash = Hash::hash(key)
// SAFETY: masked probe index; see the note at the top of this file.
for i = 0, idx = hash & self.capacity_mask {
guard self.entries.unsafe_get(idx) is Some(entry) else { break }
if entry.hash == hash && entry.key == key {
self.remove_entry(entry)
self.shift_back(idx)
self.size -= 1
break
}
if i > entry.psl {
break
}
continue i + 1, (idx + 1) & self.capacity_mask
}
}
///|
/// Remove a key from the hash set and returns whether the key was successfully removed.
///
/// Parameters:
///
/// * `set` : The hash set to modify.
/// * `key` : The key to remove. Must implement `Hash` and `Eq` traits.
///
/// Returns `true` if the key was successfully removed (i.e., it was present),
/// `false` if the key didn't exist in the set.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set = @set.Set(["a", "b"])
/// inspect(set.remove_and_check("a"), content="true") // Successfully removed
/// inspect(set.remove_and_check("a"), content="false") // Already removed
/// inspect(set.length(), content="1")
/// }
/// ```
pub fn[K : Hash + Eq] Set::remove_and_check(self : Set[K], key : K) -> Bool {
let hash = Hash::hash(key)
// SAFETY: masked probe index; see the note at the top of this file.
for i = 0, idx = hash & self.capacity_mask {
guard self.entries.unsafe_get(idx) is Some(entry) else { break false }
if entry.hash == hash && entry.key == key {
self.remove_entry(entry)
self.shift_back(idx)
self.size -= 1
break true
}
if i > entry.psl {
break false
}
continue i + 1, (idx + 1) & self.capacity_mask
}
}
///|
#owned(entry)
fn[K] Set::add_entry_to_tail(
self : Set[K],
idx : Int32,
entry : Entry[K],
) -> Unit {
match self.tail {
-1 => self.head = Some(entry)
tail => self.entries[tail].unwrap().next = Some(entry)
}
self.tail = idx
self.entries.unsafe_set(idx, Some(entry))
self.size += 1
}
///|
fn[K] Set::remove_entry(self : Set[K], entry : Entry[K]) -> Unit {
match entry.prev {
-1 => self.head = entry.next
idx => self.entries[idx].unwrap().next = entry.next
}
match entry.next {
None => self.tail = entry.prev
Some(next) => next.prev = entry.prev
}
}
///|
fn[K] Set::shift_back(self : Set[K], idx : Int32) -> Unit {
// SAFETY: the initial `cur` is in bounds by every route its callers take,
// and none of them requires trusting the list invariant: `remove` and
// `remove_and_check` pass a masked probe index, and `retain` performs its
// own checked `entries[idx]` read immediately before calling. `next` is
// re-masked each step and later `cur` values are previous `next` values.
// This is also what makes the `set_entry` call below sound.
for cur = idx {
let next = (cur + 1) & self.capacity_mask
match self.entries.unsafe_get(next) {
None | Some({ psl: 0, .. }) => {
self.entries.unsafe_set(cur, None)
break
}
Some(entry) => {
entry.psl -= 1
self.set_entry(entry, cur)
continue next
}
}
}
}
///|
fn[K] Set::grow(self : Set[K]) -> Unit {
let old_head = self.head
let new_capacity = self.capacity << 1
self.entries = FixedArray::make(new_capacity, None)
self.capacity = new_capacity
self.capacity_mask = new_capacity - 1
self.grow_at = calc_grow_threshold(self.capacity)
self.size = 0
self.head = None
self.tail = -1
for x = old_head {
match x {
None => break
Some(e) => {
let next_in_chain = e.next
e.next = None
self.rehash_place_entry(e)
continue next_in_chain
}
}
}
}
///|
#owned(outer)
fn[K] Set::rehash_place_entry(self : Set[K], outer : Entry[K]) -> Unit {
let hash = outer.hash
// SAFETY: masked probe index; see the note at the top of this file.
for psl = 0, idx = hash & self.capacity_mask {
match self.entries.unsafe_get(idx) {
None => {
outer.psl = psl
outer.prev = self.tail
self.add_entry_to_tail(idx, outer)
return
}
Some(curr) =>
if psl > curr.psl {
self.push_away(idx, curr)
outer.psl = psl
outer.prev = self.tail
self.add_entry_to_tail(idx, outer)
return
} else {
continue psl + 1, (idx + 1) & self.capacity_mask
}
}
}
}
// Utils
///|
/// Get the number of keys in the set.
#alias(size, deprecated)
pub fn[K] Set::length(self : Set[K]) -> Int32 {
self.size
}
///|
/// Get the capacity of the set.
pub fn[K] Set::capacity(self : Set[K]) -> Int32 {
self.capacity
}
///|
/// Check if the hash set is empty.
pub fn[K] Set::is_empty(self : Set[K]) -> Bool {
self.size == 0
}
///|
/// Iterate over all keys of the set in the order of insertion.
#locals(f)
pub fn[K] Set::each(self : Set[K], f : (K) -> Unit raise?) -> Unit raise? {
for x = self.head {
match x {
Some({ key, next, .. }) => {
f(key)
continue next
}
None => break
}
}
}
///|
/// Iterate over all keys of the set in the order of insertion, with index.
#locals(f)
pub fn[K] Set::eachi(
self : Set[K],
f : (Int32, K) -> Unit raise?,
) -> Unit raise? {
for i = 0, curr = self.head {
match (i, curr) {
(i, Some({ key, next, .. })) => {
f(i, key)
continue i + 1, next
}
(_, None) => break
}
}
}
///|
/// Clears the set, removing all keys. Keeps the allocated space.
pub fn[K] Set::clear(self : Set[K]) -> Unit {
self.entries.fill(None)
self.size = 0
self.head = None
self.tail = -1
}
///|
/// Returns the iterator of the hash set, provide elements in the order of insertion.
#alias(iterator, deprecated)
pub fn[K] Set::iter(self : Set[K]) -> Iter[K] {
let mut curr_entry = self.head
let len = self.size
let mut remaining = len
Iter::new(
fn() {
guard remaining > 0 && curr_entry is Some({ key, next, .. }) else { None }
curr_entry = next
remaining -= 1
Some(key)
},
size_hint=len,
)
}
///|
/// Converts the hash set to an array.
pub fn[K] Set::to_array(self : Set[K]) -> Array[K] {
let arr = Array(capacity=self.size)
for x = self.head {
match x {
Some({ key, next, .. }) => {
arr.push(key)
continue next
}
None => break
}
}
arr
}
///|
pub impl[K : Eq] Eq for Set[K] with fn equal(self, other) {
self.size == other.size && self.all_in(other)
}
///|
/// Create from `iter`.
#as_free_fn
#alias(from_iterator, deprecated)
#as_free_fn(from_iterator, deprecated)
pub fn[K : Hash + Eq] Set::from_iter(iter : Iter[K]) -> Set[K] {
let m = new_set(default_init_capacity)
while iter.next() is Some(e) {
m.add(e)
}
m
}
///|
pub impl[K] Default for Set[K] with fn default() {
new_set(default_init_capacity)
}
///|
/// Copy the set, creating a new set with the same keys and order of insertion.
#alias(clone, deprecated)
pub fn[K] Set::copy(self : Set[K]) -> Set[K] {
// copy structure
let other = {
capacity: self.capacity,
entries: FixedArray::make(self.capacity, None),
size: self.size,
capacity_mask: self.capacity_mask,
grow_at: self.grow_at,
head: None,
tail: self.tail,
}
if self.size == 0 {
return other
}
guard! self.entries[self.tail] is Some(last)
for entry = last, idx = self.tail, next = (None : Entry[K]?) {
match (entry, idx, next) {
({ prev, psl, hash, key, .. }, idx, next) => {
let new_entry = { prev, next, psl, hash, key, }
other.entries[idx] = Some(new_entry)
if prev != -1 {
continue self.entries[prev].unwrap(), prev, Some(new_entry)
} else {
other.head = Some(new_entry)
break
}
}
}
}
other
}
///|
/// Keeps only the elements for which `f` returns `true`, removing the rest in
/// place. The order of the remaining elements is preserved.
///
/// `true` means *keep*, as with `Set::filter`, which returns a new set and
/// leaves `self` unchanged; use `retain` when the original is no longer
/// needed. `f` must not modify the set.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set = @set.Set([1, 2, 3, 4])
/// set.retain(x => x % 2 == 0)
/// debug_inspect(set.to_array(), content="[2, 4]")
/// }
/// ```
#locals(f)
pub fn[K] Set::retain(self : Set[K], f : (K) -> Bool) -> Unit {
// An entry does not store its own slot, only its predecessor's, so a
// rejected entry is removed one step later, through its successor's `prev`
// (or `tail`, at the end). Removing it cannot disturb the successor: `next`
// links are references, not slots.
for x = self.head, remove_prev = false {
match x {
Some({ key, next, prev: idx, .. }) => {
if remove_prev {
guard! self.entries[idx] is Some(entry)
self.remove_entry(entry)
self.shift_back(idx)
self.size -= 1
}
continue next, !f(key)
}
None => {
if remove_prev {
let idx = self.tail
guard! self.entries[idx] is Some(entry)
self.remove_entry(entry)
self.shift_back(idx)
self.size -= 1
}
break
}
}
}
}
///|
/// Returns a new set holding the elements for which `f` returns `true`, in
/// their original insertion order. `self` is unchanged.
///
/// `true` means *keep*, as with `Array::filter`. To filter in place instead,
/// use `Set::retain`.
///
/// The result is sized for the elements kept, like `Set([...])`. It is faster
/// than rebuilding from a comprehension such as `Set([for k in s if f(k) => k])`
/// because each element's stored hash is reused: elements are never hashed
/// again.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set = @set.Set([1, 2, 3, 4])
/// let even = set.filter(x => x % 2 == 0)
/// debug_inspect(even.to_array(), content="[2, 4]")
/// inspect(set.length(), content="4")
/// }
/// ```
pub fn[K] Set::filter(self : Set[K], f : (K) -> Bool raise?) -> Set[K] raise? {
// Collect first so the result can be sized for exactly the kept elements:
// a stored hash fits a table of any size.
let kept : Array[Entry[K]] = []
for x = self.head {
match x {
Some({ key, hash, next, .. }) => {
if f(key) {
kept.push({ prev: -1, next: None, psl: 0, key, hash, })
}
continue next
}
None => break
}
}
let other = new_set(
if kept.is_empty() {
default_init_capacity
} else {
capacity_for_length(kept.length())
},
)
for entry in kept {
other.rehash_place_entry(entry)
}
other
}
///|
/// Returns a new set containing elements in `self` that are not in `other`.
pub fn[K : Eq] Set::difference(self : Set[K], other : Set[K]) -> Set[K] {
let m = new_set(default_init_capacity)
m.add_missing_from(self, other)
m
}
///|
/// Appends, in `from`'s order, every element of `from` that is not in
/// `other`, reusing the hashes stored in `from`'s entries.
fn[K : Eq] Set::add_missing_from(
self : Set[K],
from : Set[K],
other : Set[K],
) -> Unit {
for x = from.head {
match x {
Some({ key, hash, next, .. }) => {
if !other.contains_with_hash(key, hash) {
self.add_with_hash(key, hash)
}
continue next
}
None => break
}
}
}
///|
/// Returns a new set containing elements in exactly one of the two sets.
pub fn[K : Eq] Set::symmetric_difference(
self : Set[K],
other : Set[K],
) -> Set[K] {
let m = new_set(default_init_capacity)
m.add_missing_from(self, other)
m.add_missing_from(other, self)
m
}
///|
/// Returns a new set containing all elements from both sets.
pub fn[K : Eq] Set::union(self : Set[K], other : Set[K]) -> Set[K] {
// `self`'s elements in order, then `other`'s new ones in order.
let m = self.copy()
m.union_in_place(other)
m
}
///|
/// Adds every element of `other` to `self`, in place: the mutating
/// counterpart of `union`, as `Map::merge_in_place` is of `Map::merge`.
/// Elements already in `self` keep their position; new ones are appended in
/// `other`'s order. Elements are not hashed again, so only `Eq` is needed.
///
/// Example:
///
/// ```mbt check
/// test {
/// let set = @set.Set([3, 1])
/// set.union_in_place(Set([2, 3, 4]))
/// debug_inspect(set.to_array(), content="[3, 1, 2, 4]")
/// }
/// ```
pub fn[K : Eq] Set::union_in_place(self : Set[K], other : Set[K]) -> Unit {
if physical_equal(self, other) {
return
}
for x = other.head {
match x {
Some({ key, hash, next, .. }) => {
self.add_with_hash(key, hash)
continue next
}
None => break
}
}
}
///|
/// Returns a new set containing only elements present in both sets.
pub fn[K : Eq] Set::intersection(self : Set[K], other : Set[K]) -> Set[K] {
let m = new_set(default_init_capacity)
for x = self.head {
match x {
Some({ key, hash, next, .. }) => {
if other.contains_with_hash(key, hash) {
m.add_with_hash(key, hash)
}
continue next
}
None => break
}
}
m
}
///|
/// Returns `true` if every element of `self` is in `other`, probing with
/// the hashes stored in `self`'s entries.
fn[K : Eq] Set::all_in(self : Set[K], other : Set[K]) -> Bool {
for x = self.head {
match x {
Some({ key, hash, next, .. }) => {
guard other.contains_with_hash(key, hash) else { break false }
continue next
}
None => break true
}
}
}
///|
/// Returns `true` if no element of `self` is in `other`, probing with the
/// hashes stored in `self`'s entries.
fn[K : Eq] Set::none_in(self : Set[K], other : Set[K]) -> Bool {
for x = self.head {
match x {
Some({ key, hash, next, .. }) => {
guard !other.contains_with_hash(key, hash) else { break false }
continue next
}
None => break true
}
}
}
///|
pub impl[X : @json.ToJson] @json.ToJson for Set[X] with fn to_json(self) {
let res = Array(capacity=self.size)
for v in self {
res.push(v.to_json())
}
Json::array(res)
}
///|
/// Decodes a set from a JSON array, the form `to_json` produces. Elements
/// keep their array order; a repeated element is kept once, as in `Set([...])`.
///
/// # Example
///
/// ```mbt check
/// test {
/// let set : @set.Set[Int32] = @json.from_json([3, 1, 3, 2])
/// debug_inspect(set.to_array(), content="[3, 1, 2]")
/// }
/// ```
pub impl[X : @json.FromJson + Hash + Eq] @json.FromJson for Set[X] with fn from_json(
json,
path,
) {
guard json is Array(arr) else {
raise JsonDecodeError((path, "@set.from_json: expected array"))
}
// Sized the way `Set([...])` sizes itself for this many elements.
let res : Set[X] = new_set(
if arr.is_empty() {
default_init_capacity
} else {
capacity_for_length(arr.length())
},
)
for i, x in arr {
res.add(@json.FromJson::from_json(x, path.add_index(i)))
}
res
}
///|
/// Returns `true` if the two sets have no elements in common.
pub fn[K : Eq] Set::is_disjoint(self : Set[K], other : Set[K]) -> Bool {
if self.length() <= other.length() {
self.none_in(other)
} else {
other.none_in(self)
}
}
///|
/// Returns `true` if every element of `self` is also in `other`.
pub fn[K : Eq] Set::is_subset(self : Set[K], other : Set[K]) -> Bool {
self.length() <= other.length() && self.all_in(other)
}
///|
/// Returns `true` if every element of `other` is also in `self`.
pub fn[K : Eq] Set::is_superset(self : Set[K], other : Set[K]) -> Bool {
other.is_subset(self)
}
///|
/// Intersection of two hash sets.
pub impl[K : Eq] BitAnd for Set[K] with fn land(self, other) {
self.intersection(other)
}
///|
/// Union of two hash sets.
pub impl[K : Eq] BitOr for Set[K] with fn lor(self, other) {
self.union(other)
}
///|
/// Symmetric difference of two hash sets.
pub impl[K : Eq] BitXOr for Set[K] with fn lxor(self, other) {
self.symmetric_difference(other)
}
///|
/// Difference of two hash sets.
pub impl[K : Eq] Sub for Set[K] with fn sub(self, other) {
self.difference(other)
}
///|