Quellcode durchsuchen

K5 Second Variation on Caesar Cipher.

Frederic G. MARAND vor 5 Jahren
Ursprung
Commit
0da091df43

+ 11 - 0
go/.idea/runConfigurations/k5_second_variation_on_caesar_cipher.xml

@@ -0,0 +1,11 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="k5 second variation on caesar cipher" type="GoTestRunConfiguration" factoryName="Go Test">
+    <module name="codewars" />
+    <working_directory value="$PROJECT_DIR$/" />
+    <go_parameters value="-i" />
+    <framework value="gotest" />
+    <kind value="PACKAGE" />
+    <package value="code.osinet.fr/fgm/codewars/kyu5/second_variation_on_caesar_cipher" />
+    <method v="2" />
+  </configuration>
+</component>

+ 42 - 0
go/kyu5/second_variation_on_caesar_cipher/details.md

@@ -0,0 +1,42 @@
+In this country soldiers are poor but they need a certain level of secrecy for 
+their communications so, though they do not know Caesar cypher, they reinvent it
+in the following way.
+
+They use ASCII, without really knowing it, but code only letters a-z and A-Z. 
+Other caracters are kept such as.
+
+They change the "rotate" each new message. This "rotate" is a prefix for their
+message once the message is coded. The prefix is built of 2 letters, the second
+one being shifted from the first one by the "rotate", the first one is the first
+letter, after being downcased, of the uncoded message.
+
+For example if the "rotate" is 2, if the first letter of the uncoded message is 
+'J' the prefix should be 'jl'.
+
+To lessen risk they cut the coded message and the prefix in five pieces since
+they have only five runners and each runner has only one piece.
+
+If possible the message will be evenly split between the five runners; if not
+possible, parts 1, 2, 3, 4 will be longer and part 5 shorter. The fifth part can
+have length equal to the other ones or shorter. If there are many options of how
+to split, choose the option where the fifth part has the longest length, provided
+that the previous conditions are fulfilled. If the last part is the empty string
+don't put this empty string in the resulting array.
+
+For example, if the coded message has a length of 17 the five parts will have
+lengths of 4, 4, 4, 4, 1. The parts 1, 2, 3, 4 are evenly split and the last part
+of length 1 is shorter. If the length is 16 the parts will be of lengths 
+4, 4, 4, 4, 0. Parts 1, 2, 3, 4 are evenly split and the fifth runner will stay
+at home since his part is the empty string and is not kept.
+
+Could you ease them in programming their coding?
+
+Example with shift = 1 :
+
+message : "I should have known that you would have a perfect answer for me!!!"
+
+code : => ["ijJ tipvme ibw", "f lopxo uibu z", "pv xpvme ibwf ", "b qfsgfdu botx", "fs gps nf!!!"]
+
+By the way, maybe could you give them a hand to decode?
+
+Caesar cipher : see Wikipedia

+ 101 - 0
go/kyu5/second_variation_on_caesar_cipher/k.go

