From 814470733eced76a827cb9701080231c915a6e4a Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Sat, 29 Apr 2017 21:12:39 -0700 Subject: [PATCH] snapshot. --- sequence/array.go | 14 ++++ sequence/{value_buffer.go => buffer.go} | 2 +- .../{value_buffer_test.go => buffer_test.go} | 41 ++++++---- sequence/generate.go | 14 ++-- sequence/generate_test.go | 2 +- sequence/linear.go | 41 ++++++++++ sequence/random.go | 21 +++++ sequence/sequence.go | 76 ++++++++++++------- sequence/sequence_test.go | 28 ++++++- sequence/time.go | 2 +- 10 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 sequence/array.go rename sequence/{value_buffer.go => buffer.go} (99%) rename sequence/{value_buffer_test.go => buffer_test.go} (83%) create mode 100644 sequence/linear.go create mode 100644 sequence/random.go diff --git a/sequence/array.go b/sequence/array.go new file mode 100644 index 0000000..f0946c7 --- /dev/null +++ b/sequence/array.go @@ -0,0 +1,14 @@ +package sequence + +// Array is a wrapper for an array of floats that implements `ValuesProvider`. +type Array []float64 + +// Len returns the value provider length. +func (a Array) Len() int { + return len(a) +} + +// GetValue returns the value at a given index. +func (a Array) GetValue(index int) float64 { + return a[index] +} diff --git a/sequence/value_buffer.go b/sequence/buffer.go similarity index 99% rename from sequence/value_buffer.go rename to sequence/buffer.go index 44bda60..636f2c5 100644 --- a/sequence/value_buffer.go +++ b/sequence/buffer.go @@ -1,4 +1,4 @@ -package util +package sequence import ( "fmt" diff --git a/sequence/value_buffer_test.go b/sequence/buffer_test.go similarity index 83% rename from sequence/value_buffer_test.go rename to sequence/buffer_test.go index 9b2504e..0da9f4c 100644 --- a/sequence/value_buffer_test.go +++ b/sequence/buffer_test.go @@ -1,4 +1,4 @@ -package util +package sequence import ( "testing" @@ -6,10 +6,10 @@ import ( "github.com/blendlabs/go-assert" ) -func TestValueBuffer(t *testing.T) { +func TestBuffer(t *testing.T) { assert := assert.New(t) - buffer := NewValueBuffer() + buffer := NewBuffer() buffer.Enqueue(1) assert.Equal(1, buffer.Len()) @@ -100,10 +100,10 @@ func TestValueBuffer(t *testing.T) { assert.Zero(buffer.PeekBack()) } -func TestRingBufferClear(t *testing.T) { +func TestBufferClear(t *testing.T) { assert := assert.New(t) - buffer := NewValueBuffer() + buffer := NewBuffer() buffer.Enqueue(1) buffer.Enqueue(1) buffer.Enqueue(1) @@ -121,10 +121,10 @@ func TestRingBufferClear(t *testing.T) { assert.Zero(buffer.PeekBack()) } -func TestRingBufferAsSlice(t *testing.T) { +func TestBufferArray(t *testing.T) { assert := assert.New(t) - buffer := NewValueBuffer() + buffer := NewBuffer() buffer.Enqueue(1) buffer.Enqueue(2) buffer.Enqueue(3) @@ -140,10 +140,10 @@ func TestRingBufferAsSlice(t *testing.T) { assert.Equal(5, contents[4]) } -func TestRingBufferEach(t *testing.T) { +func TestBufferEach(t *testing.T) { assert := assert.New(t) - buffer := NewValueBuffer() + buffer := NewBuffer() for x := 1; x < 17; x++ { buffer.Enqueue(float64(x)) @@ -159,10 +159,10 @@ func TestRingBufferEach(t *testing.T) { assert.Equal(16, called) } -func TestNewValueBuffer(t *testing.T) { +func TestNewBuffer(t *testing.T) { assert := assert.New(t) - empty := NewValueBuffer() + empty := NewBuffer() assert.NotNil(empty) assert.Zero(empty.Len()) assert.Equal(valueBufferDefaultCapacity, empty.Capacity()) @@ -170,13 +170,24 @@ func TestNewValueBuffer(t *testing.T) { assert.Zero(empty.PeekBack()) } -func TestNewValueBufferWithValues(t *testing.T) { +func TestNewBufferWithValues(t *testing.T) { assert := assert.New(t) - values := NewValueBuffer(1, 2, 3, 4) + values := NewBuffer(1, 2, 3, 4, 5) assert.NotNil(values) - assert.Equal(4, values.Len()) + assert.Equal(5, values.Len()) assert.Equal(valueBufferDefaultCapacity, values.Capacity()) assert.Equal(1, values.Peek()) - assert.Equal(4, values.PeekBack()) + assert.Equal(5, values.PeekBack()) +} + +func TestBufferGrowth(t *testing.T) { + assert := assert.New(t) + + values := NewBuffer(1, 2, 3, 4, 5) + for i := 0; i < 1<<10; i++ { + values.Enqueue(float64(i)) + } + + assert.Equal(1<<10-1, values.PeekBack()) } diff --git a/sequence/generate.go b/sequence/generate.go index 2bd1d72..59b63f8 100644 --- a/sequence/generate.go +++ b/sequence/generate.go @@ -1,4 +1,4 @@ -package util +package sequence import ( "math/rand" @@ -18,7 +18,7 @@ type generate struct { } // Values produces an array of floats from [start,end] by optional steps. -func (g generate) Values(start, end float64, steps ...float64) Sequence { +func (g generate) Values(start, end float64, steps ...float64) Seq { var values []float64 step := 1.0 if len(steps) > 0 { @@ -34,22 +34,22 @@ func (g generate) Values(start, end float64, steps ...float64) Sequence { values = append(values, x) } } - return Sequence{Array(values)} + return Seq{Array(values)} } // Random generates a fixed length sequence of random values between (0, scale). -func (g generate) RandomValues(samples int, scale float64) Sequence { +func (g generate) RandomValues(samples int, scale float64) Seq { values := make([]float64, samples) for x := 0; x < samples; x++ { values[x] = g.rnd.Float64() * scale } - return Sequence{Array(values)} + return Seq{Array(values)} } // Random generates a fixed length sequence of random values with a given average, above and below that average by (-scale, scale) -func (g generate) RandomValuesWithAverage(samples int, average, scale float64) Sequence { +func (g generate) RandomValuesWithAverage(samples int, average, scale float64) Seq { values := make([]float64, samples) for x := 0; x < samples; x++ { @@ -57,5 +57,5 @@ func (g generate) RandomValuesWithAverage(samples int, average, scale float64) S values[x] = average + jitter } - return Sequence{Array(values)} + return Seq{Array(values)} } diff --git a/sequence/generate_test.go b/sequence/generate_test.go index 5a1eea4..d59ab11 100644 --- a/sequence/generate_test.go +++ b/sequence/generate_test.go @@ -1,4 +1,4 @@ -package util +package sequence import ( "testing" diff --git a/sequence/linear.go b/sequence/linear.go new file mode 100644 index 0000000..ab10906 --- /dev/null +++ b/sequence/linear.go @@ -0,0 +1,41 @@ +package sequence + +// NewLinear returns a new linear generator. +func NewLinear() *Linear { + return &Linear{} +} + +// Linear is a stepwise generator. +type Linear struct { + offset float64 + limit float64 + step float64 +} + +// Len returns the number of elements in the sequence. +func (lg Linear) Len() int { + return int((lg.limit - lg.offset) / lg.step) +} + +// GetValue returns the value at a given index. +func (lg Linear) GetValue(index int) float64 { + return lg.offset + (float64(index) * lg.step) +} + +// WithOffset sets the offset and returns the linear generator. +func (lg *Linear) WithOffset(offset float64) *Linear { + lg.offset = offset + return lg +} + +// WithLimit sets the step and returns the linear generator. +func (lg *Linear) WithLimit(limit float64) *Linear { + lg.limit = limit + return lg +} + +// WithStep sets the step and returns the linear generator. +func (lg *Linear) WithStep(step float64) *Linear { + lg.step = step + return lg +} diff --git a/sequence/random.go b/sequence/random.go new file mode 100644 index 0000000..85342d6 --- /dev/null +++ b/sequence/random.go @@ -0,0 +1,21 @@ +package sequence + +import "math/rand" + +type Random struct { + rnd *rand.Rand + scale *float64 + average *float64 + len int +} + +func (r *Random) Len() int { + return r.len +} + +func (r *Random) GetValue(_ int) float64 { + if r.scale != nil { + return r.rnd.Float64() * *r.scale + } + return r.rnd.Float64() +} diff --git a/sequence/sequence.go b/sequence/sequence.go index fdc5e71..936efbd 100644 --- a/sequence/sequence.go +++ b/sequence/sequence.go @@ -1,20 +1,20 @@ -package util +package sequence import "math" -// SequenceProvider is a provider for values for a sequence. -type SequenceProvider interface { +// Provider is a provider for values for a sequence. +type Provider interface { Len() int GetValue(int) float64 } -// Sequence is a utility wrapper for sequence providers. -type Sequence struct { - SequenceProvider +// Seq is a utility wrapper for sequence providers. +type Seq struct { + Provider } // Each applies the `mapfn` to all values in the value provider. -func (s Sequence) Each(mapfn func(int, float64)) { +func (s Seq) Each(mapfn func(int, float64)) { for i := 0; i < s.Len(); i++ { mapfn(i, s.GetValue(i)) } @@ -22,29 +22,62 @@ func (s Sequence) Each(mapfn func(int, float64)) { // Map applies the `mapfn` to all values in the value provider, // returning a new sequence. -func (s Sequence) Map(mapfn func(i int, v float64) float64) Sequence { +func (s Seq) Map(mapfn func(i int, v float64) float64) Seq { output := make([]float64, s.Len()) for i := 0; i < s.Len(); i++ { mapfn(i, s.GetValue(i)) } - return Sequence{Array(output)} + return Seq{Array(output)} } -// Average returns the float average of the values in the buffer. -func (s Sequence) Average() float64 { +// FoldLeft collapses a sequence from left to right. +func (s Seq) FoldLeft(mapfn func(i int, v0, v float64) float64) (v0 float64) { + if s.Len() == 0 { + return 0 + } + + v0 = s.GetValue(0) + for i := 1; i < s.Len(); i++ { + v0 = mapfn(i, v0, s.GetValue(i)) + } + return +} + +// FoldRight collapses a sequence from right to left. +func (s Seq) FoldRight(mapfn func(i int, v0, v float64) float64) (v0 float64) { + if s.Len() == 0 { + return 0 + } + + for i := s.Len() - 1; i >= 0; i-- { + v0 = mapfn(i, v0, s.GetValue(i)) + } + return +} + +// Sum adds all the elements of a series together. +func (s Seq) Sum() (accum float64) { if s.Len() == 0 { return 0 } - var accum float64 for i := 0; i < s.Len(); i++ { accum += s.GetValue(i) } - return accum / float64(s.Len()) + return +} + +// Average returns the float average of the values in the buffer. +func (s Seq) Average() float64 { + if s.Len() == 0 { + return 0 + } + + return s.Sum() / float64(s.Len()) } // Variance computes the variance of the buffer. -func (s Sequence) Variance() float64 { +func (s Seq) Variance() float64 { if s.Len() == 0 { return 0 } @@ -60,23 +93,10 @@ func (s Sequence) Variance() float64 { } // StdDev returns the standard deviation. -func (s Sequence) StdDev() float64 { +func (s Seq) StdDev() float64 { if s.Len() == 0 { return 0 } return math.Pow(s.Variance(), 0.5) } - -// Array is a wrapper for an array of floats that implements `ValuesProvider`. -type Array []float64 - -// Len returns the value provider length. -func (a Array) Len() int { - return len(a) -} - -// GetValue returns the value at a given index. -func (a Array) GetValue(index int) float64 { - return a[index] -} diff --git a/sequence/sequence_test.go b/sequence/sequence_test.go index c7d8682..e580e6e 100644 --- a/sequence/sequence_test.go +++ b/sequence/sequence_test.go @@ -1 +1,27 @@ -package util +package sequence + +import ( + "testing" + + assert "github.com/blendlabs/go-assert" +) + +func TestSequenceEach(t *testing.T) { + assert := assert.New(t) + + values := Seq{Array([]float64{1, 2, 3, 4})} + values.Each(func(i int, v float64) { + assert.Equal(i, v) + }) +} + +func TestSequenceMap(t *testing.T) { + assert := assert.New(t) + + values := Seq{Array([]float64{1, 2, 3, 4})} + mapped := values.Map(func(i int, v float64) float64 { + assert.Equal(i, v) + return v * 2 + }) + assert.Equal(4, mapped.Len()) +} diff --git a/sequence/time.go b/sequence/time.go index 693f3fc..c5e1001 100644 --- a/sequence/time.go +++ b/sequence/time.go @@ -1,4 +1,4 @@ -package util +package sequence import ( "time"