Skip to content

Made crypto square exercise more idiomatic #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 62 additions & 115 deletions exercises/crypto-square/example/crypto_square.d
Original file line number Diff line number Diff line change
@@ -1,180 +1,127 @@

module crypto;

import std.string;
import std.array;
import std.math : sqrt, floor, ceil;
import std.conv : to;

import std.stdio;
import std.algorithm : map, filter;
import std.ascii : isAlphaNum;


class Cipher
{
public:
this (const string text)
{
this.normalized = text.filter!isAlphaNum.map!toLower.to!string;
}

string normalizePlainText ()
{
return normalized;
}

/**
* Return the number of encrypted segments(that is number of columns for the crypto square).
*/
size_t size ()
{
size_t root = to!ulong(floor(sqrt(to!float(normalized.length))));

size_t distance = normalized.length - root * root;
if (distance == 0)
{
return root;
}
else
{
return to!ulong(root) + 1;
}
}

string[] plainTextSegments ()
{
size_t columns = size();
size_t rem = normalized.length % columns;
string[] result;

size_t len = normalized.length;
for (size_t j = 0; j <= (len - columns); j += columns)
{
result ~= normalized[j..j + columns];
}

// we need to handle the leftovers
if (rem)
{
result ~= normalized[len - rem .. len];
}

return result;
}

string cipherText ()
{
string result = normalizedCipherText();

return result.replace(" ", "");
}

string normalizedCipherText ()
{
string result;
string[] segments = plainTextSegments();
size_t columns = size();

for (size_t i = 0; i < columns; ++i)
{
string cryptoSegment;
foreach (s; segments)
{
if (i < s.length) cryptoSegment ~= s[i];
}

result ~= cryptoSegment;
result ~= " ";
}

return strip(result);

}

private:
const string normalized;
struct Cipher {
import std.ascii : isAlphaNum;
import std.algorithm.iteration : filter, joiner;
import std.range : array, chunks, transposed, walkLength;
import std.uni : asLowerCase;
import std.math : sqrt, ceil;

string delimiter;
typeof("".asLowerCase.filter!isAlphaNum) content;
this(string s) {
content = s.asLowerCase.filter!isAlphaNum;
}

auto normalizePlainText() {
return content;
}

@property
uint size() {
return cast(uint)(cast(double)content.walkLength).sqrt.ceil;
}

@property
auto plainTextSegments() {
return content.chunks(size);
}

@property
auto cipherText() {
return plainTextSegments.array.transposed.joiner(delimiter);
}

Cipher normalize() {
Cipher result = this;
result.delimiter = " ";
return result;
}
}

unittest
{
import std.algorithm.comparison : equal;
immutable int allTestsEnabled = 0;

// normalize_strange_characters
{
auto theCipher = new Cipher("s#$%^&plunk");
assert("splunk" == theCipher.normalizePlainText());
auto theCipher = Cipher("s#$%^&plunk");
assert(equal("splunk", theCipher.normalizePlainText()));
}

static if (allTestsEnabled)
{
// normalize_numbers
{
auto theCipher = new Cipher("1, 2, 3 GO!");
assert("123go" == theCipher.normalizePlainText());
auto theCipher = Cipher("1, 2, 3 GO!");
assert(equal("123go", theCipher.normalizePlainText()));
}

// size_of_small_square
{
auto theCipher = new Cipher("1234");
auto theCipher = Cipher("1234");
assert(2U == theCipher.size());
}

// size_of_slightly_larger_square
{
auto theCipher = new Cipher("123456789");
auto theCipher = Cipher("123456789");
assert(3U == theCipher.size());
}

// size_of_non_perfect_square
{
auto theCipher = new Cipher("123456789abc");
auto theCipher = Cipher("123456789abc");
assert(4U == theCipher.size());
}

// size_of_non_perfect_square_less
{
auto theCipher = new Cipher("zomgzombies");
auto theCipher = Cipher("zomgzombies");
assert(4U == theCipher.size());
}

// plain_text_segments_from_phrase
{
const string[] expected = ["neverv", "exthin", "eheart", "withid", "lewoes"];
auto theCipher = new Cipher("Never vex thine heart with idle woes");
auto theCipher = Cipher("Never vex thine heart with idle woes");
const auto actual = theCipher.plainTextSegments();

assert(expected == actual);
assert(equal(expected, actual));
}

// plain_text_segments_from_complex_phrase
{
const string[] expected = ["zomg", "zomb", "ies"];
auto theCipher = new Cipher("ZOMG! ZOMBIES!!!");
auto theCipher = Cipher("ZOMG! ZOMBIES!!!");
const auto actual = theCipher.plainTextSegments();

assert(expected == actual);
assert(equal(expected, actual));
}

// Cipher_text_short_phrase
{
auto theCipher = new Cipher("Time is an illusion. Lunchtime doubly so.");
assert("tasneyinicdsmiohooelntuillibsuuml" == theCipher.cipherText());
auto theCipher = Cipher("Time is an illusion. Lunchtime doubly so.");
assert(equal("tasneyinicdsmiohooelntuillibsuuml", theCipher.cipherText()));
}

// Cipher_text_long_phrase
{
auto theCipher = new Cipher("We all know interspecies romance is weird.");
assert("wneiaweoreneawssciliprerlneoidktcms" == theCipher.cipherText());
auto theCipher = Cipher("We all know interspecies romance is weird.");
assert(equal("wneiaweoreneawssciliprerlneoidktcms", theCipher.cipherText()));
}

// normalized_Cipher_text1
{
auto theCipher = new Cipher("Madness, and then illumination.");
assert("msemo aanin dnin ndla etlt shui" == theCipher.normalizedCipherText());
auto theCipher = Cipher("Madness, and then illumination.");
assert(equal("msemo aanin dnin ndla etlt shui", theCipher.normalize.cipherText()));
}

// normalized_Cipher_text2
{
auto theCipher = new Cipher("Vampires are people too!");
assert("vrel aepe mset paoo irpo" == theCipher.normalizedCipherText());
auto theCipher = Cipher("Vampires are people too!");
assert(equal("vrel aepe mset paoo irpo", theCipher.normalize.cipherText()));
}

}
}
45 changes: 22 additions & 23 deletions exercises/crypto-square/source/crypto_square.d
Original file line number Diff line number Diff line change
@@ -1,90 +1,89 @@

module crypto;


unittest
{
import std.algorithm.comparison : equal;
immutable int allTestsEnabled = 0;

// normalize_strange_characters
{
auto theCipher = new Cipher("s#$%^&plunk");
assert("splunk" == theCipher.normalizePlainText());
auto theCipher = Cipher("s#$%^&plunk");
assert(equal("splunk", theCipher.normalizePlainText()));
}

static if (allTestsEnabled)
{
// normalize_numbers
{
auto theCipher = new Cipher("1, 2, 3 GO!");
assert("123go" == theCipher.normalizePlainText());
auto theCipher = Cipher("1, 2, 3 GO!");
assert(equal("123go", theCipher.normalizePlainText()));
}

// size_of_small_square
{
auto theCipher = new Cipher("1234");
auto theCipher = Cipher("1234");
assert(2U == theCipher.size());
}

// size_of_slightly_larger_square
{
auto theCipher = new Cipher("123456789");
auto theCipher = Cipher("123456789");
assert(3U == theCipher.size());
}

// size_of_non_perfect_square
{
auto theCipher = new Cipher("123456789abc");
auto theCipher = Cipher("123456789abc");
assert(4U == theCipher.size());
}

// size_of_non_perfect_square_less
{
auto theCipher = new Cipher("zomgzombies");
auto theCipher = Cipher("zomgzombies");
assert(4U == theCipher.size());
}

// plain_text_segments_from_phrase
{
const string[] expected = ["neverv", "exthin", "eheart", "withid", "lewoes"];
auto theCipher = new Cipher("Never vex thine heart with idle woes");
auto theCipher = Cipher("Never vex thine heart with idle woes");
const auto actual = theCipher.plainTextSegments();

assert(expected == actual);
assert(equal(expected, actual));
}

// plain_text_segments_from_complex_phrase
{
const string[] expected = ["zomg", "zomb", "ies"];
auto theCipher = new Cipher("ZOMG! ZOMBIES!!!");
auto theCipher = Cipher("ZOMG! ZOMBIES!!!");
const auto actual = theCipher.plainTextSegments();

assert(expected == actual);
assert(equal(expected, actual));
}

// Cipher_text_short_phrase
{
auto theCipher = new Cipher("Time is an illusion. Lunchtime doubly so.");
assert("tasneyinicdsmiohooelntuillibsuuml" == theCipher.cipherText());
auto theCipher = Cipher("Time is an illusion. Lunchtime doubly so.");
assert(equal("tasneyinicdsmiohooelntuillibsuuml", theCipher.cipherText()));
}

// Cipher_text_long_phrase
{
auto theCipher = new Cipher("We all know interspecies romance is weird.");
assert("wneiaweoreneawssciliprerlneoidktcms" == theCipher.cipherText());
auto theCipher = Cipher("We all know interspecies romance is weird.");
assert(equal("wneiaweoreneawssciliprerlneoidktcms", theCipher.cipherText()));
}

// normalized_Cipher_text1
{
auto theCipher = new Cipher("Madness, and then illumination.");
assert("msemo aanin dnin ndla etlt shui" == theCipher.normalize.cipherText());
auto theCipher = Cipher("Madness, and then illumination.");
assert(equal("msemo aanin dnin ndla etlt shui", theCipher.normalize.cipherText()));
}

// normalized_Cipher_text2
{
auto theCipher = new Cipher("Vampires are people too!");
assert("vrel aepe mset paoo irpo" == theCipher.normalize.cipherText());
}
auto theCipher = Cipher("Vampires are people too!");
assert(equal("vrel aepe mset paoo irpo", theCipher.normalize.cipherText()));
}

}
}