Resync
This commit is contained in:
parent
2f99878b09
commit
1415b3ea84
@ -1 +1 @@
|
|||||||
hexadecimal
|
binary-search-tree
|
77
go/kindergarten-garden/README.md
Normal file
77
go/kindergarten-garden/README.md
Normal 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.
|
||||||
|
|
93
go/kindergarten-garden/kindergarten.go
Normal file
93
go/kindergarten-garden/kindergarten.go
Normal 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].
|
211
go/kindergarten-garden/kindergarten_garden_test.go
Normal file
211
go/kindergarten-garden/kindergarten_garden_test.go
Normal 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
32
go/paasio/README.md
Normal 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
40
go/paasio/interface.go
Normal 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
223
go/paasio/paasio_test.go
Normal 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
100
go/simple-cipher/README.md
Normal 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
143
go/simple-cipher/cipher.go
Normal 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
|
||||||
|
}
|
270
go/simple-cipher/simple_cipher_test.go
Normal file
270
go/simple-cipher/simple_cipher_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user