@@ -0,0 +1,101 @@
+package kata
+
+import (
+	"math"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+func Chunk(s string, index int) string {
+	if index < 0 || index > 4 {
+		return ""
+	}
+
+	var baseSize, size int
+	if len(s)%5 == 0 {
+		baseSize = len(s) / 5
+		size = baseSize
+	} else {
+		baseSize = int(math.Ceil(float64(len(s)) / 5))
+		if index == 4 {
+			size = len(s) % baseSize
+		} else {
+			size = baseSize
+		}
+	}
+	res := s[index*baseSize : index*baseSize+size]
+	return res
+}
+
+/*
+Prefix builds the encoding prefix.
+
+It assumes s is a string containing at least one rune, and its first rune is an
+ASCII character, upper- or lower-case.
+*/
+func Prefix(s string, shift int) []rune {
+	r := []rune(s)[0]
+	r = unicode.ToLower(r)
+	var off = r - 'a'
+	if off < 0 || off >= 26 {
+		panic("Invalid string")
+	}
+	off += int32(shift)
+	off %= 26
+	r2 := 'a' + off
+	return []rune{r, r2}
+}
+
+func ApplyShift(r rune, shift int) rune {
+	if r >= 'A' && r <= 'Z' {
+		return 'A' + (r-'A'+rune(shift))%26
+	}
+	if r >= 'a' && r <= 'z' {
+		return 'a' + (r-'a'+rune(shift))%26
+	}
+	return r
+}
+
+func Shift(s string) int {
+	if utf8.RuneCountInString(s) < 2 {
+		panic("String too short to decode")
+	}
+	rs := []rune(s)
+	var initial, shifted int = int(rs[0]), int(rs[1])
+	shift := (26 + shifted - initial)%26 // Avoid negatives
+	return shift
+}
+
+func Encode(s string, shift int) []string {
+	const prefixLen = 2
+	src := []rune(s)
+	dst := make([]rune, len(src)+prefixLen)
+	prefix := Prefix(s, shift)
+	n := copy(dst, prefix)
+	if n != prefixLen {
+		panic("copy error on prefix")
+	}
+
+	for i, r := range src {
+		dst[i+prefixLen] = ApplyShift(r, shift)
+	}
+	s = string(dst)
+	res := []string{Chunk(s, 0), Chunk(s, 1), Chunk(s, 2), Chunk(s, 3), Chunk(s, 4)}
+	if len(res[4]) == 0 {
+		res = res[:4]
+	}
+	return res
+}
+
+func Decode(arr []string) string {
+	reverseShift := 26 - Shift(arr[0])
+	s := strings.Join(arr, "")[2:]
+	l := utf8.RuneCountInString(s)
+	runes := make([]rune, l)
+	for i, r := range s {
+		runes[i] = ApplyShift(r, reverseShift)
+	}
+	s = string(runes)
+	return s
+}

+ 85 - 0
go/kyu5/second_variation_on_caesar_cipher/k_test.go

@@ -0,0 +1,85 @@
+package kata_test
+
+import (
+	"strings"
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "code.osinet.fr/fgm/codewars/kyu5/second_variation_on_caesar_cipher"
+)
+
+func doTest1(s string, shift int, exp []string) {
+	var ans = Encode(s, shift)
+	Expect(ans).To(Equal(exp))
+}
+func doTest2(arr []string, exp string) {
+	var ans = Decode(arr)
+	Expect(ans).To(Equal(exp))
+}
+
+var _ = Describe("Tests", func() {
+
+	It("should handle basic cases Encode", func() {
+		var u = "I should have known that you would have a perfect answer for me!!!"
+		var v = []string{"ijJ tipvme ibw", "f lopxo uibu z", "pv xpvme ibwf ", "b qfsgfdu botx", "fs gps nf!!!"}
+		doTest1(u, 1, v)
+
+		u = "abcdefghjuty12"
+		v = []string{"abbc", "defg", "hikv", "uz12"}
+		doTest1(u, 1, v)
+
+	})
+	It("should handle basic cases Decode", func() {
+		var u = "I should have known that you would have a perfect answer for me!!!"
+		var v = []string{"ijJ tipvme ibw", "f lopxo uibu z", "pv xpvme ibwf ", "b qfsgfdu botx", "fs gps nf!!!"}
+		doTest2(v, u)
+
+	})
+})
+
+func TestChunk(t *testing.T) {
+	checks := [...]struct{ len, four, last int }{
+		{45, 9, 9,},
+		{46, 10, 6},
+		{47, 10, 7},
+		{48, 10, 8},
+		{49, 10, 9},
+		{50, 10, 10},
+		{51, 11, 7},
+		{52, 11, 8},
+		{68, 14, 12},
+	}
+	for _, check := range checks {
+		s := strings.Repeat(" ", check.len)
+		actualFour := Chunk(s, 0)
+		actualLast := Chunk(s, 4)
+		if len(actualFour) != check.four {
+			t.Errorf("len %d four expected %d got %d", check.len, check.four, len(actualFour))
+		}
+		if len(actualLast) != check.last {
+			t.Errorf("len %d last expected %d got %d", check.len, check.last, len(actualLast))
+		}
+	}
+}
+
+func TestPrefix(t *testing.T) {
+	checks := [...]struct {
+		s        string
+		shift    int
+		expected string
+	}{
+		{"John", 1, "jk"},
+		{"Yann", 2, "ya"},
+		{"war", 4, "wa"},
+	}
+
+	for _, check := range checks {
+		actual := string(Prefix(check.s, check.shift))
+		if actual != check.expected {
+			t.Errorf("%v by %d: expected %s, got %s", check.s, check.shift, check.expected, actual)
+		}
+	}
+
+}

+ 13 - 0
go/kyu5/second_variation_on_caesar_cipher/second_variation_on_caesar_cipher_suite_test.go

@@ -0,0 +1,13 @@
+package kata_test
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestSecondVariationOnCaesarCipher(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "SecondVariationOnCaesarCipher Suite")
+}