-
-
Notifications
You must be signed in to change notification settings - Fork 40
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
Affin cipher fix #125
Open
tintinthong
wants to merge
14
commits into
exercism:main
Choose a base branch
from
tintinthong:affin-cipher-fix
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+249
−0
Open
Affin cipher fix #125
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
9505982
added affine-cipher
tintinthong 0ba2c40
Changes to config file. Math heavy so put difficult of 5. Do not thin…
tintinthong 3e2825b
For lintr bot, made `return()` lines more succinct
tintinthong f9be9b2
Small changes to fix linting errors
tintinthong f9accd7
fixed spaces AGAIN
tintinthong 14f6423
changed config difficulty, modularise files. will add test later
tintinthong 3b5d281
clean up
tintinthong eabc734
remove comments in test file
tintinthong 8d7e56b
removed dump file. solving lintr bot stuff.
tintinthong 6a63789
Configure affine-cipher according to "practical option"
katrinleinweber 0ffbef2
Separate the functions visually
katrinleinweber 380eff6
Fix typos & minor formatting issues
katrinleinweber cc0c5e9
Merge branch 'master' into affin-cipher-fix
tintinthong 42d1262
Change unlocked by to rotational-cipher(core). Prime-factors (not core)
tintinthong File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
# Affine Cipher | ||
|
||
Create an implementation of the affine cipher, | ||
an ancient encryption system created in the Middle East. | ||
|
||
The affine cipher is a type of monoalphabetic substitution cipher. | ||
Each character is mapped to its numeric equivalent, encrypted with | ||
a mathematical function and then converted to the letter relating to | ||
its new numeric value. | ||
|
||
To make an affine cipher, you must create two main functions: | ||
|
||
1. `encrypt(plaintext, a, b)`: A function to encrpt a plaintext/message with keys `a` and `b` | ||
2. `decrypt(encryption, a, b)`: A function to decrypt an encrypted plaintext/message with keys `a` and `b` | ||
|
||
In order to help to break down the problem into smaller pieces, you must create 4 utility functions. | ||
|
||
1. `normalise(text)`: A function to remove whitepsace in text and change all letter cases to lower case. | ||
2. `lookupindex(normalisedtext)`: A function to return an index of a letter from a lookup table. | ||
3. `gcd(x,y)`: A function to compute the greatest common divisor of two numbers. | ||
4. `mmi(a,m)`: A function to compute the modulo multiplicative inverse `a mod m`. | ||
|
||
Note: These utility functions are not required to pass the test. | ||
|
||
## Main Algorithm | ||
|
||
The algorithm is as below: | ||
|
||
1. the encryption function is: | ||
|
||
`E(x) = (ax + b) mod m` | ||
* where `x` is the letter's index from 0 to (length of alphabet - 1) | ||
* `m` is the length of the alphabet. For the Roman alphabet `m == 26`. | ||
* and `a` and `b` make the key | ||
|
||
Alphabet | x | ||
a | 0 | | ||
b | 1 | | ||
c | 2 | | ||
d | 3 | | ||
e | 4 | | ||
f | 5 | | ||
etc. | etc. | ||
|
||
2. the decryption function is: | ||
|
||
`D(y) = a^-1(y - b) mod m` | ||
* where `y` is the encrypted letter's index from 0 to (length of alphabet - 1) | ||
* it is important to note that `a^-1` is the modular multiplicative inverse | ||
of `a mod m` | ||
* the modular multiplicative inverse of `a` only exists if `a` and `m` are | ||
coprime. | ||
|
||
Alphabet | y | ||
a | 0 | | ||
b | 1 | | ||
c | 2 | | ||
d | 3 | | ||
e | 4 | | ||
f | 5 | | ||
etc. | etc. | ||
|
||
3. To find the GCD of two numbers: | ||
|
||
The easiest way to execute this is to compute the Euclidean Algorithm. The benefits of using the Euclidean algorithm is that it is commutative, i.e. `gcd(x,y) = gcd(y,x)`. | ||
|
||
4. To find the MMI of `a`: | ||
|
||
`an mod m = 1`, where `n` is the modular multiplicative inverse of `a mod m` | ||
|
||
## Caveats | ||
|
||
You must also check for the following: | ||
* `a` is coprime with `m`, otherwise, an error is returned. The statement `a` and `m` are coprime means that the greatest common divisor of `a` and `m` equals 1. | ||
* all messages are normalised (white space is removed and all letters converted to lowercase) before applying the encrypt algorithm | ||
* all encryptions are normalised (white space is removed and all letters converted to lowercase) before applying the decrypt algorithm | ||
|
||
## Examples of Main Algorithm | ||
|
||
* Encoding `test` gives `ybty` with the key a=5 b=7 | ||
* Decoding `ybty` gives `test` with the key a=5 b=7 | ||
* Decoding `ybty` gives `lqul` with the wrong key a=11 b=7 | ||
* Decoding `kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx` | ||
- gives `thequickbrownfoxjumpsoverthelazydog` with the key a=19 b=13 | ||
* Encoding `test` with the key a=18 b=13 | ||
- gives `Error: a and m must be coprime.` | ||
|
||
### Examples of finding a Greatest Common Divisor (GCD) | ||
|
||
* The greatest common divisor of two prime numbers is 1 | ||
* The greatest common divisor of 8 and 24 is 8 | ||
* The greatest common divisor of 60 and 9 is 3 | ||
|
||
### Examples of finding a Modular Multiplicative Inverse (MMI) | ||
|
||
1. simple example: | ||
- `9 mod 26 = 9` | ||
- `9 * 3 mod 26 = 27 mod 26 = 1` | ||
- `3` is the MMI of `9 mod 26` | ||
2. a more complicated example: | ||
- `15 mod 26 = 15` | ||
- `15 * 7 mod 26 = 105 mod 26 = 1` | ||
- `7` is the MMI of `15 mod 26` | ||
|
||
|
||
## Installation | ||
See [this guide](https://exercism.io/tracks/r/installation) for instructions on how to setup your local R environment. | ||
|
||
## How to implement your solution | ||
In each problem folder, there is a file named `<exercise_name>.R` containing a function that returns a `NULL` value. Place your implementation inside the body of the function. | ||
|
||
## How to run tests | ||
|
||
Inside of RStudio, simply execute the `test_<exercise_name>.R` script. This can be conveniently done with [testthat's `auto_test` function](https://www.rdocumentation.org/packages/testthat/topics/auto_test). Because Exercism code and tests are in the same folder, use this same path for both `code_path` and `test_path` parameters. On the command-line, you can also run `Rscript test_<exercise_name>.R`. | ||
|
||
|
||
## Source | ||
|
||
1. [Lecture Notes](http://pi.math.cornell.edu/~kozdron/Teaching/Cornell/135Summer06/Handouts/affine.pdf) | ||
2. [Wikipedia](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) | ||
3. [Modular Multiplicative Inverse](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse) | ||
|
||
## Submitting Incomplete Solutions | ||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
normalise <- function(text) { | ||
|
||
} | ||
|
||
lookupindex <- function(normalisedtext) { | ||
|
||
} | ||
|
||
gcd <- function(x, y) { | ||
|
||
} | ||
|
||
mmi <- function(a, m) { | ||
|
||
} | ||
|
||
encrypt <- function(plaintext, a, b) { | ||
|
||
} | ||
|
||
decrypt <- function(encryption, a, b) { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
normalise <- function(text) { | ||
return(tolower(gsub(" ", "", text))) | ||
} | ||
|
||
lookupindex <- function(normalisedtext) { | ||
letterslist <- strsplit(normalisedtext, "")[[1]] | ||
return(match(letterslist, letters) - 1) | ||
} | ||
|
||
gcd <- function(x, y) { | ||
r <- x %% y | ||
return(ifelse(r, gcd(y, r), y)) | ||
} | ||
|
||
mmi <- function(a, m) { | ||
a <- a %% m | ||
for (x in 1:m) { | ||
if ((a * x) %% m == 1) { | ||
return(x) | ||
} | ||
} | ||
return(1) | ||
} | ||
|
||
encrypt <- function(plaintext, a, b) { | ||
m <- 26 | ||
|
||
if (gcd(a, m) != 1) { | ||
stop(paste("a=", a, " and m=", m, "is coprime")) | ||
} | ||
|
||
normalisedplaintext <- normalise(plaintext) | ||
x <- lookupindex(normalisedplaintext) | ||
|
||
return(paste(letters[ ((a * x + b) %% m) + 1], collapse = "")) | ||
} | ||
|
||
decrypt <- function(encryption, a, b) { | ||
m <- 26 | ||
|
||
if (gcd(a, m) != 1) { | ||
stop("a and 26 must be co-prime") | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Duplicate of L11-14, see https://github.com/exercism/r/pull/125/files#r312707113 |
||
|
||
normalisedencryption <- normalise(encryption) | ||
y <- lookupindex(normalisedencryption) | ||
|
||
return(paste(letters[((mmi(a, m) * (y - b)) %% m) + 1], collapse = "")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
source("./affine-cipher.R") | ||
library(testthat) | ||
|
||
test_that("encrypt() returns correct string", { | ||
expect_identical(encrypt("test", 5, 7), "ybty") | ||
}) | ||
|
||
test_that("encrypt() accounts for whitespace", { | ||
expect_identical(encrypt("te st ", 5, 7), "ybty") | ||
}) | ||
|
||
test_that("encrypt() accounts for case-sensitivity", { | ||
expect_identical(encrypt("TeST", 5, 7), "ybty") | ||
}) | ||
|
||
test_that("encrypt() checks that a is coprime with m", { | ||
expect_error(encrypt("jknkasd", 18, 13)) | ||
}) | ||
|
||
test_that("decrypt() returns correct string", { | ||
expect_identical(decrypt("ybty", 5, 7), "test") | ||
expect_identical( | ||
decrypt("kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx", 19, 13), | ||
"thequickbrownfoxjumpsoverthelazydog" | ||
) | ||
}) | ||
|
||
test_that("decrypt() accounts for whitespace", { | ||
expect_identical(decrypt(" ybt y", 5, 7), "test") | ||
expect_identical( | ||
decrypt("kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx", 19, 13), | ||
"thequickbrownfoxjumpsoverthelazydog" | ||
) | ||
}) | ||
|
||
test_that("decrypt() checks that a is coprime with m", { | ||
expect_error(decrypt("jknkasd", 18, 13)) | ||
}) | ||
|
||
message("All tests passed for exercise: affine-cipher") |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does
encryption
mean the ciphertext here? If yes, please consider renaming it.message
->plaintext
might also be a good idea then, because those are the domain terms.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Agreed.