// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.13 // +build go1.13 package trie import ( "math/rand" "testing" ) func TestMask(t *testing.T) { for _, c := range []struct { p prefix b bitpos want prefix }{ { p: 0b00001000, b: 0b00000100, want: 0b00001011, }, { p: 0b01011011, b: 0b00000000, want: ^prefix(0), }, { p: 0b01011011, b: 0b00000001, want: 0b01011010, }, { p: 0b01011011, b: 0b00000010, want: 0b01011001, }, { p: 0b01011011, b: 0b00000100, want: 0b01011011, }, { p: 0b01011011, b: 0b00001000, want: 0b01010111, }, { p: 0b01011011, b: 0b00010000, want: 0b01001111, }, { p: 0b01011011, b: 0b00100000, want: 0b01011111, }, { p: 0b01011011, b: 0b01000000, want: 0b00111111, }, { p: 0b01011011, b: 0b01000000, want: 0b00111111, }, { p: 0b01011011, b: 0b10000000, want: 0b01111111, }, } { if got := mask(c.p, c.b); got != c.want { t.Errorf("mask(%#b,%#b) got %#b. want %#b", c.p, c.b, got, c.want) } } } func TestMaskImpotent(t *testing.T) { // test mask(mask(p, b), b) == mask(p,b) for _, p := range []prefix{ 0b0, 0b1, 0b100, ^prefix(0b0), ^prefix(0b10), } { for _, b := range []bitpos{ 0, 0b1, 1 << 2, 1 << 63, } { once := mask(p, b) twice := mask(once, b) if once != twice { t.Errorf("mask(mask(%#b,%#b), %#b) != mask(%#b,%#b) got %#b. want %#b", p, b, b, p, b, twice, once) } } } } func TestMatchPrefix(t *testing.T) { for _, c := range []struct { k prefix p prefix b bitpos }{ { k: 0b1000, p: 0b1011, b: 0b0100, }, { k: 0b1001, p: 0b1011, b: 0b0100, }, { k: 0b1010, p: 0b1011, b: 0b0100, }, { k: 0b1011, p: 0b1011, b: 0b0100, }, { k: 0b1100, p: 0b1011, b: 0b0100, }, { k: 0b1101, p: 0b1011, b: 0b0100, }, { k: 0b1110, p: 0b1011, b: 0b0100, }, { k: 0b1111, p: 0b1011, b: 0b0100, }, } { if !matchPrefix(c.k, c.p, c.b) { t.Errorf("matchPrefix(%#b, %#b,%#b) should be true", c.k, c.p, c.b) } } } func TestNotMatchPrefix(t *testing.T) { for _, c := range []struct { k prefix p prefix b bitpos }{ { k: 0b0000, p: 0b1011, b: 0b0100, }, { k: 0b0010, p: 0b1011, b: 0b0100, }, } { if matchPrefix(c.k, c.p, c.b) { t.Errorf("matchPrefix(%#b, %#b,%#b) should be false", c.k, c.p, c.b) } } } func TestBranchingBit(t *testing.T) { for _, c := range []struct { x prefix y prefix want bitpos }{ { x: 0b0000, y: 0b1011, want: 0b1000, }, { x: 0b1010, y: 0b1011, want: 0b0001, }, { x: 0b1011, y: 0b1111, want: 0b0100, }, { x: 0b1011, y: 0b1001, want: 0b0010, }, } { if got := branchingBit(c.x, c.y); got != c.want { t.Errorf("branchingBit(%#b, %#b,) is not expected value. got %#b want %#b", c.x, c.y, got, c.want) } } } func TestZeroBit(t *testing.T) { for _, c := range []struct { k prefix b bitpos }{ { k: 0b1000, b: 0b0100, }, { k: 0b1001, b: 0b0100, }, { k: 0b1010, b: 0b0100, }, } { if !zeroBit(c.k, c.b) { t.Errorf("zeroBit(%#b, %#b) should be true", c.k, c.b) } } } func TestZeroBitFails(t *testing.T) { for _, c := range []struct { k prefix b bitpos }{ { k: 0b1000, b: 0b1000, }, { k: 0b1001, b: 0b0001, }, { k: 0b1010, b: 0b0010, }, { k: 0b1011, b: 0b0001, }, } { if zeroBit(c.k, c.b) { t.Errorf("zeroBit(%#b, %#b) should be false", c.k, c.b) } } } func TestOrd(t *testing.T) { a := bitpos(0b0010) b := bitpos(0b1000) if ord(a, b) { t.Errorf("ord(%#b, %#b) should be false", a, b) } if !ord(b, a) { t.Errorf("ord(%#b, %#b) should be true", b, a) } if ord(a, a) { t.Errorf("ord(%#b, %#b) should be false", a, a) } if !ord(a, 0) { t.Errorf("ord(%#b, %#b) should be true", a, 0) } } func TestPrefixesOverlapLemma(t *testing.T) { // test // mask(p, fbb) == mask(q, fbb) // iff // m > n && matchPrefix(q, p, m) or (note: big endian encoding) // m < n && matchPrefix(p, q, n) or (note: big endian encoding) // m ==n && p == q // Case 1: mask(p, fbb) == mask(q, fbb) => m > n && matchPrefix(q, p, m) m, n := bitpos(1<<2), bitpos(1<<1) p, q := mask(0b100, m), mask(0b010, n) if !(prefixesOverlap(p, m, q, n) && m > n && matchPrefix(q, p, m)) { t.Errorf("prefixesOverlap(%#b, %#b, %#b, %#b) lemma does not hold", p, m, q, n) } // Case 2: mask(p, fbb) == mask(q, fbb) => m < n && matchPrefix(p, q, n) m, n = bitpos(1<<2), bitpos(1<<3) p, q = mask(0b100, m), mask(0b1000, n) if !(prefixesOverlap(p, m, q, n) && m < n && matchPrefix(p, q, n)) { t.Errorf("prefixesOverlap(%#b, %#b, %#b, %#b) lemma does not hold", p, m, q, n) } // Case 3: mask(p, fbb) == mask(q, fbb) => m < n && matchPrefix(p, q, n) m, n = bitpos(1<<2), bitpos(1<<2) p, q = mask(0b100, m), mask(0b001, n) if !(prefixesOverlap(p, m, q, n) && m == n && p == q) { t.Errorf("prefixesOverlap(%#b, %#b, %#b, %#b) lemma does not hold", p, m, q, n) } // Case 4: mask(p, fbb) != mask(q, fbb) m, n = bitpos(1<<1), bitpos(1<<1) p, q = mask(0b100, m), mask(0b001, n) if prefixesOverlap(p, m, q, n) || (m > n && matchPrefix(q, p, m)) || (m < n && matchPrefix(p, q, n)) || (m == n && p == q) { t.Errorf("prefixesOverlap(%#b, %#b, %#b, %#b) lemma does not hold", p, m, q, n) } // Do a few more random cases r := rand.New(rand.NewSource(123)) N := 2000 for i := 0; i < N; i++ { m := bitpos(1 << (r.Uint64() % (64 + 1))) n := bitpos(1 << (r.Uint64() % (64 + 1))) p := mask(prefix(r.Uint64()), m) q := mask(prefix(r.Uint64()), n) lhs := prefixesOverlap(p, m, q, n) rhs := (m > n && matchPrefix(q, p, m)) || (m < n && matchPrefix(p, q, n)) || (m == n && p == q) if lhs != rhs { t.Errorf("prefixesOverlap(%#b, %#b, %#b, %#b) != got %v. want %v", p, m, q, n, lhs, rhs) } } }