Skip to content
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
1 change: 1 addition & 0 deletions bin/auto-sync.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ scrabble-score
secret-handshake
series
sieve
simple-cipher
space-age
spiral-matrix
square-root
Expand Down
2 changes: 1 addition & 1 deletion exercises/practice/simple-cipher/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Description
# Instructions

Create an implementation of the [Vigenère cipher][wiki].
The Vigenère cipher is a simple substitution cipher.
Expand Down
7 changes: 5 additions & 2 deletions exercises/practice/simple-cipher/.meta/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"camilopayan"
],
"contributors": [
"MichaelBunker"
"MichaelBunker",
"Narkunan"
],
"files": {
"solution": [
Expand All @@ -16,5 +17,7 @@
".meta/example.php"
]
},
"blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher"
"blurb": "Implement the Vigenère cipher, a simple substitution cipher.",
"source": "Substitution Cipher at Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Substitution_cipher"
}
24 changes: 0 additions & 24 deletions exercises/practice/simple-cipher/.meta/example.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,5 @@
<?php

/*
* By adding type hints and enabling strict type checking, code can become
* easier to read, self-documenting and reduce the number of potential bugs.
* By default, type declarations are non-strict, which means they will attempt
* to change the original type to match the type specified by the
* type-declaration.
*
* In other words, if you pass a string to a function requiring a float,
* it will attempt to convert the string value to a float.
*
* To enable strict mode, a single declare directive must be placed at the top
* of the file.
* This means that the strictness of typing is configured on a per-file basis.
* This directive not only affects the type declarations of parameters, but also
* a function's return type.
*
* For more info review the Concept on strict type checking in the PHP track
* <link>.
*
* To disable strict typing, comment out the directive below.
*/

declare(strict_types=1);

class SimpleCipher
{
public const LETTERS = "abcdefghijklmnopqrstuvwxyz";
Expand Down
46 changes: 46 additions & 0 deletions exercises/practice/simple-cipher/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[b8bdfbe1-bea3-41bb-a999-b41403f2b15d]
description = "Random key cipher -> Can encode"

[3dff7f36-75db-46b4-ab70-644b3f38b81c]
description = "Random key cipher -> Can decode"

[8143c684-6df6-46ba-bd1f-dea8fcb5d265]
description = "Random key cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method"

[defc0050-e87d-4840-85e4-51a1ab9dd6aa]
description = "Random key cipher -> Key is made only of lowercase letters"

[565e5158-5b3b-41dd-b99d-33b9f413c39f]
description = "Substitution cipher -> Can encode"

[d44e4f6a-b8af-4e90-9d08-fd407e31e67b]
description = "Substitution cipher -> Can decode"

[70a16473-7339-43df-902d-93408c69e9d1]
description = "Substitution cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method"

[69a1458b-92a6-433a-a02d-7beac3ea91f9]
description = "Substitution cipher -> Can double shift encode"

[21d207c1-98de-40aa-994f-86197ae230fb]
description = "Substitution cipher -> Can wrap on encode"

[a3d7a4d7-24a9-4de6-bdc4-a6614ced0cb3]
description = "Substitution cipher -> Can wrap on decode"

[e31c9b8c-8eb6-45c9-a4b5-8344a36b9641]
description = "Substitution cipher -> Can encode messages longer than the key"

[93cfaae0-17da-4627-9a04-d6d1e1be52e3]
description = "Substitution cipher -> Can decode messages longer than the key"
145 changes: 74 additions & 71 deletions exercises/practice/simple-cipher/SimpleCipherTest.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,8 @@
<?php

/*
* By adding type hints and enabling strict type checking, code can become
* easier to read, self-documenting and reduce the number of potential bugs.
* By default, type declarations are non-strict, which means they will attempt
* to change the original type to match the type specified by the
* type-declaration.
*
* In other words, if you pass a string to a function requiring a float,
* it will attempt to convert the string value to a float.
*
* To enable strict mode, a single declare directive must be placed at the top
* of the file.
* This means that the strictness of typing is configured on a per-file basis.
* This directive not only affects the type declarations of parameters, but also
* a function's return type.
*
* For more info review the Concept on strict type checking in the PHP track
* <link>.
*
* To disable strict typing, comment out the directive below.
*/

declare(strict_types=1);

use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;

class SimpleCipherTest extends TestCase
Expand All @@ -33,121 +12,145 @@ public static function setUpBeforeClass(): void
require_once 'SimpleCipher.php';
}

