pax_global_header00006660000000000000000000000064141177727330014526gustar00rootroot0000000000000052 comment=e0252a2c53377c8ae5636f9c29a67d9b22ffacdb deheap-1.0/000077500000000000000000000000001411777273300126145ustar00rootroot00000000000000deheap-1.0/LICENSE.txt000066400000000000000000000020361411777273300144400ustar00rootroot00000000000000Copyright 2019 Aaron H. Alpar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. deheap-1.0/README.md000066400000000000000000000010741411777273300140750ustar00rootroot00000000000000# deheap Package deheap provides the implementation of a doubly ended heap. Doubly ended heaps are heaps with two sides, a min side and a max side. Like normal single-sided heaps, elements can be pushed onto and pulled off of a deheap. deheaps have an additional `Pop` function, `PopMax`, that returns elements from the opposite side of the ordering. This implementation has emphasized compatibility with existing libraries in the sort and heap packages. Performace of the deheap functions should be very close to the performance of the functions of the heap library deheap-1.0/corpus/000077500000000000000000000000001411777273300141275ustar00rootroot00000000000000deheap-1.0/corpus/0000066400000000000000000000000021411777273300142010ustar00rootroot00000000000000A<deheap-1.0/corpus/1000066400000000000000000000000021411777273300142020ustar00rootroot00000000000000B>deheap-1.0/corpus/2000066400000000000000000000000101411777273300142020ustar00rootroot00000000000000ABCD<><>deheap-1.0/deheap.go000066400000000000000000000114611411777273300143740ustar00rootroot00000000000000// // Copyright 2019 Aaron H. Alpar // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // // Package deheap provides the implementation of a doubly ended heap. // Doubly ended heaps are heaps with two sides, a min side and a max side. // Like normal single-sided heaps, elements can be pushed onto and pulled // off of a deheap. deheaps have an additional Pop function, PopMax, that // returns elements from the opposite side of the ordering. // // This implementation has emphasized compatibility with existing libraries // in the sort and heap packages. // // Performace of the deheap functions should be very close to the // performance of the functions of the heap library // package deheap import ( "container/heap" "math/bits" ) func hparent(i int) int { return (i - 1) / 2 } func hlchild(i int) int { return (i * 2) + 1 } func parent(i int) int { return ((i + 1) / 4) - 1 } func lchild(i int) int { return ((i + 1 ) * 4) - 1 } func level(i int) int { return bits.Len(uint(i)+1) - 1 } func isMinHeap(i int) bool { return level(i) % 2 == 0 } func min4(h heap.Interface, l int, min bool, i int) int { q := i i++ if i >= l { return q } if min == h.Less(i, q) { q = i } i++ if i >= l { return q } if min == h.Less(i, q) { q = i } i++ if i >= l { return q } if min == h.Less(i, q) { q = i } return q } // min2 func min2(h heap.Interface, l int, min bool, i int) int { if i+1 >= l { return i } if min != h.Less(i+1, i) { return i } return i + 1 } // min3 func min3(h heap.Interface, l int, min bool, i, j, k int) int { q := i if j < l && h.Less(j, q) == min { q = j } if k < l && h.Less(k, q) == min { q = k } return q } // bubbledown func bubbledown(h heap.Interface, l int, min bool, i int) (q int, r int) { q = i r = i for { // find min of children j := min2(h, l, min, hlchild(i)) if j >= l { break } // find min of grandchildren k := min4(h, l, min, lchild(i)) // swap of less than the element at i v := min3(h, l, min, i, j, k) if v == i || v >= l { break } // v == k q = v h.Swap(v, i) if v == j { break } p := hparent(v) if h.Less(p, v) == min { h.Swap(p, v) r = p } i = v } return q, r } // bubbleup func bubbleup(h heap.Interface, min bool, i int) (q bool) { if i < 0 { return false } j := parent(i) for j >= 0 && min == h.Less(i, j) { q = true h.Swap(i, j) i = j j = parent(i) } min = !min j = hparent(i) for j >= 0 && min == h.Less(i, j) { q = true h.Swap(i, j) i = j j = parent(i) } return q } // Pop the smallest value off the heap. See heap.Pop(). // Time complexity is O(log n), where n = h.Len() func Pop(h heap.Interface) interface{} { l := h.Len()-1 h.Swap(0, l) q := h.Pop() bubbledown(h, l, true, 0) return q } // Pop the largest value off the heap. See heap.Pop(). // Time complexity is O(log n), where n = h.Len() func PopMax(h heap.Interface) interface{} { l := h.Len() j := 0 if l > 1 { j = min2(h, l,false, 1) } l = l - 1 h.Swap(j, l) q := h.Pop() bubbledown(h, l,false, j) return q } // Remove element at index i. See heap.Remove(). // The complexity is O(log n) where n = h.Len(). func Remove(h heap.Interface, i int) (q interface{}) { l := h.Len() - 1 h.Swap(i, l) q = h.Pop() if l != i { q, r := bubbledown(h, l, isMinHeap(i), i) bubbleup(h, isMinHeap(q), q) bubbleup(h, isMinHeap(r), r) } return q } // Push an element onto the heap. See heap.Push() // Time complexity is O(log n), where n = h.Len() func Push(h heap.Interface, o interface{}) { h.Push(o) l := h.Len() i := l - 1 bubbleup(h, isMinHeap(i), i) } // Init initializes the heap. // This should be called once on non-empty heaps before calling Pop(), PopMax() or Push(). See heap.Init() func Init(h heap.Interface) { l := h.Len() for i := 0; i < l; i++ { bubbleup(h, isMinHeap(i), i) } } deheap-1.0/deheap_test.go000066400000000000000000000441221411777273300154330ustar00rootroot00000000000000// // Copyright 2019 Aaron H. Alpar // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files // (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // package deheap import ( "container/heap" "fmt" "math" "math/rand" "reflect" "sort" "testing" "time" ) type IntHeap []int func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h IntHeap) Len() int { return len(h) } func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int)) } func (h *IntHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[0 : n-1] return x } func _newRand() *rand.Rand { return rand.New(rand.NewSource(time.Now().Unix())) } func TestParent(t *testing.T) { x := parent(0) if x != -1 { t.Fatalf("unexpected value: %d", x) } x = parent(1) if x != -1 { t.Fatalf("unexpected value: %d", x) } x = parent(2) if x != -1 { t.Fatalf("unexpected value: %d", x) } x = parent(3) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = parent(4) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = parent(5) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = parent(6) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = parent(7) if x != 1 { t.Fatalf("unexpected value: %d", x) } } func TestHParent(t *testing.T) { x := hparent(0) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = hparent(1) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = hparent(2) if x != 0 { t.Fatalf("unexpected value: %d", x) } x = hparent(3) if x != 1 { t.Fatalf("unexpected value: %d", x) } x = hparent(4) if x != 1 { t.Fatalf("unexpected value: %d", x) } x = hparent(5) if x != 2 { t.Fatalf("unexpected value: %d", x) } x = hparent(6) if x != 2 { t.Fatalf("unexpected value: %d", x) } x = hparent(7) if x != 3 { t.Fatalf("unexpected value: %d", x) } } func TestLChild(t *testing.T) { x := lchild(0) if x != 3 { t.Fatalf("unexpected value") } x = lchild(3) if x != 15 { t.Fatalf("unexpected value") } x = parent(15) if x != 3 { t.Fatalf("unexpected value") } x = parent(5) if x != 0 { t.Fatalf("unexpected value: %d", x) } } func TestHLChild(t *testing.T) { x := hlchild(0) if x != 1 { t.Fatalf("unexpected value") } x = hlchild(1) if x != 3 { t.Fatalf("unexpected value") } x = hparent(3) if x != 1 { t.Fatalf("unexpected value") } } func TestMin2(t *testing.T) { h := &IntHeap{10, 1} x := min2(h, h.Len(), true, 0) if x != 1 { t.Fatalf("unexpected value") } h = &IntHeap{1, 10} x = min2(h, h.Len(),true, 0) if x != 0 { t.Fatalf("unexpected value") } x = min2(h, h.Len(),false, 9) if x != 9 { t.Fatalf("unexpected value") } h = &IntHeap{10, 10} x = min2(h, h.Len(),true, 0) if x != 0 { t.Fatalf("unexpected value") } } func TestLevel(t *testing.T) { x := level(0) if x != 0 { t.Fatalf("unexpected value") } x = level(1) if x != 1 { t.Fatalf("unexpected value") } x = level(2) if x != 1 { t.Fatalf("unexpected value") } } func TestBubbleUp(t *testing.T) { h := &IntHeap{1, 15, 2} bubbleup(h, isMinHeap(2), 2) if !reflect.DeepEqual(h, &IntHeap{1, 15, 2}) { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{1, 4, 10} bubbleup(h, isMinHeap(2), 2) if !reflect.DeepEqual(h, &IntHeap{1, 4, 10}) { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{2, 15, 13, 4, 6, 8, 1} bubbleup(h, isMinHeap(6), 6) if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{1, 15, 14, 2, 3, 4, 5, 13, 12, 11, 10, 6, 7, 8, 9} bubbleup(h, isMinHeap(14), 14) if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v", h) } } func TestBubbleDown(t *testing.T) { h := &IntHeap{15, 1, 2} bubbledown(h, h.Len(),isMinHeap(0), 0) if !reflect.DeepEqual(h, &IntHeap{1, 15, 2}) { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{5, 7, 4, 6, 1, 3, 2} bubbledown(h, h.Len(),isMinHeap(0), 0) if !reflect.DeepEqual(h, &IntHeap{1, 7, 4, 6, 5, 3, 2}) { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{10, 8, 12, 1, 2, 9, 10, 5, 3, 4, 6, 11} bubbledown(h, h.Len(),isMinHeap(0), 0) if !reflect.DeepEqual(h, &IntHeap{1, 10, 12, 3, 2, 9, 10, 5, 8, 4, 6, 11}) { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{14, 15, 12, 4, 2, 3, 5, 13} bubbledown(h, h.Len(),isMinHeap(0), 0) if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v", h) } h = &IntHeap{13, 14, 15, 3, 4, 5, 6, 7, 8, 9, 10} bubbledown(h, h.Len(),isMinHeap(0), 0) if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v", h) } } func TestMin4(t *testing.T) { h := &IntHeap{3, 1, 2, 4} x := min4(h, h.Len(),true, 0) if x != 1 { t.Fatalf("unexpected value") } h = &IntHeap{1, 3, 2, 4} x = min4(h, h.Len(),true, 0) if x != 0 { t.Fatalf("unexpected value") } h = &IntHeap{2, 3, 1, 4} x = min4(h, h.Len(), true, 0) if x != 2 { t.Fatalf("unexpected value") } h = &IntHeap{2, 3, 4, 1} x = min4(h, h.Len(),true, 0) if x != 3 { t.Fatalf("unexpected value") } h = &IntHeap{1, 1, 2, 2} x = min4(h, h.Len(), true, 0) if x != 0 { t.Fatalf("unexpected value") } h = &IntHeap{2, 2, 1, 1} x = min4(h, h.Len(),true, 0) if x != 2 { t.Fatalf("unexpected value") } h = &IntHeap{2} x = min4(h, h.Len(),true, 0) if x != 0 { t.Fatalf("unexpected value") } } func TestMin3(t *testing.T) { h := &IntHeap{3, 1, 2} x := min3(h, h.Len(),true, 0, 1, 2) if x != 1 { t.Fatalf("unexpected value") } h = &IntHeap{1, 3, 2} x = min3(h, h.Len(),true, 0, 1, 2) if x != 0 { t.Fatalf("unexpected value") } h = &IntHeap{2, 3, 1} x = min3(h, h.Len(),true, 0, 1, 2) if x != 2 { t.Fatalf("unexpected value") } h = &IntHeap{1, 1, 2} x = min3(h, h.Len(),true, 0, 1, 2) if x != 0 && x != 1 { t.Fatalf("unexpected value") } h = &IntHeap{2, 1, 1} x = min3(h, h.Len(),true, 0, 1, 2) if x != 2 && x != 1 { t.Fatalf("unexpected value") } h = &IntHeap{2} x = min3(h, h.Len(),true, 0, 1, 2) if x != 0 { t.Fatalf("unexpected value") } } func TestInit(t *testing.T) { h := &IntHeap{15, 1, 2, 14, 13, 12, 11, 3, 4, 5, 6, 7, 8, 9, 10} Init(h) if x, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v %v %v", x, y, h) } } func TestPush(t *testing.T) { h := &IntHeap{} for i := 0; i < 32; i++ { Push(h, i) } if x, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v %v %v", x, y, h) } h = &IntHeap{} for i := 3; i >= 0; i-- { Push(h, i) if x, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v %v %v", x, y, h) } } h = &IntHeap{} for i := 32; i >= 0; i-- { q := (i%2 == 0) k := i if q { k = 32 - i } Push(h, k) } if x, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %v %v %v", x, y, h) } } func TestPops(t *testing.T) { ts := []struct { h IntHeap q0 []int q1 []int q2 []int }{ { IntHeap{1, 4, 3, 2, 4, 3}, []int{1, 2, 3, 3, 4, 4}, []int{4, 4, 3, 3, 2, 1}, []int{1, 2, 3, 3, 4, 4}, }, { IntHeap{1, 3, 2, 2, 3}, []int{1, 2, 2, 3, 3}, []int{3, 3, 2, 2, 1}, []int{1, 2, 2, 3, 3}, }, { IntHeap{1, 5, 4, 2, 3, 3}, []int{1, 2, 3, 3, 4, 5}, []int{5, 4, 3, 3, 2, 1}, []int{1, 2, 3, 3, 4, 5}, }, { IntHeap{1, 4, 4, 2, 3, 3}, []int{1, 2, 3, 3, 4, 4}, []int{4, 4, 3, 3, 2, 1}, []int{1, 2, 3, 3, 4, 4}, }, { IntHeap{1, 4, 4, 3, 3, 2}, []int{1, 2, 3, 3, 4, 4}, []int{4, 4, 3, 3, 2, 1}, []int{1, 2, 3, 3, 4, 4}, }, { IntHeap{1, 8, 9, 2, 3, 4, 5, 6, 7}, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int{9, 8, 7, 6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, { IntHeap{1, 9, 5, 4, 7, 3, 2, 6, 8}, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, []int{9, 8, 7, 6, 5, 4, 3, 2, 1}, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}, }, } for ti, tv := range ts { if a, b, ok := isHeap(t, &tv.h); !ok { t.Fatalf("unexpected value: %d %d %v", a, b, tv.h) } t.Run(fmt.Sprintf("%d Pop", ti), func(t1 *testing.T) { hp0 := make(IntHeap, len(tv.h)) copy(hp0, tv.h) for _, y := range tv.q0 { if y != Pop(&hp0) { t.Fatalf("unexpected value") } } }) t.Run(fmt.Sprintf("%d PopMax", ti), func(t1 *testing.T) { hp1 := make(IntHeap, len(tv.h)) copy(hp1, tv.h) for _, y := range tv.q1 { if y != PopMax(&hp1) { t.Fatalf("unexpected value") } } }) for k := 0; k < len(tv.h); k++ { t.Run(fmt.Sprintf("%d Remove %d", ti, k), func(t1 *testing.T) { hp2 := make(IntHeap, len(tv.h)) copy(hp2, tv.h) q2 := make([]int, len(tv.q2)) copy(q2, tv.q2) c := Remove(&hp2, k).(int) j := sort.SearchInts(q2, c) q2 = append(q2[:j], q2[j+1:]...) for j, y := range q2 { x := Pop(&hp2) if y != x { t.Fatalf("unexpected value: %d %d", j, x) } } }) } } } func TestRemove5(t *testing.T) { h := &IntHeap{1, 4, 4, 2, 3, 3} x0 := Pop(h).(int) if x0 != 1 { t.Fatalf("unexpected value") } if x0, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %d %d %2d", x0, y, h) } x1 := Remove(h, 1).(int) if x1 != 4 { t.Fatalf("unexpected value: %d", x1) } x3 := PopMax(h).(int) if x3 != 4 { t.Fatalf("unexpected value: %d", x3) } x4 := PopMax(h).(int) if x4 != 3 { t.Fatalf("unexpected value: %d", x4) } x5 := PopMax(h).(int) if x5 != 3 { t.Fatalf("unexpected value: %d", x5) } x6 := PopMax(h).(int) if x6 != 2 { t.Fatalf("unexpected value: %d", x6) } } func TestPop3(t *testing.T) { h := &IntHeap{1, 6, 4, 5, 2, 3} x0 := Pop(h).(int) if x0 != 1 { t.Fatalf("unexpected value") } if x0, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %d %d %2d", x0, y, h) } x1 := Pop(h).(int) if x1 != 2 { t.Fatalf("unexpected value") } x3 := Pop(h).(int) if x3 != 3 { t.Fatalf("unexpected value") } x4 := Pop(h).(int) if x4 != 4 { t.Fatalf("unexpected value") } x5 := Pop(h).(int) if x5 != 5 { t.Fatalf("unexpected value") } x6 := Pop(h).(int) if x6 != 6 { t.Fatalf("unexpected value") } } func TestPop(t *testing.T) { h := &IntHeap{1, 31, 30, 4, 3, 2, 5, 22, 17, 19, 21, 23, 25, 27, 29, 15, 10, 7, 16, 8, 18, 9, 20, 6, 14, 11, 24, 12, 26, 13, 28} x0 := Pop(h).(int) if x0, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %d %d %2d", x0, y, h) } x1 := Pop(h).(int) if x1 < x0 { t.Fatalf("unexpected value: %2d", h) } h = &IntHeap{17, 31, 30, 20, 19, 22, 18, 24, 26, 23, 21, 28, 25, 27, 29} x0 = Pop(h).(int) if x0, y, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %d %d %2d", x0, y, h) } x1 = Pop(h).(int) if x1 < x0 { t.Fatalf("unexpected value: %2d", h) } } func TestRandomPop(t *testing.T) { N := 10000 h := randIntHeap(t, N) i0 := 0 i1 := N + 1 s := _newRand() for h.Len() > 0 { if s.Intn(2) == 0 { x := Pop(h).(int) if x < i0 { t.Fatalf("unexpected value") } i0 = x } else { x := PopMax(h).(int) if x > i1 { t.Fatalf("unexpected value") } i1 = x } } } func TestRemove(t *testing.T) { h := &IntHeap{0, 9, 5, 6, 1, 2, 4, 8, 7, 3} x := Remove(h, 9).(int) if x != 3 { t.Fatalf("unexpected value") } if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value") } if !reflect.DeepEqual(h, &IntHeap{0, 9, 5, 6, 1, 2, 4, 8, 7}) { t.Fatalf("unexpected value") } x = Remove(h, 2).(int) if x != 5 { t.Fatalf("unexpected value") } if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value") } if !reflect.DeepEqual(h, &IntHeap{0, 9, 7, 6, 1, 2, 4, 8}) { t.Fatalf("unexpected value") } x = Remove(h, 0).(int) if x != 0 { t.Fatalf("unexpected value") } if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value") } if !reflect.DeepEqual(h, &IntHeap{1, 9, 7, 6, 8, 2, 4}) && !reflect.DeepEqual(h, &IntHeap{1, 9, 8, 6, 7, 2, 4}) { t.Fatalf("unexpected value: %v", h) } } func TestDups(t *testing.T) { h := &IntHeap{} in := []int{0, 10, 9, 8, 7, 6, 5, 5, 4, 7, 3, 2, 1} out := []int{0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10} for _, v := range in { Push(h, v) } l := h.Len() if l != 13 { t.Fatalf("unexpected value") } for i, v := range out { x := Pop(h).(int) if h.Len() != l-(i+1) { t.Fatalf("unexpected value") } if x != v { t.Fatalf("unexpected value") } } h = &IntHeap{} out = []int{10, 9, 8, 7, 7, 6, 5, 5, 4, 3, 2, 1, 0} for _, v := range in { Push(h, v) } l = h.Len() if l != 13 { t.Fatalf("unexpected value") } for i, v := range out { x := PopMax(h).(int) if h.Len() != l-(i+1) { t.Fatalf("unexpected value") } if x != v { t.Fatalf("unexpected value") } } } func TestOps(t *testing.T) { for k := 0; k < 1000; k++ { s := rand.New(rand.NewSource(time.Now().Unix())) N := s.Intn(64) + 2 h := randIntHeapWithDups(t, N, 1/10) y0 := math.MinInt32 y1 := math.MaxInt32 for h.Len() > 0 { if s.Intn(2) == 0 { x := Pop(h).(int) if x < y0 || x > y1 { t.Fatalf("unexpected value: %d %d", x, y0) } y0 = x } else { x := PopMax(h).(int) if x > y1 || x < y0 { t.Fatalf("unexpected value: %d %d", x, y1) } y1 = x } if _, _, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value") } } h = randIntHeapWithDups(t, N, 1/10) for h.Len() > 0 { h0 := make([]int, h.Len()) copy(h0, []int(*h)) x := s.Intn(h.Len()) Remove(h, x) if i, j, ok := isHeap(t, h); !ok { t.Fatalf("unexpected value: %d %d %d\n%v\n%v", x, i, j, h0, h) } } } } func BenchmarkMin4(b *testing.B) { r := &[]int{} for i := 0; i < b.N; i++ { *r = append(*r, i) } s := _newRand() s.Shuffle(len(*r), func(i, j int) { (*r)[i], (*r)[j] = (*r)[j], (*r)[i] }) h := (*IntHeap)(r) b.ResetTimer() for i := 0; i < b.N; i++ { min4(h, h.Len(), true, i) } } func BenchmarkBaselinePush(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) b.ResetTimer() a := []int{} for _, q := range r { a = append(a, q) } } func BenchmarkPush(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) b.ResetTimer() h := &IntHeap{} for _, q := range r { Push(h, q) } } func BenchmarkPop(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) h := &IntHeap{} for _, q := range r { Push(h, q) } b.ResetTimer() for i := 0; i < b.N; i++ { Pop(h) } } func BenchmarkPopMax(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) h := &IntHeap{} for _, q := range r { Push(h, q) } b.ResetTimer() for i := 0; i < b.N; i++ { PopMax(h) } } func BenchmarkPushPop(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) b.ResetTimer() h := &IntHeap{} for _, q := range r { Push(h, q) } for i := 0; i < b.N; i++ { Pop(h) } } func BenchmarkHeapPushPop(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) b.ResetTimer() h := &IntHeap{} for _, q := range r { heap.Push(h, q) } for i := 0; i < b.N; i++ { heap.Pop(h) } } func BenchmarkHeapPop(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) h := &IntHeap{} for _, q := range r { heap.Push(h, q) } b.ResetTimer() x := Pop(h).(int) for i := 0; i < b.N-1; i++ { y := heap.Pop(h).(int) if x > y { panic("bad") } } } func BenchmarkHeapPush(b *testing.B) { r := []int{} for i := 0; i < b.N; i++ { r = append(r, i) } s := _newRand() s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) b.ResetTimer() h := &IntHeap{} for _, q := range r { heap.Push(h, q) } } func isHeap(t *testing.T, h heap.Interface) (int, int, bool) { t.Helper() l := h.Len() for i := l - 1; i >= 0; i-- { min := isMinHeap(i) p0 := parent(i) p1 := hparent(i) if p0 >= 0 && min != h.Less(p0, i) { return p0, i, false } if p1 >= 0 && min == h.Less(p1, i) { return p1, i, false } } if l > 1 && h.Less(1, 0) { return 1, 0, false } if l > 2 && h.Less(2, 0) { return 2, 0, false } return 0, 0, true } func randIntHeap(t *testing.T, n int) *IntHeap { t.Helper() r := []int{} for i := 0; i < n; i++ { r = append(r, i+1) } s := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) h := &IntHeap{} for _, q := range r { Push(h, q) } if x, y, ok := isHeap(t, h); !ok { panic(fmt.Sprintf("not a heap!: %d %d", x, y)) } return h } func randIntHeapWithDups(t *testing.T, n int, fraction float64) *IntHeap { t.Helper() s := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) r := []int{} for i := 0; i < n; i++ { r = append(r, i+1) for s.Float64() < fraction { r = append(r, i) } } s.Shuffle(len(r), func(i, j int) { r[i], r[j] = r[j], r[i] }) h := &IntHeap{} for _, q := range r { Push(h, q) } if x, y, ok := isHeap(t, h); !ok { panic(fmt.Sprintf("not a heap!: %d %d", x, y)) } return h } deheap-1.0/example_intheap_test.go000066400000000000000000000024041411777273300173450ustar00rootroot00000000000000// Example developed by borrowing from "container/heap/example_intheap_test.go" // Portions Copyright 2012 by the Go authors // // This example demonstrates an integer doubbly-ended heap built using the deheap interface. package deheap_test import ( "fmt" "github.com/aalpar/deheap" ) // An IntHeap is a min-heap of ints. type IntDeheap []int func (h IntDeheap) Len() int { return len(h) } func (h IntDeheap) Less(i, j int) bool { return h[i] < h[j] } func (h IntDeheap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *IntDeheap) Push(x interface{}) { // Push and Pop use pointer receivers because they modify the slice's length, // not just its contents. *h = append(*h, x.(int)) } func (h *IntDeheap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[:n-1] return x } // This example inserts several ints into an IntHeap, checks the minimum, // and removes them in order of priority. func Example_intHeap() { h := &IntDeheap{2, 1, 5, 6} deheap.Init(h) deheap.Push(h, 3) fmt.Printf("minimum: %d\n", (*h)[0]) for h.Len() > 3 { fmt.Printf("%d ", deheap.PopMax(h)) } for h.Len() > 1 { fmt.Printf("%d ", deheap.Pop(h)) } fmt.Printf("middle value: %d\n", (*h)[0]) // Output: // minimum: 1 // 6 5 1 2 middle value: 3 } deheap-1.0/fuzz.go000066400000000000000000000043271411777273300141470ustar00rootroot00000000000000// Run fuzz tests on the package // // This is done with a byte heap to test and a simple reimplementation // to check correctness against. // // First install go-fuzz // // go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build // // Next build the instrumented package // // go-fuzz-build // // Finally fuzz away // // go-fuzz // // See https://github.com/dvyukov/go-fuzz for more instructions //+build gofuzz package deheap import ( "fmt" "sort" ) // An byteHeap is a double ended heap of bytes type byteDeheap []byte func (h byteDeheap) Len() int { return len(h) } func (h byteDeheap) Less(i, j int) bool { return h[i] < h[j] } func (h byteDeheap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *byteDeheap) Push(x interface{}) { *h = append(*h, x.(byte)) } func (h *byteDeheap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[:n-1] return x } // sortedHeap is an inefficient reimplementation for test purposes type sortedHeap []byte func (h *sortedHeap) Push(x byte) { data := *h i := sort.Search(len(data), func(i int) bool { return data[i] >= x }) // i is the either the position of x or where it should be inserted data = append(data, 0) copy(data[i+1:], data[i:]) data[i] = x *h = data } func (h *sortedHeap) Pop() (x byte) { data := *h x = data[0] *h = data[1:] return x } func (h *sortedHeap) PopMax() (x byte) { data := *h x = data[len(data)-1] *h = data[:len(data)-1] return x } // Fuzzer input is a string of bytes. // // If the byte is one of these, then the action is performed // '<' Pop (minimum) // '>' PopMax // Otherwise the bytes is Pushed onto the heap func Fuzz(data []byte) int { h := &byteDeheap{} Init(h) s := sortedHeap{} for _, c := range data { switch c { case '<': if h.Len() > 0 { got := Pop(h) want := s.Pop() if got != want { panic(fmt.Sprintf("Pop: want = %d, got = %d", want, got)) } } case '>': if h.Len() > 0 { got := PopMax(h) want := s.PopMax() if got != want { panic(fmt.Sprintf("PopMax: want = %d, got = %d", want, got)) } } default: Push(h, c) s.Push(c) } if len(s) != h.Len() { panic("wrong length") } } return 1 } deheap-1.0/go.mod000066400000000000000000000000511411777273300137160ustar00rootroot00000000000000module github.com/aalpar/deheap go 1.16