Skip to content

Commit 5275b1f

Browse files
Merge pull request #259 from task4233/add-tests-for-polybius-cipher
Add tests for polybius cipher
2 parents 867a87d + 39efcac commit 5275b1f

File tree

2 files changed

+240
-0
lines changed

2 files changed

+240
-0
lines changed

ciphers/polybius/polybius.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Package polybius is encrypting method with polybius square
2+
// ref: https://en.wikipedia.org/wiki/Polybius_square#Hybrid_Polybius_Playfair_Cipher
3+
package polybius
4+
5+
import (
6+
"fmt"
7+
"math"
8+
"strings"
9+
)
10+
11+
// Polybius is struct having size, characters, and key
12+
type Polybius struct {
13+
size int
14+
characters string
15+
key string
16+
}
17+
18+
// NewPolybius returns a pointer to object of Polybius.
19+
// If the size of "chars" is longer than "size",
20+
// "chars" are truncated to "size".
21+
func NewPolybius(key string, size int, chars string) (*Polybius, error) {
22+
key = strings.ToUpper(key)
23+
chars = strings.ToUpper(chars)[:size]
24+
for idx, ch := range chars {
25+
if strings.Contains(chars[idx+1:], string(ch)) {
26+
return nil, fmt.Errorf("\"chars\" contains same character: %c", ch)
27+
}
28+
}
29+
30+
if len(key) != size*size {
31+
return nil, fmt.Errorf("len(key): %d must be as long as size squared: %d", len(key), size*size)
32+
}
33+
return &Polybius{size, chars, key}, nil
34+
}
35+
36+
// Encrypt encrypts with polybius encryption
37+
func (p *Polybius) Encrypt(text string) (string, error) {
38+
chars := []rune(strings.ToUpper(text))
39+
encryptedText := ""
40+
for _, char := range chars {
41+
encryptedChar, err := p.encipher(char)
42+
if err != nil {
43+
return "", fmt.Errorf("failed encipher: %w", err)
44+
}
45+
encryptedText += encryptedChar
46+
}
47+
return encryptedText, nil
48+
}
49+
50+
// Decrypt decrypts with polybius encryption
51+
func (p *Polybius) Decrypt(text string) (string, error) {
52+
chars := []rune(strings.ToUpper(text))
53+
decryptedText := ""
54+
for i := 0; i < len(chars); i += 2 {
55+
decryptedChar, err := p.decipher(chars[i:int(math.Min(float64(i+2), float64(len(chars))))])
56+
if err != nil {
57+
return "", fmt.Errorf("failed decipher: %w", err)
58+
}
59+
decryptedText += decryptedChar
60+
}
61+
return decryptedText, nil
62+
}
63+
64+
func (p *Polybius) encipher(char rune) (string, error) {
65+
index := strings.IndexRune(p.key, char)
66+
if index < 0 {
67+
return "", fmt.Errorf("%c does not exist in keys", char)
68+
}
69+
row := index / p.size
70+
col := index % p.size
71+
chars := []rune(p.characters)
72+
return string([]rune{chars[row], chars[col]}), nil
73+
}
74+
75+
func (p *Polybius) decipher(chars []rune) (string, error) {
76+
if len(chars) != 2 {
77+
return "", fmt.Errorf("the size of \"chars\" must be even")
78+
}
79+
row := strings.IndexRune(p.characters, chars[0])
80+
if row < 0 {
81+
return "", fmt.Errorf("%c does not exist in characters", chars[0])
82+
}
83+
col := strings.IndexRune(p.characters, chars[1])
84+
if col < 0 {
85+
return "", fmt.Errorf("%c does not exist in characters", chars[1])
86+
}
87+
return string([]rune(p.key)[row*p.size+col]), nil
88+
}

