-
Notifications
You must be signed in to change notification settings - Fork 664
/
unbounded_deque.go
190 lines (169 loc) · 4.94 KB
/
unbounded_deque.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package buffer
import "github.com/ava-labs/avalanchego/utils"
const defaultInitSize = 32
// An unbounded deque (double-ended queue).
// See https://en.wikipedia.org/wiki/Double-ended_queue
// Not safe for concurrent access.
type Deque[T any] interface {
// Place an element at the leftmost end of the deque.
// Returns true if the element was placed in the deque.
PushLeft(T) bool
// Place an element at the rightmost end of the deque.
// Returns true if the element was placed in the deque.
PushRight(T) bool
// Remove and return the leftmost element of the deque.
// Returns false if the deque is empty.
PopLeft() (T, bool)
// Remove and return the rightmost element of the deque.
// Returns false if the deque is empty.
PopRight() (T, bool)
// Return the leftmost element of the deque without removing it.
// Returns false if the deque is empty.
PeekLeft() (T, bool)
// Return the rightmost element of the deque without removing it.
// Returns false if the deque is empty.
PeekRight() (T, bool)
// Returns the element at the given index.
// Returns false if the index is out of bounds.
// The leftmost element is at index 0.
Index(int) (T, bool)
// Returns the number of elements in the deque.
Len() int
// Returns the elements in the deque from left to right.
List() []T
}
// Returns a new unbounded deque with the given initial slice size.
// Note that the returned deque is always empty -- [initSize] is just
// a hint to prevent unnecessary resizing.
func NewUnboundedDeque[T any](initSize int) Deque[T] {
if initSize < 2 {
initSize = defaultInitSize
}
return &unboundedSliceDeque[T]{
// Note that [initSize] must be >= 2 to satisfy invariants (1) and (2).
data: make([]T, initSize),
right: 1,
}
}
// Invariants after each function call and before the first call:
// (1) The next element pushed left will be placed at data[left]
// (2) The next element pushed right will be placed at data[right]
// (3) There are [size] elements in the deque.
type unboundedSliceDeque[T any] struct {
size, left, right int
data []T
}
func (b *unboundedSliceDeque[T]) PushRight(elt T) bool {
// Invariant (2) says it's safe to place the element without resizing.
b.data[b.right] = elt
b.size++
b.right++
b.right %= len(b.data)
b.resize()
return true
}
func (b *unboundedSliceDeque[T]) PushLeft(elt T) bool {
// Invariant (1) says it's safe to place the element without resizing.
b.data[b.left] = elt
b.size++
b.left--
if b.left < 0 {
b.left = len(b.data) - 1 // Wrap around
}
b.resize()
return true
}
func (b *unboundedSliceDeque[T]) PopLeft() (T, bool) {
if b.size == 0 {
return utils.Zero[T](), false
}
idx := b.leftmostEltIdx()
elt := b.data[idx]
// Zero out to prevent memory leak.
b.data[idx] = utils.Zero[T]()
b.size--
b.left++
b.left %= len(b.data)
return elt, true
}
func (b *unboundedSliceDeque[T]) PeekLeft() (T, bool) {
if b.size == 0 {
return utils.Zero[T](), false
}
idx := b.leftmostEltIdx()
return b.data[idx], true
}
func (b *unboundedSliceDeque[T]) PopRight() (T, bool) {
if b.size == 0 {
return utils.Zero[T](), false
}
idx := b.rightmostEltIdx()
elt := b.data[idx]
// Zero out to prevent memory leak.
b.data[idx] = utils.Zero[T]()
b.size--
b.right--
if b.right < 0 {
b.right = len(b.data) - 1 // Wrap around
}
return elt, true
}
func (b *unboundedSliceDeque[T]) PeekRight() (T, bool) {
if b.size == 0 {
return utils.Zero[T](), false
}
idx := b.rightmostEltIdx()
return b.data[idx], true
}
func (b *unboundedSliceDeque[T]) Index(idx int) (T, bool) {
if idx < 0 || idx >= b.size {
return utils.Zero[T](), false
}
leftmostIdx := b.leftmostEltIdx()
idx = (leftmostIdx + idx) % len(b.data)
return b.data[idx], true
}
func (b *unboundedSliceDeque[T]) Len() int {
return b.size
}
func (b *unboundedSliceDeque[T]) List() []T {
if b.size == 0 {
return nil
}
list := make([]T, b.size)
leftmostIdx := b.leftmostEltIdx()
if numCopied := copy(list, b.data[leftmostIdx:]); numCopied < b.size {
// We copied all of the elements from the leftmost element index
// to the end of the underlying slice, but we still haven't copied
// all of the elements, so wrap around and copy the rest.
copy(list[numCopied:], b.data[:b.right])
}
return list
}
func (b *unboundedSliceDeque[T]) leftmostEltIdx() int {
if b.left == len(b.data)-1 { // Wrap around case
return 0
}
return b.left + 1 // Normal case
}
func (b *unboundedSliceDeque[T]) rightmostEltIdx() int {
if b.right == 0 {
return len(b.data) - 1 // Wrap around case
}
return b.right - 1 // Normal case
}
func (b *unboundedSliceDeque[T]) resize() {
if b.size != len(b.data) {
return
}
newData := make([]T, b.size*2)
leftmostIdx := b.leftmostEltIdx()
copy(newData, b.data[leftmostIdx:])
numCopied := len(b.data) - leftmostIdx
copy(newData[numCopied:], b.data[:b.right])
b.data = newData
b.left = len(b.data) - 1
b.right = b.size
}