This commit is contained in:
Brian Buller 2016-09-12 17:41:46 -05:00
parent 2f99878b09
commit 1415b3ea84
10 changed files with 1190 additions and 1 deletions

View File

@ -1 +1 @@
hexadecimal binary-search-tree

View File

@ -0,0 +1,77 @@
# Kindergarten Garden
Write a program that, given a diagram, can tell you which plants each child in the kindergarten class is responsible for.
The kindergarten class is learning about growing plants. The teachers
thought it would be a good idea to give them actual seeds, plant them in
actual dirt, and grow actual plants.
They've chosen to grow grass, clover, radishes, and violets.
To this end, they've put little styrofoam cups along the window sills,
and planted one type of plant in each cup, choosing randomly from the
available types of seeds.
```plain
[window][window][window]
........................ # each dot represents a styrofoam cup
........................
```
There are 12 children in the class:
- Alice, Bob, Charlie, David,
- Eve, Fred, Ginny, Harriet,
- Ileana, Joseph, Kincaid, and Larry.
Each child gets 4 cups, two on each row. The children are assigned to
cups in alphabetical order.
The following diagram represents Alice's plants:
```plain
[window][window][window]
VR......................
RG......................
```
So in the row nearest the window, she has a violet and a radish; in the
row behind that, she has a radish and some grass.
Your program will be given the plants from left-to-right starting with
the row nearest the windows. From this, it should be able to determine
which plants belong to which students.
For example, if it's told that the garden looks like so:
```plain
[window][window][window]
VRCGVVRVCGGCCGVRGCVCGCGV
VRCCCGCRRGVCGCRVVCVGCGCV
```
Then if asked for Alice's plants, it should provide:
- Violets, radishes, violets, radishes
While asking for Bob's plants would yield:
- Clover, grass, clover, clover
To run the tests simply run the command `go test` in the exercise directory.
If the test suite contains benchmarks, you can run these with the `-bench`
flag:
go test -bench .
For more detailed info about the Go track see the [help
page](http://exercism.io/languages/go).
## Source
Random musings during airplane trip. [http://jumpstartlab.com](http://jumpstartlab.com)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

View File

@ -0,0 +1,93 @@
package kindergarten
import (
"errors"
"sort"
"strings"
)
type Garden struct {
children []string
rows []string
}
func NewGarden(diagram string, children []string) (*Garden, error) {
var err error
g := new(Garden)
g.rows = strings.Split(diagram[1:], "\n")
g.children = children
if diagram[0] != '\n' {
err = errors.New("Wrong diagram format")
} else if len(g.rows) == 0 {
err = errors.New("No rows")
} else if len(g.children) == 0 {
err = errors.New("No children")
} else if len(g.rows[0]) != len(g.rows[1]) {
err = errors.New("Mismatched rows")
} else {
for i := range g.rows {
if len(g.rows[i])%2 != 0 {
err = errors.New("Odd number of cups")
break
}
for j := range g.rows[i] {
tst := g.rows[i][j]
switch tst {
case 'R':
case 'C':
case 'G':
case 'V':
default:
err = errors.New("Invalid Cup Codes")
}
}
if err != nil {
break
}
}
for i := range g.children {
if i > 0 {
if g.children[i] == g.children[i-1] {
err = errors.New("Duplicate Children")
break
}
}
}
}
return g, err
}
func (g *Garden) Plants(child string) ([]string, bool) {
var i int
var ret []string
var found bool
c := g.children
sort.Strings(c)
for i = 0; i < len(c); i++ {
if c[i] == child {
found = true
break
}
}
if !found || len(g.rows) == 0 || len(g.rows[0]) <= 2*i {
return ret, found
}
for j := range []int{0, 1} {
for k := range []int{0, 1} {
switch g.rows[j][(i*2 + k)] {
case 'G':
ret = append(ret, "grass")
case 'C':
ret = append(ret, "clover")
case 'R':
ret = append(ret, "radishes")
case 'V':
ret = append(ret, "violets")
}
}
}
return ret, found
}
// kindergarten_garden_test.go:140: Garden 6 lookup Patricia =
// [radishes radishes grass clover], want [violets clover radishes violets].

View File

@ -0,0 +1,211 @@
// Kindergarten garden
//
// You must define a type Garden with constructor
//
// func NewGarden(diagram string, children []string) (*Garden, error)
//
// and method
//
// func (g *Garden) Plants(child string) ([]string, bool)
//
// The diagram argument starts each row with a '\n'. This allows Go's
// raw string literals to present diagrams in source code nicely as two
// rows flush left, for example,
//
// diagram := `
// VVCCGG
// VVCCGG`
package kindergarten
import (
"reflect"
"sort"
"testing"
)
type lookup struct {
child string
plants []string
ok bool
}
type gardenTest struct {
number int
diagram string
children []string
ok bool
lookups []lookup
}
var tests = []gardenTest{
{1, `
RC
GG`, []string{"Alice"}, true, []lookup{
{"Alice", []string{"radishes", "clover", "grass", "grass"}, true},
}},
{2, `
VC
RC`, []string{"Alice"}, true, []lookup{
{"Alice", []string{"violets", "clover", "radishes", "clover"}, true},
}},
{3, `
VVCG
VVRC`, []string{"Alice", "Bob"}, true, []lookup{
{"Bob", []string{"clover", "grass", "radishes", "clover"}, true},
}},
{4, `
VVCCGG
VVCCGG`, []string{"Alice", "Bob", "Charlie"}, true, []lookup{
{"Bob", []string{"clover", "clover", "clover", "clover"}, true},
{"Charlie", []string{"grass", "grass", "grass", "grass"}, true},
}},
test5, // full garden test
test6, // out of order names test
// failure tests
{7, "RC\nGG", []string{"Alice"}, false, nil}, // wrong diagram format
{8, `
RCCC
GG`, []string{""}, false, nil}, // mismatched rows
{9, `
RCC
GGC`, []string{"Alice"}, false, nil}, // odd number of cups
{10, `
RCCC
GGCC`, []string{"Alice", "Alice"}, false, nil}, // duplicate name
{11, `
rc
gg`, []string{"Alice"}, false, nil}, // invaid cup codes
{12, `
RC
GG`, []string{"Alice"}, true, []lookup{ // lookup invalid name
{"Bob", []string{"radishes", "clover", "grass", "grass"}, false},
}},
}
// full garden test
var test5 = gardenTest{5, `
VRCGVVRVCGGCCGVRGCVCGCGV
VRCCCGCRRGVCGCRVVCVGCGCV`, []string{
"Alice", "Bob", "Charlie", "David", "Eve", "Fred",
"Ginny", "Harriet", "Ileana", "Joseph", "Kincaid", "Larry"}, true, []lookup{
{"Alice", []string{"violets", "radishes", "violets", "radishes"}, true},
{"Bob", []string{"clover", "grass", "clover", "clover"}, true},
{"Charlie", []string{"violets", "violets", "clover", "grass"}, true},
{"David", []string{"radishes", "violets", "clover", "radishes"}, true},
{"Eve", []string{"clover", "grass", "radishes", "grass"}, true},
{"Fred", []string{"grass", "clover", "violets", "clover"}, true},
{"Ginny", []string{"clover", "grass", "grass", "clover"}, true},
{"Harriet", []string{"violets", "radishes", "radishes", "violets"}, true},
{"Ileana", []string{"grass", "clover", "violets", "clover"}, true},
{"Joseph", []string{"violets", "clover", "violets", "grass"}, true},
{"Kincaid", []string{"grass", "clover", "clover", "grass"}, true},
{"Larry", []string{"grass", "violets", "clover", "violets"}, true},
}}
// out of order names test
var (
test6names = []string{"Samantha", "Patricia", "Xander", "Roger"}
test6 = gardenTest{6, `
VCRRGVRG
RVGCCGCV`, append([]string{}, test6names...), true, []lookup{
{"Patricia", []string{"violets", "clover", "radishes", "violets"}, true},
{"Roger", []string{"radishes", "radishes", "grass", "clover"}, true},
{"Samantha", []string{"grass", "violets", "clover", "grass"}, true},
{"Xander", []string{"radishes", "grass", "clover", "violets"}, true},
}}
)
func TestGarden(t *testing.T) {
for _, test := range tests {
g, err := NewGarden(test.diagram, test.children)
switch {
case err != nil:
if test.ok {
t.Fatalf("NewGarden test %d returned error %q. Error not expected.",
test.number, err)
}
case !test.ok:
t.Fatalf("NewGarden test %d error = %v. Expected error.",
test.number, err)
}
for _, l := range test.lookups {
switch plants, ok := g.Plants(l.child); {
case ok != l.ok:
t.Fatalf("Garden %d lookup %s returned ok = %t, want %t.",
test.number, l.child, ok, l.ok)
case ok && !reflect.DeepEqual(plants, l.plants):
t.Fatalf("Garden %d lookup %s = %v, want %v.",
test.number, l.child, plants, l.plants)
}
}
}
}
// The lazy way to meet the alphabetizing requirement is with sort.Strings
// on the argument slice. That's an in-place sort though and it's bad practice
// to have a side effect.
func TestNamesNotModified(t *testing.T) {
cp := append([]string{}, test6names...)
_, err := NewGarden(test6.diagram, cp)
if err != nil {
t.Skip("TestNamesNotModified requires valid garden")
}
if !reflect.DeepEqual(cp, test6names) {
t.Fatalf("NewGarden modified children argment. " +
"Arguments should not be modified.")
}
sort.Strings(cp)
if reflect.DeepEqual(cp, test6names) {
t.Skip("TestNamesNotModified requires names out of order")
}
}
// A test taken from the Ruby tests. It checks that Garden objects
// are self-contained and do not rely on package variables.
func TestTwoGardens(t *testing.T) {
diagram := `
VCRRGVRG
RVGCCGCV`
g1, err1 := NewGarden(diagram, []string{"Alice", "Bob", "Charlie", "Dan"})
g2, err2 := NewGarden(diagram, []string{"Bob", "Charlie", "Dan", "Erin"})
if err1 != nil || err2 != nil {
t.Skip("Two garden test needs valid gardens")
}
tf := func(g *Garden, n int, child string, expPlants []string) {
switch plants, ok := g.Plants(child); {
case !ok:
t.Skip("Garden %d lookup %s returned ok = false, want true.",
n, child)
case !reflect.DeepEqual(plants, expPlants):
t.Fatalf("Garden %d lookup %s = %v, want %v.",
n, child, plants, expPlants)
}
}
tf(g1, 1, "Bob", []string{"radishes", "radishes", "grass", "clover"})
tf(g2, 2, "Bob", []string{"violets", "clover", "radishes", "violets"})
tf(g1, 1, "Charlie", []string{"grass", "violets", "clover", "grass"})
tf(g2, 2, "Charlie", []string{"radishes", "radishes", "grass", "clover"})
}
func BenchmarkNewGarden(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range tests {
NewGarden(test.diagram, test.children)
}
}
}
func BenchmarkGarden_Plants(b *testing.B) {
g, err := NewGarden(test5.diagram, test5.children)
if err != nil {
b.Skip("BenchmarkGarden_Plants requires valid garden")
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, l := range test5.lookups {
g.Plants(l.child)
}
}
}

32
go/paasio/README.md Normal file
View File

@ -0,0 +1,32 @@
# Paasio
Write a program that reports network IO statistics
You are writing a [PaaS][], and you need a way to bill customers based
on network and filesystem usage.
Create a wrapper for network connections and files that can report IO
statistics. The wrapper must report:
- The total number of bytes read/written.
- The total number of read/write operations.
[PaaS]: http://en.wikipedia.org/wiki/Platform_as_a_service
To run the tests simply run the command `go test` in the exercise directory.
If the test suite contains benchmarks, you can run these with the `-bench`
flag:
go test -bench .
For more detailed info about the Go track see the [help
page](http://exercism.io/languages/go).
## Source
Brian Matsuo [https://github.com/bmatsuo](https://github.com/bmatsuo)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

40
go/paasio/interface.go Normal file
View File

@ -0,0 +1,40 @@
package paasio
import "io"
// ReadCounter is an interface describing objects that can be read from,
// and that can count the number of times they have been read from.
//
// If multiple goroutines concurrently call Read, implementations are not
// required to provide any guarantees about interleaving of the Read calls.
// However, implementations MUST guarantee that calls to ReadCount always return
// correct results even in the presence of concurrent Read calls.
type ReadCounter interface {
io.Reader
// ReadCount returns the total number of bytes successfully read along
// with the total number of calls to Read().
ReadCount() (n int64, nops int)
}
// WriteCounter is an interface describing objects that can be written to,
// and that can count the number of times they have been written to.
//
// If multiple goroutines concurrently call Write, implementations are not
// required to provide any guarantees about interleaving of the Write calls.
// However, implementations MUST guarantee that calls to WriteCount always return
// correct results even in the presence of concurrent Write calls.
type WriteCounter interface {
io.Writer
// WriteCount returns the total number of bytes successfully written along
// with the total number of calls to Write().
WriteCount() (n int64, nops int)
}
// ReadWriteCounter is the union of ReadCounter and WriteCounter.
//
// All guarantees that apply to either of ReadCounter or WriteCounter
// also apply to ReadWriteCounter.
type ReadWriteCounter interface {
ReadCounter
WriteCounter
}

223
go/paasio/paasio_test.go Normal file
View File

@ -0,0 +1,223 @@
package paasio
import (
"bytes"
"crypto/rand"
"io"
"runtime"
"strings"
"sync"
"testing"
"time"
)
// testVersion identifies the API tested by the test program.
const targetTestVersion = 3
func TestMultiThreaded(t *testing.T) {
if testVersion != targetTestVersion {
t.Errorf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
}
mincpu := 2
minproc := 2
ncpu := runtime.NumCPU()
if ncpu < mincpu {
t.Fatalf("at least %d cpu cores are required", mincpu)
}
nproc := runtime.GOMAXPROCS(0)
if nproc < minproc {
t.Errorf("at least %d threads are required; rerun the tests", minproc)
t.Errorf("")
t.Errorf("\tgo test -cpu %d ...", minproc)
}
}
// this test could be improved to test that error conditions are preserved.
func testWrite(t *testing.T, writer func(io.Writer) WriteCounter) {
for i, test := range []struct {
writes []string
}{
{nil},
{[]string{""}},
{[]string{"I", " ", "never met ", "", "a gohper"}},
} {
var buf bytes.Buffer
buft := writer(&buf)
for _, s := range test.writes {
n, err := buft.Write([]byte(s))
if err != nil {
t.Errorf("test %d: Write(%q) unexpected error: %v", i, s, err)
continue
}
if n != len(s) {
t.Errorf("test %d: Write(%q) unexpected number of bytes written: %v", i, s, n)
continue
}
}
out := buf.String()
if out != strings.Join(test.writes, "") {
t.Errorf("test %d: unexpected content in underlying writer: %q", i, out)
}
}
}
func TestWriteWriter(t *testing.T) {
testWrite(t, NewWriteCounter)
}
func TestWriteReadWriter(t *testing.T) {
testWrite(t, func(w io.Writer) WriteCounter {
var r nopReader
return NewReadWriteCounter(readWriter{r, w})
})
}
// this test could be improved to test exact number of operations as well as
// ensure that error conditions are preserved.
func testRead(t *testing.T, reader func(io.Reader) ReadCounter) {
chunkLen := 10 << 20 // 10MB
orig := make([]byte, 10<<20)
_, err := rand.Read(orig)
if err != nil {
t.Fatalf("error reading random data")
}
buf := bytes.NewBuffer(orig)
rc := reader(buf)
var obuf bytes.Buffer
ncopy, err := io.Copy(&obuf, rc)
if err != nil {
t.Fatalf("error reading: %v", err)
}
if ncopy != int64(chunkLen) {
t.Fatalf("copied %d bytes instead of %d", ncopy, chunkLen)
}
if string(orig) != obuf.String() {
t.Fatalf("unexpected output from Read()")
}
n, nops := rc.ReadCount()
if n != int64(chunkLen) {
t.Fatalf("reported %d bytes read instead of %d", n, chunkLen)
}
if nops < 2 {
t.Fatalf("unexpected number of reads: %v", nops)
}
}
func TestReadReader(t *testing.T) {
testRead(t, NewReadCounter)
}
func TestReadReadWriter(t *testing.T) {
testRead(t, func(r io.Reader) ReadCounter {
var w nopWriter
return NewReadWriteCounter(readWriter{r, w})
})
}
func testReadTotal(t *testing.T, rc ReadCounter) {
numGo := 8000
numBytes := 50
totalBytes := int64(numGo) * int64(numBytes)
p := make([]byte, numBytes)
t.Logf("Calling Read() for %d*%d=%d bytes", numGo, numBytes, totalBytes)
wg := new(sync.WaitGroup)
wg.Add(numGo)
start := make(chan struct{})
for i := 0; i < numGo; i++ {
go func() {
<-start
rc.Read(p)
wg.Done()
}()
}
close(start)
wg.Wait()
n, nops := rc.ReadCount()
if n != totalBytes {
t.Errorf("expected %d bytes read; %d bytes reported", totalBytes, n)
}
if nops != numGo {
t.Errorf("expected %d read operations; %d operations reported", numGo, nops)
}
}
func TestReadTotalReader(t *testing.T) {
var r nopReader
testReadTotal(t, NewReadCounter(r))
}
func TestReadTotalReadWriter(t *testing.T) {
var rw nopReadWriter
testReadTotal(t, NewReadWriteCounter(rw))
}
func testWriteTotal(t *testing.T, wt WriteCounter) {
numGo := 8000
numBytes := 50
totalBytes := int64(numGo) * int64(numBytes)
p := make([]byte, numBytes)
t.Logf("Calling Write() with %d*%d=%d bytes", numGo, numBytes, totalBytes)
wg := new(sync.WaitGroup)
wg.Add(numGo)
start := make(chan struct{})
for i := 0; i < numGo; i++ {
go func() {
<-start
wt.Write(p)
wg.Done()
}()
}
close(start)
wg.Wait()
n, nops := wt.WriteCount()
if n != totalBytes {
t.Errorf("expected %d bytes written; %d bytes reported", totalBytes, n)
}
if nops != numGo {
t.Errorf("expected %d write operations; %d operations reported", numGo, nops)
}
}
func TestWriteTotalWriter(t *testing.T) {
var w nopWriter
testWriteTotal(t, NewWriteCounter(w))
}
func TestWriteTotalReadWriter(t *testing.T) {
var rw nopReadWriter
testWriteTotal(t, NewReadWriteCounter(rw))
}
type nopWriter struct{ error }
func (w nopWriter) Write(p []byte) (int, error) {
time.Sleep(1)
if w.error != nil {
return 0, w.error
}
return len(p), nil
}
type nopReader struct{ error }
func (r nopReader) Read(p []byte) (int, error) {
time.Sleep(1)
if r.error != nil {
return 0, r.error
}
return len(p), nil
}
type nopReadWriter struct {
nopReader
nopWriter
}
type readWriter struct {
io.Reader
io.Writer
}

100
go/simple-cipher/README.md Normal file
View File

@ -0,0 +1,100 @@
# Simple Cipher
Implement a simple shift cipher like Caesar and a more secure substitution cipher
## Step 1
"If he had anything confidential to say, he wrote it in cipher, that is,
by so changing the order of the letters of the alphabet, that not a word
could be made out. If anyone wishes to decipher these, and get at their
meaning, he must substitute the fourth letter of the alphabet, namely D,
for A, and so with the others."
—Suetonius, Life of Julius Caesar
Ciphers are very straight-forward algorithms that allow us to render
text less readable while still allowing easy deciphering. They are
vulnerable to many forms of cryptoanalysis, but we are lucky that
generally our little sisters are not cryptoanalysts.
The Caesar Cipher was used for some messages from Julius Caesar that
were sent afield. Now Caesar knew that the cipher wasn't very good, but
he had one ally in that respect: almost nobody could read well. So even
being a couple letters off was sufficient so that people couldn't
recognize the few words that they did know.
Your task is to create a simple shift cipher like the Caesar Cipher.
This image is a great example of the Caesar Cipher: ![Caesar Cipher][1]
For example:
Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". Obscure enough to keep our message secret in transit.
When "ldpdsdqgdehdu" is put into the decode function it would return
the original "iamapandabear" letting your friend read your original
message.
## Step 2
Shift ciphers are no fun though when your kid sister figures it out. Try
amending the code to allow us to specify a key and use that for the
shift distance. This is called a substitution cipher.
Here's an example:
Given the key "aaaaaaaaaaaaaaaaaa", encoding the string "iamapandabear"
would return the original "iamapandabear".
Given the key "ddddddddddddddddd", encoding our string "iamapandabear"
would return the obscured "lpdsdqgdehdu"
In the example above, we've set a = 0 for the key value. So when the
plaintext is added to the key, we end up with the same message coming
out. So "aaaa" is not an ideal key. But if we set the key to "dddd", we
would get the same thing as the Caesar Cipher.
## Step 3
The weakest link in any cipher is the human being. Let's make your
substitution cipher a little more fault tolerant by providing a source
of randomness and ensuring that they key is not composed of numbers or
capital letters.
If someone doesn't submit a key at all, generate a truly random key of
at least 100 characters in length, accessible via Cipher#key (the #
syntax means instance variable)
If the key submitted has capital letters or numbers, throw an
ArgumentError with a message to that effect.
## Extensions
Shift ciphers work by making the text slightly odd, but are vulnerable
to frequency analysis. Substitution ciphers help that, but are still
very vulnerable when the key is short or if spaces are preserved. Later
on you'll see one solution to this problem in the exercise
"crypto-square".
If you want to go farther in this field, the questions begin to be about
how we can exchange keys in a secure way. Take a look at [Diffie-Hellman
on Wikipedia][dh] for one of the first implementations of this scheme.
[1]: http://upload.wikimedia.org/wikipedia/en/7/75/Caesar3.png
[dh]: http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
To run the tests simply run the command `go test` in the exercise directory.
If the test suite contains benchmarks, you can run these with the `-bench`
flag:
go test -bench .
For more detailed info about the Go track see the [help
page](http://exercism.io/languages/go).
## Source
Substitution Cipher at Wikipedia [http://en.wikipedia.org/wiki/Substitution_cipher](http://en.wikipedia.org/wiki/Substitution_cipher)
## Submitting Incomplete Problems
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

143
go/simple-cipher/cipher.go Normal file
View File

@ -0,0 +1,143 @@
package cipher
import (
"regexp"
"strings"
)
// Cipher is an interface for all ciphers
type Cipher interface {
Encode(string) string
Decode(string) string
}
// ShiftCipher is a cipher that shifts all letters a fixed distance
type ShiftCipher struct {
width int
}
// NewShift returns a new ShiftCipher
func NewShift(w int) *ShiftCipher {
if w == 0 || w < -25 || w > 25 {
return nil
}
c := new(ShiftCipher)
c.width = w
return c
}
// Encode encodes a string using the current ShiftCipher
func (c *ShiftCipher) Encode(s string) string {
var ret string
s = strings.ToLower(s)
re := regexp.MustCompile("[^a-z]")
s = re.ReplaceAllString(s, "")
for i := range s {
newChar := s[i] + byte(c.width)
if newChar < 'a' {
newChar = 'z' - ('a' - newChar) + 1
}
if newChar > 'z' {
newChar = 'a' + (newChar - 'z') - 1
}
ret = ret + string(newChar)
}
return ret
}
// Decode decodes a string using the current ShiftCipher
func (c *ShiftCipher) Decode(s string) string {
var ret string
s = strings.ToLower(s)
re := regexp.MustCompile("[^a-z]")
s = re.ReplaceAllString(s, "")
for i := range s {
newChar := s[i] - byte(c.width)
if newChar < 'a' {
newChar = 'z' - ('a' - newChar) + 1
}
if newChar > 'z' {
newChar = 'a' + (newChar - 'z') - 1
}
ret = ret + string(newChar)
}
return ret
}
// A CeasarCipher is a ShiftCipher, just hardcoded at 3
type CeasarCipher struct {
c *ShiftCipher
}
// NewCaesar creates a new CaesarCipher
func NewCaesar() *CeasarCipher {
c := new(CeasarCipher)
c.c = NewShift(3)
return c
}
// Encode just calls the shift cipher in the caesar cipher
func (c *CeasarCipher) Encode(s string) string {
return c.c.Encode(s)
}
// Decode just calls the shift cipher in the caesar cipher
func (c *CeasarCipher) Decode(s string) string {
return c.c.Decode(s)
}
// VigenereCipher a cipher where each character can have a different shift width
type VigenereCipher struct {
key string
}
// Encode encodes the given string
func (c *VigenereCipher) Encode(s string) string {
var ret string
s = strings.ToLower(s)
re := regexp.MustCompile("[^a-z]")
s = re.ReplaceAllString(s, "")
for i := range s {
keyChar := c.key[i%len(c.key)] - 'a'
newChar := s[i] + keyChar
if newChar < 'a' {
newChar = 'z' - ('a' - newChar) + 1
}
if newChar > 'z' {
newChar = 'a' + (newChar - 'z') - 1
}
ret = ret + string(newChar)
}
return ret
}
// Decode decodes the given string
func (c *VigenereCipher) Decode(s string) string {
var ret string
s = strings.ToLower(s)
re := regexp.MustCompile("[^a-z]")
s = re.ReplaceAllString(s, "")
for i := range s {
keyChar := c.key[i%len(c.key)] - 'a'
newChar := s[i] - keyChar
if newChar < 'a' {
newChar = 'z' - ('a' - newChar) + 1
}
if newChar > 'z' {
newChar = 'a' + (newChar - 'z') - 1
}
ret = ret + string(newChar)
}
return ret
}
// NewVigenere returns a new Vigenere Cipher
func NewVigenere(key string) *VigenereCipher {
re := regexp.MustCompile("[^a-z]")
if len(key) < 3 || re.MatchString(key) {
return nil
}
c := new(VigenereCipher)
c.key = key
return c
}

View File

@ -0,0 +1,270 @@
// For Step 1, implement the Caesar cipher, which seems clear enough except
// maybe for figuring out whether to add or subtract.
//
// For Step 2, implement a shift cipher, like Caesar except the shift amount
// is specified as an int. (Each letter of "ddddddddddddddddd", is 'd';
// 'd'-'a' == 3, so the corresponding shift amount is 3.)
//
// Steps 2 and 3 seem to be describing the Vigenère cipher (Google it)
// so let's do that too. The random thing, don't worry about. There is
// no test for that.
//
// API:
//
// type Cipher interface {
// Encode(string) string
// Decode(string) string
// }
// NewCaesar() Cipher
// NewShift(int) Cipher
// NewVigenere(string) Cipher
//
// Interface Cipher is in file cipher.go.
//
// Argument for NewShift must be in the range 1 to 25 or -1 to -25.
// Zero is disallowed. For invalid arguments NewShift returns nil.
//
// Argument for NewVigenere must consist of lower case letters a-z only.
// Values consisting entirely of the letter 'a' are disallowed.
// For invalid arguments NewVigenere returns nil.
package cipher
import (
"fmt"
"testing"
)
// type for testing cipher encoding alone, without requiring any text prep.
type prepped struct {
pt string // prepped text == decoded plain text
ct string // cipher text
}
// +3? -3? Positions vary. Your code just has to pass the tests.
var caesarPrepped = []prepped{
{"iamapandabear", "ldpdsdqgdehdu"},
{"programmingisawesome", "surjudpplqjlvdzhvrph"},
{"todayisholiday", "wrgdblvkrolgdb"},
{"venividivici", "yhqlylglylfl"},
}
func TestCaesarPrepped(t *testing.T) {
c := NewCaesar()
for _, test := range caesarPrepped {
if enc := c.Encode(test.pt); enc != test.ct {
t.Fatalf("Caesar Encode(%q) = %q, want %q.", test.pt, enc, test.ct)
}
}
}
// type for testing implementations of the Cipher interface
type cipherTest struct {
source string // source text, any UTF-8
cipher string // cipher text, result of Encode(st)
plain string // decoded plain text, result of Decode(ct)
}
var caesarTests = []cipherTest{
{"Go, go, gophers", "jrjrjrskhuv", "gogogophers"},
{"I am a panda bear.", "ldpdsdqgdehdu", "iamapandabear"},
{"Programming is AWESOME!", "surjudpplqjlvdzhvrph", "programmingisawesome"},
{"today is holiday", "wrgdblvkrolgdb", "todayisholiday"},
{"Twas the night before Christmas",
"wzdvwkhqljkwehiruhfkulvwpdv",
"twasthenightbeforechristmas"},
{"venividivici", "yhqlylglylfl", "venividivici"},
{" -- @#!", "", ""},
{"", "", ""},
}
func TestCaesar(t *testing.T) {
testCipher("Caesar", NewCaesar(), caesarTests, t)
}
func testCipher(name string, c Cipher, tests []cipherTest, t *testing.T) {
for _, test := range tests {
if enc := c.Encode(test.source); enc != test.cipher {
t.Fatalf("%s Encode(%q) = %q, want %q.",
name, test.plain, enc, test.cipher)
}
if dec := c.Decode(test.cipher); dec != test.plain {
t.Fatalf("%s Decode(%q) = %q, want %q.",
name, test.cipher, dec, test.plain)
}
}
}
var NSATests = []cipherTest{
{"THE ENEMY IS NEAR", "qebbkbjvfpkbxo", "theenemyisnear"},
{"SPIES SEND SECRET MESSAGES",
"pmfbppbkapbzobqjbppxdbp",
"spiessendsecretmessages"},
{"THOMAS JEFFERSON DESIGNED A SUBSTITUTION CIPHER",
"qeljxpgbccboplkabpfdkbaxprypqfqrqflkzfmebo",
"thomasjeffersondesignedasubstitutioncipher"},
{"the quick brown fox jumps over the lazy dog",
"qebnrfzhyoltkclugrjmplsboqebixwvald",
"thequickbrownfoxjumpsoverthelazydog"},
}
func TestShift(t *testing.T) {
// test shift(3) against Caesar cases.
c := NewShift(3)
if c == nil {
t.Fatal("NewShift(3) returned nil, want non-nil Cipher")
}
testCipher("Shift(3)", c, caesarTests, t)
// NSA and WP say Caesar uses shift of -3
c = NewShift(-3)
if c == nil {
t.Fatal("NewShift(-3) returned nil, want non-nil Cipher")
}
testCipher("Shift(-3)", c, NSATests, t)
// invalid shifts
for _, s := range []int{-27, -26, 0, 26, 27} {
if NewShift(s) != nil {
t.Fatal("NewShift(%d) returned non-nil, "+
"Want nil return for invalid argument.", s)
}
}
}
var vtests = []struct {
key string
tests []cipherTest
}{
{"lemon", []cipherTest{{"ATTACKATDAWN", "lxfopvefrnhr", "attackatdawn"}}},
{"abcdefghij", []cipherTest{
{"aaaaaaaaaa", "abcdefghij", "aaaaaaaaaa"},
{"zzzzzzzzzz", "zabcdefghi", "zzzzzzzzzz"},
}},
{"iamapandabear", []cipherTest{
{"I am a panda bear.", "qayaeaagaciai", "iamapandabear"},
}},
{"duxrceqyaimciuucnelkeoxjhdyduu", []cipherTest{
{"Diffie Hellman", "gccwkixcltycv", "diffiehellman"},
}},
{"qgbvno", []cipherTest{
{"cof-FEE, 123!", "sugars", "coffee"},
}},
}
func TestVigenere(t *testing.T) {
for _, test := range vtests {
v := NewVigenere(test.key)
if v == nil {
t.Fatalf("NewVigenere(%q) returned nil, want non-nil Cipher",
test.key)
}
testCipher(fmt.Sprintf("Vigenere(%q)", test.key), v, test.tests, t)
}
// invalid keys
for _, k := range []string{"", "a", "aa", "no way", "CAT", "3", "and,"} {
if NewVigenere(k) != nil {
t.Fatalf("NewVigenere(%q) returned non-nil, "+
"Want nil return for invalid argument.", k)
}
}
}
// Benchmark combined time to run all tests.
// Note other ciphers test different data; times cannot be compared.
func BenchmarkEncodeCaesar(b *testing.B) {
c := NewCaesar()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range caesarTests {
c.Encode(test.source)
}
}
}
func BenchmarkDecodeCaesar(b *testing.B) {
c := NewCaesar()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range caesarTests {
c.Decode(test.cipher)
}
}
}
func BenchmarkNewShift(b *testing.B) {
for i := 0; i < b.N; i++ {
for s := -27; s <= 27; s++ {
NewShift(s)
}
}
}
func BenchmarkEncodeShift(b *testing.B) {
s := NewShift(5)
all := append(caesarTests, NSATests...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range all {
s.Encode(test.source)
}
}
}
func BenchmarkDecodeShift(b *testing.B) {
s := NewShift(5)
all := append(caesarTests, NSATests...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range all {
s.Decode(test.cipher)
}
}
}
func BenchmarkNewVigenere(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range vtests {
NewVigenere(test.key)
}
}
}
func BenchmarkEncVigenere(b *testing.B) {
v := make([]Cipher, len(vtests))
for i, test := range vtests {
v[i] = NewVigenere(test.key)
if v[i] == nil {
b.Skip("Benchmark requires valid Vigenere test cases")
}
}
b.ResetTimer()
for j := 0; j < b.N; j++ {
for i, test := range vtests {
vi := v[i]
for _, test := range test.tests {
vi.Encode(test.source)
}
}
}
}
func BenchmarkDecVigenere(b *testing.B) {
v := make([]Cipher, len(vtests))
for i, test := range vtests {
v[i] = NewVigenere(test.key)
if v[i] == nil {
b.Skip("Benchmark requires valid Vigenere test cases")
}
}
b.ResetTimer()
for j := 0; j < b.N; j++ {
for i, test := range vtests {
vi := v[i]
for _, test := range test.tests {
vi.Decode(test.cipher)
}
}
}
}