From 6d3d25de2100fece265be9e76d1a3bc0a80270aa Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 28 Jul 2009 14:54:49 -0700 Subject: [PATCH] Ring ADT. - 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 | 61 +++++++ src/pkg/container/ring/ring.go | 174 +++++++++++++++++++ src/pkg/container/ring/ring_test.go | 260 ++++++++++++++++++++++++++++ 3 files changed, 495 insertions(+) create mode 100644 src/pkg/container/ring/Makefile create mode 100644 src/pkg/container/ring/ring.go create mode 100644 src/pkg/container/ring/ring_test.go diff --git a/src/pkg/container/ring/Makefile b/src/pkg/container/ring/Makefile new file mode 100644 index 00000000000..931f40524f4 --- /dev/null +++ b/src/pkg/container/ring/Makefile @@ -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 index 00000000000..62dba6f8143 --- /dev/null +++ b/src/pkg/container/ring/ring.go @@ -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 index 00000000000..c2e9d414cdd --- /dev/null +++ b/src/pkg/container/ring/ring_test.go @@ -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); + } + } +}