defmodule InPlace.BitSetTest do use ExUnit.Case alias InPlace.BitSet test "basic operations" do lb = -100 ub = 100 bitset = BitSet.new(lb, ub) assert BitSet.size(bitset) == 0 assert catch_throw(BitSet.put(bitset, ub + 1)) == :value_out_of_bounds assert catch_throw(BitSet.put(bitset, lb - 1)) == :value_out_of_bounds rand_val = Enum.random(lb..ub) BitSet.put(bitset, rand_val) assert BitSet.size(bitset) == 1 assert BitSet.member?(bitset, rand_val) BitSet.delete(bitset, rand_val) assert BitSet.size(bitset) == 0 refute BitSet.member?(bitset, rand_val) end test "min and max" do lb = -100 ub = 100 bitset = BitSet.new(lb, ub) refute BitSet.min(bitset) || BitSet.max(bitset) [val1, val2, val3] = Enum.take_random(lb..ub, 3) |> Enum.sort() BitSet.put(bitset, val1) assert BitSet.min(bitset) == val1 && BitSet.max(bitset) == val1 BitSet.put(bitset, val2) assert BitSet.min(bitset) == val1 && BitSet.max(bitset) == val2 BitSet.delete(bitset, val1) BitSet.put(bitset, val3) assert BitSet.min(bitset) == val2 && BitSet.max(bitset) == val3 BitSet.delete(bitset, val2) BitSet.delete(bitset, val3) refute BitSet.min(bitset) || BitSet.max(bitset) end test "traversal" do lb = -100 ub = 100 bitset = BitSet.new(lb, ub) rand_values = Enum.take_random(lb..ub, Enum.random(2..(ub - lb))) Enum.each(rand_values, fn val -> BitSet.put(bitset, val) end) sorted_rand_values = Enum.sort(rand_values) assert Enum.all?(0..length(rand_values) - 2, fn idx -> BitSet.next(bitset, Enum.at(sorted_rand_values, idx)) == Enum.at(sorted_rand_values, idx + 1) end) end test "iteration" do lb = -100 ub = 100 bitset = BitSet.new(lb, ub) iterator_fun = fn value, acc -> [value | acc] end assert Enum.empty?(BitSet.iterate(bitset, [], iterator_fun)) rand_values = Enum.take_random(lb..ub, Enum.random(2..ub - lb)) Enum.each(rand_values, fn val -> BitSet.put(bitset, val) end) assert Enum.sort(rand_values, :desc) == BitSet.iterate(bitset, [], iterator_fun) end test "subset?" do set1 = BitSet.new(-10, 10) set2 = BitSet.new(-10, 10) assert BitSet.subset?(set1, set2) BitSet.put(set1, Enum.random(-10..10)) refute BitSet.subset?(set1, set2) assert BitSet.subset?(set2, set1) end test "equal?" do set1 = BitSet.new(1, 10) set2 = BitSet.new(-10, 10) assert BitSet.equal?(set1, set2) Enum.each(1..10, fn val -> BitSet.put(set1, val) BitSet.put(set2, val) end) assert BitSet.equal?(set1, set2) s = Enum.random([set1, set2]) BitSet.delete(s, Enum.random(1..10)) refute BitSet.equal?(set1, set2) end test "disjoint?" do set1 = BitSet.new(-10, 10) set2 = BitSet.new(-10, 10) assert BitSet.disjoint?(set1, set2) {even_values, odd_values} = Enum.split_with(-10..10, fn x -> rem(x, 2) == 0 end) Enum.each(even_values, fn val -> BitSet.put(set1, val) end) Enum.each(odd_values, fn val -> BitSet.put(set2, val) end) assert BitSet.disjoint?(set1, set2) BitSet.put(set1, Enum.random(odd_values)) refute BitSet.disjoint?(set1, set2) end test "intersection" do lb = -1000 ub = 1000 set1 = BitSet.new(lb, ub) set2 = BitSet.new(lb, ub) intersection = BitSet.intersection(set1, set2) assert BitSet.empty?(intersection) {even_values, odd_values} = Enum.split_with(lb..ub, fn x -> rem(x, 2) == 0 end) Enum.each(even_values, fn val -> BitSet.put(set1, val) end) Enum.each(odd_values, fn val -> BitSet.put(set2, val) end) intersection = BitSet.intersection(set1, set2) assert BitSet.empty?(intersection) ## Add some odd values to "even values" set, ## and some even values to "odd values" set. random_evens = Enum.take_random(even_values, 500) random_odds = Enum.take_random(odd_values, 500) Enum.each(random_odds, fn v -> BitSet.put(set1, v) end) Enum.each(random_evens, fn v -> BitSet.put(set2, v) end) intersection = BitSet.intersection(set1, set2) assert BitSet.to_list(intersection) == (random_evens ++ random_odds) |> Enum.sort() end test "union" do lb = -1000 ub = 1000 set1 = BitSet.new(lb, ub) set2 = BitSet.new(lb, ub) union = BitSet.union(set1, set2) assert BitSet.empty?(union) {even_values, odd_values} = Enum.split_with(lb..ub, fn x -> rem(x, 2) == 0 end) Enum.each(even_values, fn val -> BitSet.put(set1, val) end) Enum.each(odd_values, fn val -> BitSet.put(set2, val) end) union = BitSet.union(set1, set2) assert BitSet.to_list(union) == Enum.to_list(lb..ub) end test "difference" do set = BitSet.new(-10, 10) Enum.each(-10..10, fn val -> BitSet.put(set, val) end) even_set = BitSet.filter(set, fn x -> rem(x, 2) == 0 end) odd_set = BitSet.filter(set, fn x -> rem(x, 2) in [-1, 1] end) assert BitSet.equal?( BitSet.difference(set, even_set), odd_set) assert BitSet.empty?(BitSet.difference(even_set, set)) assert BitSet.empty?(BitSet.difference(odd_set, set)) end test "symmetric difference" do set1 = BitSet.new(-10, 10) set2 = BitSet.new(-10, 10) symdiff_set = BitSet.symmetric_difference(set1, set2) assert BitSet.empty?(symdiff_set) {even_values, odd_values} = Enum.split_with(-10..10, fn x -> rem(x, 2) == 0 end) Enum.each(even_values, fn val -> BitSet.put(set1, val) end) Enum.each(odd_values, fn val -> BitSet.put(set2, val) end) symdiff_set = BitSet.symmetric_difference(set1, set2) assert BitSet.to_list(symdiff_set) == Enum.to_list(-10..10) ## Example from `h MapSet.symmetric_difference s1 = BitSet.new(1, 3) s2 = BitSet.new(2, 4) Enum.each([1, 2, 3], fn val -> BitSet.put(s1, val) end) Enum.each([2, 3, 4], fn val -> BitSet.put(s2, val) end) assert BitSet.symmetric_difference(s1, s2) |> BitSet.to_list() == [1, 4] end test "filter" do set = BitSet.new(-10, 10) Enum.each(-10..10, fn val -> BitSet.put(set, val) end) {even_values, odd_values} = Enum.split_with(-10..10, fn x -> rem(x, 2) == 0 end) even_set = BitSet.filter(set, fn x -> rem(x, 2) == 0 end) odd_set = BitSet.filter(set, fn x -> rem(x, 2) in [-1, 1] end) assert BitSet.to_list(even_set) == even_values assert BitSet.to_list(odd_set) == odd_values end end