ciphers/polybius/polybius_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package polybius_test
2+
3+
import (
4+
"TheAlgorithms/Go/ciphers/polybius"
5+
"fmt"
6+
"log"
7+
"testing"
8+
)
9+
10+
func ExampleNewPolybius() {
11+
// initialize
12+
const (
13+
plainText = "HogeFugaPiyoSpam"
14+
size = 5
15+
characters = "HogeF"
16+
key = "abcdefghijklmnopqrstuvwxy"
17+
)
18+
p, err := polybius.NewPolybius(key, size, characters)
19+
if err != nil {
20+
log.Fatalf("failed NewPolybius: %v", err)
21+
}
22+
encryptedText, err := p.Encrypt(plainText)
23+
if err != nil {
24+
log.Fatalf("failed Encrypt: %v", err)
25+
}
26+
fmt.Printf("Encrypt=> plainText: %s, encryptedText: %s\n", plainText, encryptedText)
27+
28+
decryptedText, err := p.Decrypt(encryptedText)
29+
if err != nil {
30+
log.Fatalf("failed Decrypt: %v", err)
31+
}
32+
fmt.Printf("Decrypt=> encryptedText: %s, decryptedText: %s\n", encryptedText, decryptedText)
33+
34+
// Output:
35+
// Encrypt=> plainText: HogeFugaPiyoSpam, encryptedText: OGGFOOHFOHFHOOHHEHOEFFGFEEEHHHGG
36+
// Decrypt=> encryptedText: OGGFOOHFOHFHOOHHEHOEFFGFEEEHHHGG, decryptedText: HOGEFUGAPIYOSPAM
37+
}
38+
39+
func TestNewPolybius(t *testing.T) {
40+
t.Parallel()
41+
cases := []struct {
42+
name string
43+
size int
44+
characters string
45+
key string
46+
wantErr string
47+
}{
48+
{
49+
name: "correct initalization", size: 5, characters: "HogeF", key: "abcdefghijklmnopqrstuvwxy", wantErr: "",
50+
},
51+
{
52+
name: "truncate characters", size: 5, characters: "HogeFuga", key: "abcdefghijklmnopqrstuvwxy", wantErr: "",
53+
},
54+
{
55+
name: "invaid key", size: 5, characters: "HogeFuga", key: "abcdefghi", wantErr: "len(key): 9 must be as long as size squared: 25",
56+
},
57+
{
58+
name: "invalid characters", size: 5, characters: "HogeH", key: "abcdefghijklmnopqrstuvwxy", wantErr: "\"chars\" contains same character: H",
59+
},
60+
}
61+
62+
for _, tc := range cases {
63+
t.Run(tc.name, func(t *testing.T) {
64+
_, err := polybius.NewPolybius(tc.key, tc.size, tc.characters)
65+
if err != nil && err.Error() != tc.wantErr {
66+
t.Errorf("failed NewPolybius: %v", err)
67+
}
68+
})
69+
}
70+
}
71+
72+
func TestPolybiusEncrypt(t *testing.T) {
73+
t.Parallel()
74+
cases := []struct {
75+
name string
76+
text string
77+
want string
78+
}{
79+
{
80+
name: "correct encryption", text: "HogeFugaPiyoSpam", want: "OGGFOOHFOHFHOOHHEHOEFFGFEEEHHHGG",
81+
},
82+
{
83+
name: "invalid encryption", text: "hogz", want: "failed encipher: Z does not exist in keys",
84+
},
85+
}
86+
// initialize
87+
const (
88+
size = 5
89+
characters = "HogeF"
90+
key = "abcdefghijklmnopqrstuvwxy"
91+
)
92+
p, err := polybius.NewPolybius(key, size, characters)
93+
if err != nil {
94+
t.Fatalf("failed NewPolybius: %v", err)
95+
}
96+
for _, tc := range cases {
97+
t.Run(tc.name, func(t *testing.T) {
98+
encrypted, err := p.Encrypt(tc.text)
99+
if err != nil {
100+
if err.Error() != tc.want {
101+
t.Errorf("failed Encrypt: %v", err)
102+
}
103+
} else if encrypted != tc.want {
104+
t.Errorf("Encrypt: %v, want: %v", encrypted, tc.want)
105+
}
106+
})
107+
}
108+
}
109+
110+
func TestPolybiusDecrypt(t *testing.T) {
111+
t.Parallel()
112+
cases := []struct {
113+
name string
114+
text string
115+
want string
116+
}{
117+
{
118+
name: "correct decryption", text: "OGGFOOHFOHFHOOHHEHOEFFGFEEEHHHGG", want: "HOGEFUGAPIYOSPAM",
119+
},
120+
{
121+
name: "invalid decryption(position of even number)", text: "hogz", want: "failed decipher: Z does not exist in characters",
122+
},
123+
{
124+
name: "invalid decryption(position of odd number)", text: "hode", want: "failed decipher: D does not exist in characters",
125+
},
126+
{
127+
name: "invalid text size which is odd", text: "hog", want: "failed decipher: the size of \"chars\" must be even",
128+
},
129+
}
130+
// initialize
131+
const (
132+
size = 5
133+
characters = "HogeF"
134+
key = "abcdefghijklmnopqrstuvwxy"
135+
)
136+
p, err := polybius.NewPolybius(key, size, characters)
137+
if err != nil {
138+
t.Fatalf("failed NewPolybius: %v", err)
139+
}
140+
for _, tc := range cases {
141+
t.Run(tc.name, func(t *testing.T) {
142+
encrypted, err := p.Decrypt(tc.text)
143+
if err != nil {
144+
if err.Error() != tc.want {
145+
t.Errorf("failed Encrypt: %v", err)
146+
}
147+
} else if encrypted != tc.want {
148+
t.Errorf("Encrypt: %v, want: %v", encrypted, tc.want)
149+
}
150+
})
151+
}
152+
}

0 commit comments

Comments
 (0)