# ericqweinstein's solution

## to Crypto Square in the Go Track

Published at Mar 29 2020 · 0 comments
Instructions
Test suite
Solution

Implement the classic method for composing secret messages called a square code.

Given an English text, output the encoded version of that text.

First, the input is normalized: the spaces and punctuation are removed from the English text and the message is downcased.

Then, the normalized characters are broken into rows. These rows can be regarded as forming a rectangle when printed with intervening newlines.

For example, the sentence

``````"If man was meant to stay on the ground, god would have given us roots."
``````

is normalized to:

``````"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
``````

The plaintext should be organized in to a rectangle. The size of the rectangle (`r x c`) should be decided by the length of the message, such that `c >= r` and `c - r <= 1`, where `c` is the number of columns and `r` is the number of rows.

Our normalized text is 54 characters long, dictating a rectangle with `c = 8` and `r = 7`:

``````"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots  "
``````

The coded message is obtained by reading down the columns going left to right.

The message above is coded as:

``````"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
``````

Output the encoded text in chunks that fill perfect rectangles `(r X c)`, with `c` chunks of `r` length, separated by spaces. For phrases that are `n` characters short of the perfect rectangle, pad each of the last `n` chunks with a single trailing space.

``````"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn  sseoau "
``````

Notice that were we to stack these, we could visually decode the ciphertext back in to the original message:

``````"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
``````

## Coding the solution

Look for a stub file having the name crypto_square.go and place your solution code in that file.

## Running the tests

To run the tests run the command `go test` from within the exercise directory.

If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem` flags:

``````go test -v --bench . --benchmem
``````

Keep in mind that each reviewer will run benchmarks on a different machine, with different specs, so the results from these benchmark tests may vary.

## Further information

For more detailed information about the Go track, including how to get help if you're having trouble, please visit the exercism.io Go language page.

## Source

J Dalbey's Programming Practice problems http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### crypto_square_test.go

``````package cryptosquare

import "testing"

var tests = []struct {
pt string // plain text
ct string // cipher text
}{
{
"s#\$%^&plunk",
"su pn lk",
},
{
"1, 2, 3 GO!",
"1g 2o 3 ",
},
{
"1234",
"13 24",
},
{
"123456789",
"147 258 369",
},
{
"123456789abc",
"159 26a 37b 48c",
},
{
"Never vex thine heart with idle woes",
"neewl exhie vtetw ehaho ririe vntds",
},
{
"ZOMG! ZOMBIES!!!",
"zzi ooe mms gb ",
},
{
"Time is an illusion. Lunchtime doubly so.",
"tasney inicds miohoo elntu  illib  suuml ",
},
{
"We all know interspecies romance is weird.",
"wneiaw eorene awssci liprer lneoid ktcms ",
},
{
"msemo aanin dnin  ndla  etlt  shui ",
},
{
"Vampires are people too!",
"vrel aepe mset paoo irpo",
},
{
"",
"",
},
{
"1",
"1",
},
{
"12",
"1 2",
},
{
"12 3",
"13 2 ",
},
{
"12345678",
"147 258 36 ",
},
{
"123456789a",
"159 26a 37  48 ",
},
{
"If man was meant to stay on the ground god would have given us roots",
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn  sseoau ",
},
{
"Have a nice day. Feed the dog & chill out!",
"hifei acedl veeol eddgo aatcu nyhht",
},
}

func TestEncode(t *testing.T) {
for _, test := range tests {
if ct := Encode(test.pt); ct != test.ct {
t.Fatalf(`Encode(%q):
got  %q
want %q`, test.pt, ct, test.ct)
}
}
}

func BenchmarkEncode(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range tests {
Encode(test.pt)
}
}
}``````
``````package cryptosquare

import (
"math"
"strings"
"unicode"
)

func Encode(plaintext string) string {
if plaintext == "" {
return plaintext
}

toEncode := normalize(plaintext)

// I originally assumed I'd need both row
// _and_ and column sizes; I _should_ rewrite
// `dimensions` to only provide the one, but
// this is just Exercism, so I'll leave it.
_, columns := dimensions(len(toEncode))

rows := toRows(toEncode, columns)

encoded := toColumns(rows)

return strings.Join(encoded, " ")
}

// Normalizes text to the desired
// format (stripping non-alphanumeric
func normalize(input string) string {
input = strings.ToLower(input)
var output strings.Builder

for _, character := range input {
if shouldWrite(character) {
output.WriteRune(character)
}
}

return output.String()
}

// Helper function to identify alphanumeric characters.
func shouldWrite(character rune) bool {
return (unicode.IsLetter(character) || unicode.IsDigit(character))
}

// Takes the length of the normalized text and
// returns a two-tuple (insofar as Go has tuples,
// which... it doesn't) of `numRows`, `numColumns`.
func dimensions(textLength int) (numRows, numColumns int) {
n := math.Sqrt(float64(textLength))

return int(math.Floor(n)), int(math.Ceil(n))
}

// Converts the input `text` into a slice of
// strings (rows), where each row is of length
// `numColumns`. We pad the final row with
// whitespace as needed to get a rectangle.
func toRows(text string, numColumns int) (rows []string) {
for i := 0; i < len(text); i += numColumns {
end := i + numColumns

if end > len(text) {
end = len(text)
}

rows = append(rows, text[i:end])
}

// Pad last row with whitespace.
lastRowLength := len(rows[len(rows)-1])

// There's almost certainly a more efficient
// way to do this, but leaving as-is for now.
for i := lastRowLength; i < numColumns; i++ {
rows[len(rows)-1] += " "
}

return rows
}

// "Rotates" the `rows` into columns in
// order to perform the final encoding.
func toColumns(rows []string) (rotated []string) {
rowLength := len(rows[0])

for i := 0; i < rowLength; i++ {
var sb strings.Builder

for _, row := range rows {
sb.WriteByte(row[i])
}

rotated = append(rotated, sb.String())
}

return rotated
}``````

### What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

• What compromises have been made?