public function testRandomCipherKeyIsLetters(): void
{
$cipher = new SimpleCipher();
$this->assertMatchesRegularExpression('/\A[a-z]+\z/', $cipher->key);
}

/**
* Here we take advantage of the fact that plaintext of "aaa..." doesn't
* output the key. This is a critical problem with shift ciphers, some
* characters will always output the key verbatim.
*
* Uuid: b8bdfbe1-bea3-41bb-a999-b41403f2b15d
*/
public function testRandomKeyCipherEncode(): void
#[TestDox('Random key cipher -> Can encode')]
public function testRandomKeyCipherCanEncode(): void
{
$cipher = new SimpleCipher();
$plaintext = 'aaaaaaaaaa';
$this->assertEquals(substr($cipher->key, 0, 10), $cipher->encode($plaintext));
}

public function testRandomKeyCipherDecode(): void
/**
* Uuid: 3dff7f36-75db-46b4-ab70-644b3f38b81c
*/
#[TestDox('Random key cipher -> Can decode')]
public function testRandomKeyCipherCanDecode(): void
{
$cipher = new SimpleCipher();
$plaintext = 'aaaaaaaaaa';
$this->assertEquals($plaintext, $cipher->decode(substr($cipher->key, 0, 10)));
}

public function testRandomKeyCipherReversible(): void
/**
* Uuid: 8143c684-6df6-46ba-bd1f-dea8fcb5d265
*/
#[TestDox('Random key cipher -> Is reversible')]
public function testRandomKeyCipherIsReversible(): void
{
$cipher = new SimpleCipher();
$plaintext = 'abcdefghij';
$this->assertEquals($plaintext, $cipher->decode($cipher->encode($plaintext)));
}

public function testCipherWithCapsKey(): void
{
$this->expectException(InvalidArgumentException::class);
$cipher = new SimpleCipher('ABCDEF');
}

public function testCipherWithNumericKey(): void
{
$this->expectException(InvalidArgumentException::class);
$cipher = new SimpleCipher('12345');
}

public function testCipherWithEmptyKey(): void
{
$this->expectException(InvalidArgumentException::class);
$cipher = new SimpleCipher('');
}

public function testCipherKeyIsAsSubmitted(): void
/**
* Uuid: defc0050-e87d-4840-85e4-51a1ab9dd6aa
*/
#[TestDox('Random key cipher -> Key is made only of lowercase letters')]
public function testRandomCipherKeyIsLowercaseLetters(): void
{
$cipher = new SimpleCipher('abcdefghij');
$this->assertEquals($cipher->key, 'abcdefghij');
$cipher = new SimpleCipher();
$this->assertMatchesRegularExpression('/\A[a-z]+\z/', $cipher->key);
}

public function testCipherEncode(): void
/**
* Uuid: 565e5158-5b3b-41dd-b99d-33b9f413c39f
*/
#[TestDox('Substitution cipher -> Can encode')]
public function testSubstitutionCipherCanEncode(): void
{
$cipher = new SimpleCipher('abcdefghij');
$plaintext = 'aaaaaaaaaa';
$ciphertext = 'abcdefghij';
$this->assertEquals($ciphertext, $cipher->encode($plaintext));
}

