Files
Odin/tests/internal/test_bit_set_array.odin
T

190 lines
5.8 KiB
Odin

package test_internal
import "core:fmt"
import "core:testing"
// Regression tests for `bit_set` types backed by an array of integers, e.g. `bit_set[E; [4]u64]`.
// The interesting cases are the set relations (`<`, `<=`, `>`, `>=`) with their subset/superset
// semantics, which must agree between the compile-time constant folding and the generated code, and
// must match an ordinary integer-backed bit_set. An array backing also lets a bit_set exceed the
// 128-bit limit of the integer backings.
@(private="file")
Bsa_E :: enum u8 { A, B, C, D, E, F, G, H }
@(private="file")
Bsa_Arr :: bit_set[Bsa_E; [4]u64] // array backed (256 bits)
@(private="file")
Bsa_Int :: bit_set[Bsa_E; u16] // integer backed, for cross-checking
@(private="file")
bsa_arr_from :: proc(m: u8) -> (s: Bsa_Arr) {
for i in 0..<8 {
if (m >> uint(i)) & 1 == 1 {
s += {Bsa_E(i)}
}
}
return
}
@(private="file")
bsa_int_from :: proc(m: u8) -> (s: Bsa_Int) {
for i in 0..<8 {
if (m >> uint(i)) & 1 == 1 {
s += {Bsa_E(i)}
}
}
return
}
@(test)
bit_set_array_membership :: proc(t: ^testing.T) {
s := Bsa_Arr{.A, .C, .E}
testing.expect(t, .A in s, ".A should be in s")
testing.expect(t, .E in s, ".E should be in s")
testing.expect(t, .B not_in s, ".B should not be in s")
testing.expect(t, .H not_in s, ".H should not be in s")
// runtime (non-constant) key
k := Bsa_E.E
testing.expect(t, k in s, "runtime key .E should be in s")
k = .F
testing.expect(t, k not_in s, "runtime key .F should not be in s")
// constant folding of `in`
C :: Bsa_Arr{.A, .C}
#assert(.A in C)
#assert(.B not_in C)
}
@(test)
bit_set_array_algebra :: proc(t: ^testing.T) {
a := Bsa_Arr{.A, .C, .E}
b := Bsa_Arr{.C, .E, .G}
testing.expect_value(t, a | b, Bsa_Arr{.A, .C, .E, .G})
testing.expect_value(t, a & b, Bsa_Arr{.C, .E})
testing.expect_value(t, a &~ b, Bsa_Arr{.A})
testing.expect_value(t, a + b, a | b) // `+` aliases `|`
testing.expect_value(t, a - b, a &~ b) // `-` aliases `&~`
// complement
full := ~Bsa_Arr{}
testing.expect_value(t, card(full), 8)
testing.expect_value(t, ~a, full &~ a)
// assignment operators
s := Bsa_Arr{.A}
s += {.C}
s |= {.E}
testing.expect_value(t, s, Bsa_Arr{.A, .C, .E})
s -= {.A}
s &~= {.C}
testing.expect_value(t, s, Bsa_Arr{.E})
s = Bsa_Arr{.A, .B, .C}
s &= {.B, .C, .D}
testing.expect_value(t, s, Bsa_Arr{.B, .C})
}
@(test)
bit_set_array_subset :: proc(t: ^testing.T) {
sup := Bsa_Arr{.A, .B, .C, .D}
sub := Bsa_Arr{.B, .C}
dis := Bsa_Arr{.E, .F}
testing.expect(t, sub <= sup, "sub is a subset")
testing.expect(t, sub < sup, "sub is a strict subset")
testing.expect(t, sup >= sub, "sup is a superset")
testing.expect(t, sup > sub, "sup is a strict superset")
testing.expect(t, sup <= sup, "reflexive <=")
testing.expect(t, sup >= sup, "reflexive >=")
testing.expect(t, !(sup < sup), "not a strict subset of itself")
testing.expect(t, !(sup > sup), "not a strict superset of itself")
testing.expect(t, !(dis <= sup), "disjoint set is not a subset")
testing.expect(t, !(sup <= dis), "superset is not a subset of a disjoint set")
// the same relations must fold at compile time
CSUP :: Bsa_Arr{.A, .B, .C, .D}
CSUB :: Bsa_Arr{.B, .C}
#assert(CSUB <= CSUP)
#assert(CSUB < CSUP)
#assert(CSUP >= CSUB)
#assert(CSUP > CSUB)
#assert(CSUP <= CSUP)
#assert(!(CSUP < CSUP))
#assert(CSUP == CSUP)
#assert(CSUB != CSUP)
}
@(test)
bit_set_array_card :: proc(t: ^testing.T) {
testing.expect_value(t, card(Bsa_Arr{}), 0)
testing.expect_value(t, card(Bsa_Arr{.A}), 1)
testing.expect_value(t, card(Bsa_Arr{.A, .C, .E, .G}), 4)
testing.expect_value(t, card(~Bsa_Arr{}), 8)
}
@(test)
bit_set_array_over_128_bits :: proc(t: ^testing.T) {
Big :: enum { V0 = 0, V64 = 64, V127 = 127, V128 = 128, V200 = 200 }
BS :: bit_set[Big; [4]u64] // 256 bits, cannot be an integer backing
testing.expect_value(t, size_of(BS), 32)
s := BS{.V0, .V128, .V200}
testing.expect(t, .V0 in s, ".V0 in s")
testing.expect(t, .V128 in s, ".V128 in s (past 128 bits)")
testing.expect(t, .V200 in s, ".V200 in s (past 128 bits)")
testing.expect(t, .V64 not_in s, ".V64 not in s")
testing.expect(t, .V127 not_in s, ".V127 not in s")
testing.expect_value(t, card(s), 3)
// bits beyond 128 must participate in the set relations
testing.expect(t, BS{.V200} <= s, "high bit subset")
testing.expect(t, !(BS{.V64} <= s), "absent high bit is not a subset")
testing.expect_value(t, s & BS{.V128}, BS{.V128})
}
@(test)
bit_set_array_matches_integer_backing :: proc(t: ^testing.T) {
// Exhaustively compare an array-backed and an integer-backed bit_set over the same 8-bit masks
// for every relation and set operation.
mismatches := 0
for a in u16(0)..<256 {
for b in u16(0)..<256 {
aa, ab := bsa_arr_from(u8(a)), bsa_arr_from(u8(b))
ia, ib := bsa_int_from(u8(a)), bsa_int_from(u8(b))
if (aa < ab) != (ia < ib) { mismatches += 1 }
if (aa <= ab) != (ia <= ib) { mismatches += 1 }
if (aa > ab) != (ia > ib) { mismatches += 1 }
if (aa >= ab) != (ia >= ib) { mismatches += 1 }
if (aa == ab) != (ia == ib) { mismatches += 1 }
if (aa != ab) != (ia != ib) { mismatches += 1 }
if card(aa) != card(ia) { mismatches += 1 }
if card(aa | ab) != card(ia | ib) { mismatches += 1 }
if card(aa & ab) != card(ia & ib) { mismatches += 1 }
if card(aa &~ ab) != card(ia &~ ib) { mismatches += 1 }
if card(~aa) != card(~ia) { mismatches += 1 }
}
}
testing.expect_value(t, mismatches, 0)
}
@(test)
bit_set_array_formatting :: proc(t: ^testing.T) {
a := Bsa_Arr{.A, .C, .H}
i := Bsa_Int{.A, .C, .H}
// The `%w` verb lists the members without the surrounding bit_set type name, so an array-backed
// set and an integer-backed set with the same members format identically.
sa := fmt.tprintf("%w", a)
si := fmt.tprintf("%w", i)
testing.expect_value(t, sa, si)
testing.expect_value(t, sa, "{Bsa_E.A, Bsa_E.C, Bsa_E.H}")
}