]> Cypherpunks repositories - gostls13.git/commitdiff
Ring ADT.
authorRobert Griesemer <gri@golang.org>
Tue, 28 Jul 2009 21:54:49 +0000 (14:54 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 28 Jul 2009 21:54:49 +0000 (14:54 -0700)
- Provides analogous functionality to a doubly-linked list
  implementation.
- Completely symmetric set of operations.
- Operations on the ADT do not lead to results that are
  outside the domain of the ADT (closed interface).
- Alternative to container/list.

R=rsc
DELTA=489  (489 added, 0 deleted, 0 changed)
OCL=32284
CL=32323

src/pkg/container/ring/Makefile [new file with mode: 0644]
src/pkg/container/ring/ring.go [new file with mode: 0644]
src/pkg/container/ring/ring_test.go [new file with mode: 0644]

diff --git a/src/pkg/container/ring/Makefile b/src/pkg/container/ring/Makefile
new file mode 100644 (file)
index 0000000..931f405
--- /dev/null
@@ -0,0 +1,61 @@
+# 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.
+
+
+# DO NOT EDIT.  Automatically generated by gobuild.
+# gobuild -m >Makefile
+
+D=/container/
+
+include $(GOROOT)/src/Make.$(GOARCH)
+AR=gopack
+
+default: packages
+
+clean:
+       rm -rf *.[$(OS)] *.a [$(OS)].out _obj
+
+test: packages
+       gotest
+
+coverage: packages
+       gotest
+       6cov -g $$(pwd) | grep -v '_test\.go:'
+
+%.$O: %.go
+       $(GC) -I_obj $*.go
+
+%.$O: %.c
+       $(CC) $*.c
+
+%.$O: %.s
+       $(AS) $*.s
+
+O1=\
+       ring.$O\
+
+
+phases: a1
+_obj$D/ring.a: phases
+
+a1: $(O1)
+       $(AR) grc _obj$D/ring.a ring.$O
+       rm -f $(O1)
+
+
+newpkg: clean
+       mkdir -p _obj$D
+       $(AR) grc _obj$D/ring.a
+
+$(O1): newpkg
+$(O2): a1
+
+nuke: clean
+       rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ring.a
+
+packages: _obj$D/ring.a
+
+install: packages
+       test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D
+       cp _obj$D/ring.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ring.a
diff --git a/src/pkg/container/ring/ring.go b/src/pkg/container/ring/ring.go
new file mode 100644 (file)
index 0000000..62dba6f
--- /dev/null
@@ -0,0 +1,174 @@
+// 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.
+
+// The ring package 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 interface{};  // 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 with ring s such that r.Next(1)
+// becomes s and returns the original value for r.Next(1).
+// 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(1),
+// 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;
+}
+
+
+// Forward returns a channel for forward iteration through a ring.
+// Iteration is undefined if the ring is changed during iteration.
+//
+func (r *Ring) Forward() <-chan *Ring {
+       c := make(chan *Ring);
+       go func() {
+               if r != nil {
+                       c <- r;
+                       for p := r.Next(); p != r; p = p.next {
+                               c <- p;
+                       }
+               }
+               close(c);
+       }();
+       return c;
+}
+
+
+// Backward returns a channel for backward iteration through a ring.
+// Iteration is undefined if the ring is changed during iteration.
+//
+func (r *Ring) Backward() <-chan *Ring {
+       c := make(chan *Ring);
+       go func() {
+               if r != nil {
+                       c <- r;
+                       for p := r.Prev(); p != r; p = p.prev {
+                               c <- p;
+                       }
+               }
+               close(c);
+       }();
+       return c;
+}
diff --git a/src/pkg/container/ring/ring_test.go b/src/pkg/container/ring/ring_test.go
new file mode 100644 (file)
index 0000000..c2e9d41
--- /dev/null
@@ -0,0 +1,260 @@
+// 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 (
+       "container/ring";
+       "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);
+       }
+
+       // forward iteration
+       n = 0;
+       s := 0;
+       for p := range r.Forward() {
+               n++;
+               if p.Value != nil {
+                       s += p.Value.(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);
+       }
+
+       // backward iteration
+       n = 0;
+       s = 0;
+       for p := range r.Backward() {
+               n++;
+               if p.Value != nil {
+                       s += p.Value.(int);
+               }
+       }
+       if n != N {
+               t.Errorf("number of backward iterations == %d; expected %d", n, N);
+       }
+       if sum >= 0 && s != sum {
+               t.Errorf("backward 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 sum(r *Ring) int {
+       s := 0;
+       for p := range r.Forward() {
+               s += p.Value.(int);
+       }
+       return s;
+}
+
+
+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 < 100; 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);
+       sum6 := sumN(6);
+
+       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);
+               }
+       }
+}