public function testCipherDecode(): void
/**
* Uuid: d44e4f6a-b8af-4e90-9d08-fd407e31e67b
*/
#[TestDox('Substitution cipher -> Can decode')]
public function testSubstitutionCipherCanDecode(): void
{
$cipher = new SimpleCipher('abcdefghij');
$plaintext = 'aaaaaaaaaa';
$ciphertext = 'abcdefghij';
$this->assertEquals($plaintext, $cipher->decode($ciphertext));
}

public function testCipherReversible(): void
/**
* Uuid: 70a16473-7339-43df-902d-93408c69e9d1
*/
#[TestDox('Substitution cipher -> Is reversible')]
public function testSubstitutionCipherIsReversible(): void
{
$cipher = new SimpleCipher('abcdefghij');
$plaintext = 'abcdefghij';
$this->assertEquals($plaintext, $cipher->decode($cipher->encode($plaintext)));
}

public function testDoubleShiftEncode(): void
/**
* Uuid: 69a1458b-92a6-433a-a02d-7beac3ea91f9
*/
#[TestDox('Substitution cipher -> Can double shift encode')]
public function testSubstitutionCipherCanDoubleShiftEncode(): void
{
$cipher = new SimpleCipher('iamapandabear');
$plaintext = 'iamapandabear';
$ciphertext = 'qayaeaagaciai';
$this->assertEquals($ciphertext, $cipher->encode($plaintext));
}

public function testCipherEncodeWrap(): void
/**
* Uuid: 21d207c1-98de-40aa-994f-86197ae230fb
*/
#[TestDox('Substitution cipher -> Can wrap on encode')]
public function testSubstitutionCipherCanWrapEncode(): void
{
$cipher = new SimpleCipher('abcdefghij');
$plaintext = 'zzzzzzzzzz';
$ciphertext = 'zabcdefghi';
$this->assertEquals($ciphertext, $cipher->encode($plaintext));
}

public function testShiftCipherEncode(): void
/**
* Uuid: a3d7a4d7-24a9-4de6-bdc4-a6614ced0cb3
*/
#[TestDox('Substitution cipher -> Can wrap on decode')]
public function testSubstitutionCipherCanWrapDecode(): void
{
$cipher = new SimpleCipher('dddddddddd');
$plaintext = 'aaaaaaaaaa';
$ciphertext = 'dddddddddd';
$this->assertEquals($ciphertext, $cipher->encode($plaintext));
$cipher = new SimpleCipher('abcdefghij');
$plaintext = 'zzzzzzzzzz';
$ciphertext = 'zabcdefghi';
$this->assertEquals($plaintext, $cipher->decode($ciphertext));
}

public function testShiftCipherDecode(): void
/**
* Uuid: e31c9b8c-8eb6-45c9-a4b5-8344a36b9641
*/
#[TestDox('Substitution cipher -> Can encode messages longer than the key')]
public function testSubstitutionCipherCanEncodeMessageLongerThanKey(): void
{
$cipher = new SimpleCipher('dddddddddd');
$plaintext = 'aaaaaaaaaa';
$ciphertext = 'dddddddddd';
$this->assertEquals($plaintext, $cipher->decode($ciphertext));
$cipher = new SimpleCipher('abc');
$cipherText = 'iboaqcnecbfcr';
$plainText = 'iamapandabear';
$this->assertEquals($cipherText, $cipher->encode($plainText));
}

public function testShiftCipherReversible(): void
/**
* Uuid: 93cfaae0-17da-4627-9a04-d6d1e1be52e3
*/
#[TestDox('Substitution cipher -> Can decode messages longer than the key')]
public function testSubstitutionCipherCanDecodeMessageLongerThanKey(): void
{
$cipher = new SimpleCipher('dddddddddd');
$plaintext = 'abcdefghij';
$this->assertEquals($plaintext, $cipher->decode($cipher->encode($plaintext)));
$cipher = new SimpleCipher('abc');
$cipherText = 'iboaqcnecbfcr';
$plainText = 'iamapandabear';
$this->assertEquals($plainText, $cipher->decode($cipherText));
}
}
Loading