Skip to content

Commit

Permalink
Add Prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Adolf committed Feb 19, 2018
1 parent 12b754a commit b6b766a
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 0 deletions.
44 changes: 44 additions & 0 deletions gorkov.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package gorkov

import (
"bytes"
"fmt"
)

// Token is an one element of a markov chain. Usually this is a word or some
// whitespace.
type Token interface {
Expand Down Expand Up @@ -33,6 +38,8 @@ const (
)

var (
// Start is a pseudo token that starts a chain.
Start = NewToken("s", "")
// End is a pseude token that ends a chain.
End = NewToken("e", "")
)
Expand Down Expand Up @@ -73,3 +80,40 @@ func Literal(value string) Token {
func TokensEqual(a, b Token) bool {
return a.Type() == b.Type() && a.Identifier() == b.Identifier()
}

// Prefix is slice of tokens. It is used when selecting the next token while
// generating a new sequence.
type Prefix []Token

// NewPrefix returns a new prefix of the given length. It will consist only of
// Start tokens. It will panic if a number <= 0 is given.
func NewPrefix(n int) Prefix {
if n <= 0 {
panic(fmt.Errorf("invalid prefix length %d", n))
}
p := make(Prefix, n)
for i := range p {
p[i] = Start
}
return p
}

// PrefixKey is a string that identifies a prefix. It can be used as a key in
// a map.
type PrefixKey string

// Key returns a string that can be used in a map to identfy this prefix.
func (p Prefix) Key() PrefixKey {
buf := &bytes.Buffer{}
for _, t := range p {
fmt.Fprintf(buf, "\x00%s\x00%s", t.Type(), t.Identifier()) // nolint: gas
}
return PrefixKey(buf.String())
}

// Shift adds the given token to the end of the prefix and drops the first
// token.
func (p Prefix) Shift(t Token) {
copy(p, p[1:])
p[len(p)-1] = t
}
251 changes: 251 additions & 0 deletions prefix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package gorkov_test

import (
"fmt"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

. "github.com/Patagonicus/gorkov"
)

var _ = Describe("Prefix", func() {
assertPrefixEquals := func(actual, expected Prefix) {
ExpectWithOffset(1, len(actual)).To(Equal(len(expected)))
for i := range actual {
ExpectWithOffset(1, TokensEqual(actual[i], expected[i])).To(BeTrue())
}
}

Describe("Creating a new prefix", func() {
for n := 1; n < 10; n++ {
Context(fmt.Sprintf("Of length %d", n), func() {
length := n
prefix := NewPrefix(length)

It(fmt.Sprintf("Should have length %d", length), func() {
Expect(len(prefix)).To(Equal(length))
})

It("Should only contain Start tokens", func() {
for _, t := range prefix {
Expect(TokensEqual(t, Start)).To(BeTrue(), "a new prefix should only contain Start tokens")
}
})
})
}

Context("Of length 0", func() {
It("Should panic", func() {
Expect(func() { NewPrefix(0) }).To(Panic())
})
})

Context("Of length -1", func() {
It("Should panic", func() {
Expect(func() { NewPrefix(-1) }).To(Panic())
})
})
})

Describe("Shifting tokens", func() {
var (
fooToken Token
barToken Token
)

BeforeEach(func() {
fooToken = Literal("foo")
barToken = Literal("bar")
})

Context("For a prefix of length 1", func() {
It("should replace the token", func() {
prefix := NewPrefix(1)

prefix.Shift(fooToken)
assertPrefixEquals(prefix, Prefix{fooToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{barToken})
})
})
Context("for a prefix of length 2", func() {
It("should drop the first and add the new token at the end", func() {
prefix := NewPrefix(2)

prefix.Shift(fooToken)
assertPrefixEquals(prefix, Prefix{Start, fooToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{fooToken, barToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{barToken, barToken})
})
})
Context("for a prefix of length 5", func() {
It("should drop the first and add the new token at the end", func() {
prefix := NewPrefix(5)

prefix.Shift(fooToken)
assertPrefixEquals(prefix, Prefix{Start, Start, Start, Start, fooToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{Start, Start, Start, fooToken, barToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{Start, Start, fooToken, barToken, barToken})

prefix.Shift(fooToken)
assertPrefixEquals(prefix, Prefix{Start, fooToken, barToken, barToken, fooToken})

prefix.Shift(barToken)
assertPrefixEquals(prefix, Prefix{fooToken, barToken, barToken, fooToken, barToken})

prefix.Shift(fooToken)
assertPrefixEquals(prefix, Prefix{barToken, barToken, fooToken, barToken, fooToken})
})
})
})

Describe("Generating a key", func() {
assertKeyContains := func(p Prefix, s string) {
ExpectWithOffset(1, string(p.Key())).To(ContainSubstring(s))
}

var (
fooToken Token
barToken Token
foobarToken Token
)

BeforeEach(func() {
fooToken = Literal("foo")
barToken = Literal("bar")
foobarToken = Literal("foobar")
})

Context("With a length of 1", func() {
var (
start Prefix
foo Prefix
bar Prefix
foobar Prefix
)

BeforeEach(func() {
start = Prefix{Start}
foo = Prefix{fooToken}
bar = Prefix{barToken}
foobar = Prefix{foobarToken}
})

It("should contain the type of the token", func() {
assertKeyContains(start, Start.Type())
assertKeyContains(foo, fooToken.Type())
assertKeyContains(bar, barToken.Type())
assertKeyContains(foobar, foobarToken.Type())
})

It("should contain the identifier of the token", func() {
assertKeyContains(start, Start.Identifier())
assertKeyContains(foo, fooToken.Identifier())
assertKeyContains(bar, barToken.Identifier())
assertKeyContains(foobar, foobarToken.Identifier())
})

Specify("different prefixes have different keys", func() {
Expect(start.Key()).NotTo(Equal(foo.Key()))
Expect(start.Key()).NotTo(Equal(bar.Key()))
Expect(start.Key()).NotTo(Equal(foobar.Key()))

Expect(foo.Key()).NotTo(Equal(bar.Key()))
Expect(foo.Key()).NotTo(Equal(foobar.Key()))

Expect(bar.Key()).NotTo(Equal(foobar.Key()))
})

Specify("equal prefixes have the same key", func() {
Expect(Prefix{Start}.Key()).To(Equal(start.Key()))
Expect(Prefix{fooToken}.Key()).To(Equal(foo.Key()))
Expect(Prefix{barToken}.Key()).To(Equal(bar.Key()))
Expect(Prefix{foobarToken}.Key()).To(Equal(foobar.Key()))
})
})

Context("With a length of 2", func() {
var (
startStart Prefix
startFoo Prefix
fooBar Prefix
fooFoo Prefix
foobarEnd Prefix
)

BeforeEach(func() {
startStart = Prefix{Start, Start}
startFoo = Prefix{Start, fooToken}
fooBar = Prefix{fooToken, barToken}
fooFoo = Prefix{fooToken, fooToken}
foobarEnd = Prefix{foobarToken, End}
})

It("should contain the type of the first token", func() {
assertKeyContains(startStart, Start.Type())
assertKeyContains(startFoo, Start.Type())
assertKeyContains(fooBar, fooToken.Type())
assertKeyContains(fooFoo, fooToken.Type())
assertKeyContains(foobarEnd, foobarToken.Type())
})

It("should contain the type of the second token", func() {
assertKeyContains(startStart, Start.Type())
assertKeyContains(startFoo, fooToken.Type())
assertKeyContains(fooBar, barToken.Type())
assertKeyContains(fooFoo, fooToken.Type())
assertKeyContains(foobarEnd, End.Type())
})

It("should contain the identifier of the first token", func() {
assertKeyContains(startStart, Start.Identifier())
assertKeyContains(startFoo, Start.Identifier())
assertKeyContains(fooBar, fooToken.Identifier())
assertKeyContains(fooFoo, fooToken.Identifier())
assertKeyContains(foobarEnd, foobarToken.Identifier())
})

It("should contain the identifier of the second token", func() {
assertKeyContains(startStart, Start.Identifier())
assertKeyContains(startFoo, fooToken.Identifier())
assertKeyContains(fooBar, barToken.Identifier())
assertKeyContains(fooFoo, fooToken.Identifier())
assertKeyContains(foobarEnd, End.Identifier())
})

Specify("different prefixes have different keys", func() {
Expect(startStart.Key()).NotTo(Equal(startFoo.Key()))
Expect(startStart.Key()).NotTo(Equal(fooBar.Key()))
Expect(startStart.Key()).NotTo(Equal(fooFoo.Key()))
Expect(startStart.Key()).NotTo(Equal(foobarEnd.Key()))

Expect(startFoo.Key()).NotTo(Equal(fooBar.Key()))
Expect(startFoo.Key()).NotTo(Equal(fooFoo.Key()))
Expect(startFoo.Key()).NotTo(Equal(foobarEnd.Key()))

Expect(fooBar.Key()).NotTo(Equal(fooFoo.Key()))
Expect(fooBar.Key()).NotTo(Equal(foobarEnd.Key()))

Expect(fooFoo.Key()).NotTo(Equal(foobarEnd.Key()))
})

Specify("equal prefixes have the same key", func() {
Expect(Prefix{Start, Start}.Key()).To(Equal(startStart.Key()))
Expect(Prefix{Start, fooToken}.Key()).To(Equal(startFoo.Key()))
Expect(Prefix{fooToken, barToken}.Key()).To(Equal(fooBar.Key()))
Expect(Prefix{fooToken, fooToken}.Key()).To(Equal(fooFoo.Key()))
Expect(Prefix{foobarToken, End}.Key()).To(Equal(foobarEnd.Key()))
})
})
})
})

0 comments on commit b6b766a

Please sign in to comment.