summaryrefslogtreecommitdiffstats
path: root/src/container/ring
diff options
context:
space:
mode:
Diffstat (limited to 'src/container/ring')
-rw-r--r--src/container/ring/example_test.go193
-rw-r--r--src/container/ring/ring.go136
-rw-r--r--src/container/ring/ring_test.go228
3 files changed, 557 insertions, 0 deletions
diff --git a/src/container/ring/example_test.go b/src/container/ring/example_test.go
new file mode 100644
index 0000000..4b659d2
--- /dev/null
+++ b/src/container/ring/example_test.go
@@ -0,0 +1,193 @@
+// Copyright 2017 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.
+
+package ring_test
+
+import (
+ "container/ring"
+ "fmt"
+)
+
+func ExampleRing_Len() {
+ // Create a new ring of size 4
+ r := ring.New(4)
+
+ // Print out its length
+ fmt.Println(r.Len())
+
+ // Output:
+ // 4
+}
+
+func ExampleRing_Next() {
+ // Create a new ring of size 5
+ r := ring.New(5)
+
+ // Get the length of the ring
+ n := r.Len()
+
+ // Initialize the ring with some integer values
+ for i := 0; i < n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+
+ // Iterate through the ring and print its contents
+ for j := 0; j < n; j++ {
+ fmt.Println(r.Value)
+ r = r.Next()
+ }
+
+ // Output:
+ // 0
+ // 1
+ // 2
+ // 3
+ // 4
+}
+
+func ExampleRing_Prev() {
+ // Create a new ring of size 5
+ r := ring.New(5)
+
+ // Get the length of the ring
+ n := r.Len()
+
+ // Initialize the ring with some integer values
+ for i := 0; i < n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+
+ // Iterate through the ring backwards and print its contents
+ for j := 0; j < n; j++ {
+ r = r.Prev()
+ fmt.Println(r.Value)
+ }
+
+ // Output:
+ // 4
+ // 3
+ // 2
+ // 1
+ // 0
+}
+
+func ExampleRing_Do() {
+ // Create a new ring of size 5
+ r := ring.New(5)
+
+ // Get the length of the ring
+ n := r.Len()
+
+ // Initialize the ring with some integer values
+ for i := 0; i < n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+
+ // Iterate through the ring and print its contents
+ r.Do(func(p any) {
+ fmt.Println(p.(int))
+ })
+
+ // Output:
+ // 0
+ // 1
+ // 2
+ // 3
+ // 4
+}
+
+func ExampleRing_Move() {
+ // Create a new ring of size 5
+ r := ring.New(5)
+
+ // Get the length of the ring
+ n := r.Len()
+
+ // Initialize the ring with some integer values
+ for i := 0; i < n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+
+ // Move the pointer forward by three steps
+ r = r.Move(3)
+
+ // Iterate through the ring and print its contents
+ r.Do(func(p any) {
+ fmt.Println(p.(int))
+ })
+
+ // Output:
+ // 3
+ // 4
+ // 0
+ // 1
+ // 2
+}
+
+func ExampleRing_Link() {
+ // Create two rings, r and s, of size 2
+ r := ring.New(2)
+ s := ring.New(2)
+
+ // Get the length of the ring
+ lr := r.Len()
+ ls := s.Len()
+
+ // Initialize r with 0s
+ for i := 0; i < lr; i++ {
+ r.Value = 0
+ r = r.Next()
+ }
+
+ // Initialize s with 1s
+ for j := 0; j < ls; j++ {
+ s.Value = 1
+ s = s.Next()
+ }
+
+ // Link ring r and ring s
+ rs := r.Link(s)
+
+ // Iterate through the combined ring and print its contents
+ rs.Do(func(p any) {
+ fmt.Println(p.(int))
+ })
+
+ // Output:
+ // 0
+ // 0
+ // 1
+ // 1
+}
+
+func ExampleRing_Unlink() {
+ // Create a new ring of size 6
+ r := ring.New(6)
+
+ // Get the length of the ring
+ n := r.Len()
+
+ // Initialize the ring with some integer values
+ for i := 0; i < n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+
+ // Unlink three elements from r, starting from r.Next()
+ r.Unlink(3)
+
+ // Iterate through the remaining ring and print its contents
+ r.Do(func(p any) {
+ fmt.Println(p.(int))
+ })
+
+ // Output:
+ // 0
+ // 4
+ // 5
+}
diff --git a/src/container/ring/ring.go b/src/container/ring/ring.go
new file mode 100644
index 0000000..268670b
--- /dev/null
+++ b/src/container/ring/ring.go
@@ -0,0 +1,136 @@
+// Copyright 2009 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.
+
+// Package ring implements operations on circular lists.
+package ring
+
+// A Ring is an element of a circular list, or ring.
+// Rings do not have a beginning or end; a pointer to any ring element
+// serves as reference to the entire ring. Empty rings are represented
+// as nil Ring pointers. The zero value for a Ring is a one-element
+// ring with a nil Value.
+type Ring struct {
+ next, prev *Ring
+ Value any // for use by client; untouched by this library
+}
+
+func (r *Ring) init() *Ring {
+ r.next = r
+ r.prev = r
+ return r
+}
+
+// Next returns the next ring element. r must not be empty.
+func (r *Ring) Next() *Ring {
+ if r.next == nil {
+ return r.init()
+ }
+ return r.next
+}
+
+// Prev returns the previous ring element. r must not be empty.
+func (r *Ring) Prev() *Ring {
+ if r.next == nil {
+ return r.init()
+ }
+ return r.prev
+}
+
+// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
+// in the ring and returns that ring element. r must not be empty.
+func (r *Ring) Move(n int) *Ring {
+ if r.next == nil {
+ return r.init()
+ }
+ switch {
+ case n < 0:
+ for ; n < 0; n++ {
+ r = r.prev
+ }
+ case n > 0:
+ for ; n > 0; n-- {
+ r = r.next
+ }
+ }
+ return r
+}
+
+// New creates a ring of n elements.
+func New(n int) *Ring {
+ if n <= 0 {
+ return nil
+ }
+ r := new(Ring)
+ p := r
+ for i := 1; i < n; i++ {
+ p.next = &Ring{prev: p}
+ p = p.next
+ }
+ p.next = r
+ r.prev = p
+ return r
+}
+
+// Link connects ring r with ring s such that r.Next()
+// becomes s and returns the original value for r.Next().
+// r must not be empty.
+//
+// If r and s point to the same ring, linking
+// them removes the elements between r and s from the ring.
+// The removed elements form a subring and the result is a
+// reference to that subring (if no elements were removed,
+// the result is still the original value for r.Next(),
+// and not nil).
+//
+// If r and s point to different rings, linking
+// them creates a single ring with the elements of s inserted
+// after r. The result points to the element following the
+// last element of s after insertion.
+func (r *Ring) Link(s *Ring) *Ring {
+ n := r.Next()
+ if s != nil {
+ p := s.Prev()
+ // Note: Cannot use multiple assignment because
+ // evaluation order of LHS is not specified.
+ r.next = s
+ s.prev = r
+ n.prev = p
+ p.next = n
+ }
+ return n
+}
+
+// Unlink removes n % r.Len() elements from the ring r, starting
+// at r.Next(). If n % r.Len() == 0, r remains unchanged.
+// The result is the removed subring. r must not be empty.
+func (r *Ring) Unlink(n int) *Ring {
+ if n <= 0 {
+ return nil
+ }
+ return r.Link(r.Move(n + 1))
+}
+
+// Len computes the number of elements in ring r.
+// It executes in time proportional to the number of elements.
+func (r *Ring) Len() int {
+ n := 0
+ if r != nil {
+ n = 1
+ for p := r.Next(); p != r; p = p.next {
+ n++
+ }
+ }
+ return n
+}
+
+// Do calls function f on each element of the ring, in forward order.
+// The behavior of Do is undefined if f changes *r.
+func (r *Ring) Do(f func(any)) {
+ if r != nil {
+ f(r.Value)
+ for p := r.Next(); p != r; p = p.next {
+ f(p.Value)
+ }
+ }
+}
diff --git a/src/container/ring/ring_test.go b/src/container/ring/ring_test.go
new file mode 100644
index 0000000..28acbbc
--- /dev/null
+++ b/src/container/ring/ring_test.go
@@ -0,0 +1,228 @@
+// Copyright 2009 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.
+
+package ring
+
+import (
+ "fmt"
+ "testing"
+)
+
+// For debugging - keep around.
+func dump(r *Ring) {
+ if r == nil {
+ fmt.Println("empty")
+ return
+ }
+ i, n := 0, r.Len()
+ for p := r; i < n; p = p.next {
+ fmt.Printf("%4d: %p = {<- %p | %p ->}\n", i, p, p.prev, p.next)
+ i++
+ }
+ fmt.Println()
+}
+
+func verify(t *testing.T, r *Ring, N int, sum int) {
+ // Len
+ n := r.Len()
+ if n != N {
+ t.Errorf("r.Len() == %d; expected %d", n, N)
+ }
+
+ // iteration
+ n = 0
+ s := 0
+ r.Do(func(p any) {
+ n++
+ if p != nil {
+ s += p.(int)
+ }
+ })
+ if n != N {
+ t.Errorf("number of forward iterations == %d; expected %d", n, N)
+ }
+ if sum >= 0 && s != sum {
+ t.Errorf("forward ring sum = %d; expected %d", s, sum)
+ }
+
+ if r == nil {
+ return
+ }
+
+ // connections
+ if r.next != nil {
+ var p *Ring // previous element
+ for q := r; p == nil || q != r; q = q.next {
+ if p != nil && p != q.prev {
+ t.Errorf("prev = %p, expected q.prev = %p\n", p, q.prev)
+ }
+ p = q
+ }
+ if p != r.prev {
+ t.Errorf("prev = %p, expected r.prev = %p\n", p, r.prev)
+ }
+ }
+
+ // Next, Prev
+ if r.Next() != r.next {
+ t.Errorf("r.Next() != r.next")
+ }
+ if r.Prev() != r.prev {
+ t.Errorf("r.Prev() != r.prev")
+ }
+
+ // Move
+ if r.Move(0) != r {
+ t.Errorf("r.Move(0) != r")
+ }
+ if r.Move(N) != r {
+ t.Errorf("r.Move(%d) != r", N)
+ }
+ if r.Move(-N) != r {
+ t.Errorf("r.Move(%d) != r", -N)
+ }
+ for i := 0; i < 10; i++ {
+ ni := N + i
+ mi := ni % N
+ if r.Move(ni) != r.Move(mi) {
+ t.Errorf("r.Move(%d) != r.Move(%d)", ni, mi)
+ }
+ if r.Move(-ni) != r.Move(-mi) {
+ t.Errorf("r.Move(%d) != r.Move(%d)", -ni, -mi)
+ }
+ }
+}
+
+func TestCornerCases(t *testing.T) {
+ var (
+ r0 *Ring
+ r1 Ring
+ )
+ // Basics
+ verify(t, r0, 0, 0)
+ verify(t, &r1, 1, 0)
+ // Insert
+ r1.Link(r0)
+ verify(t, r0, 0, 0)
+ verify(t, &r1, 1, 0)
+ // Insert
+ r1.Link(r0)
+ verify(t, r0, 0, 0)
+ verify(t, &r1, 1, 0)
+ // Unlink
+ r1.Unlink(0)
+ verify(t, &r1, 1, 0)
+}
+
+func makeN(n int) *Ring {
+ r := New(n)
+ for i := 1; i <= n; i++ {
+ r.Value = i
+ r = r.Next()
+ }
+ return r
+}
+
+func sumN(n int) int { return (n*n + n) / 2 }
+
+func TestNew(t *testing.T) {
+ for i := 0; i < 10; i++ {
+ r := New(i)
+ verify(t, r, i, -1)
+ }
+ for i := 0; i < 10; i++ {
+ r := makeN(i)
+ verify(t, r, i, sumN(i))
+ }
+}
+
+func TestLink1(t *testing.T) {
+ r1a := makeN(1)
+ var r1b Ring
+ r2a := r1a.Link(&r1b)
+ verify(t, r2a, 2, 1)
+ if r2a != r1a {
+ t.Errorf("a) 2-element link failed")
+ }
+
+ r2b := r2a.Link(r2a.Next())
+ verify(t, r2b, 2, 1)
+ if r2b != r2a.Next() {
+ t.Errorf("b) 2-element link failed")
+ }
+
+ r1c := r2b.Link(r2b)
+ verify(t, r1c, 1, 1)
+ verify(t, r2b, 1, 0)
+}
+
+func TestLink2(t *testing.T) {
+ var r0 *Ring
+ r1a := &Ring{Value: 42}
+ r1b := &Ring{Value: 77}
+ r10 := makeN(10)
+
+ r1a.Link(r0)
+ verify(t, r1a, 1, 42)
+
+ r1a.Link(r1b)
+ verify(t, r1a, 2, 42+77)
+
+ r10.Link(r0)
+ verify(t, r10, 10, sumN(10))
+
+ r10.Link(r1a)
+ verify(t, r10, 12, sumN(10)+42+77)
+}
+
+func TestLink3(t *testing.T) {
+ var r Ring
+ n := 1
+ for i := 1; i < 10; i++ {
+ n += i
+ verify(t, r.Link(New(i)), n, -1)
+ }
+}
+
+func TestUnlink(t *testing.T) {
+ r10 := makeN(10)
+ s10 := r10.Move(6)
+
+ sum10 := sumN(10)
+
+ verify(t, r10, 10, sum10)
+ verify(t, s10, 10, sum10)
+
+ r0 := r10.Unlink(0)
+ verify(t, r0, 0, 0)
+
+ r1 := r10.Unlink(1)
+ verify(t, r1, 1, 2)
+ verify(t, r10, 9, sum10-2)
+
+ r9 := r10.Unlink(9)
+ verify(t, r9, 9, sum10-2)
+ verify(t, r10, 9, sum10-2)
+}
+
+func TestLinkUnlink(t *testing.T) {
+ for i := 1; i < 4; i++ {
+ ri := New(i)
+ for j := 0; j < i; j++ {
+ rj := ri.Unlink(j)
+ verify(t, rj, j, -1)
+ verify(t, ri, i-j, -1)
+ ri.Link(rj)
+ verify(t, ri, i, -1)
+ }
+ }
+}
+
+// Test that calling Move() on an empty Ring initializes it.
+func TestMoveEmptyRing(t *testing.T) {
+ var r Ring
+
+ r.Move(1)
+ verify(t, &r, 1, 0)
+}