271 lines
6.8 KiB
Go
271 lines
6.8 KiB
Go
// 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)
|
|
}
|
|
}
|
|
}
|
|
}
|