Skip to content
Closed
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
12 changes: 12 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,18 @@
"control_flow_conditionals",
"text_formatting"
]
},
{
"slug": "affine-cipher",
"uuid": "9bc3f040-9e4b-4ed0-b23c-3ae565e83a59",
"core": false,
"difficulty": 5,
"topics": [
"control_flow_conditionals",
"math",
"recursion",
"algorithms"
]
}
]
}
109 changes: 109 additions & 0 deletions exercises/affine-cipher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# 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 functions:

1. `encrypt(message,a,b)`: A function to encrpt a message with keys `a` and `b`
2. `decrypt(encryption,a,b)`: A function to decrypt an encrypted messsage with keys `a` and `b`

## Algorithm

The algorithm is as below:

** Note: `m` equals 26. You should take `m` as a constant. **

1. the encryption function is:

`E(x) = (ax + b) mod m`
- where `x` is the letter's index from 0 - 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 numeric value of an encrypted letter, ie. `y = E(x)`
- 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 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 encryptions have whitespace removed before applying the algorithm
- all encryptions have whitespace removed before applying the algorithm
- all messages are converted lower case before applying the algorithm

## Examples

- 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 Modular Multiplicative Inverse (MMI)

- simple example:
- `9 mod 26 = 9`
- `9 * 3 mod 26 = 27 mod 26 = 1`
- `3` is the MMI of `9 mod 26`
- 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.
8 changes: 8 additions & 0 deletions exercises/affine-cipher/affine-cipher.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
encrypt <- function(message, a, b) {

}


decrypt <- function(encryption, a, b) {

}
49 changes: 49 additions & 0 deletions exercises/affine-cipher/example.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# encrypts message
encrypt <- function(message, a, b) {
m <- 26

# computes the greatest common divisor of numbers x and y
gcd <- function(x, y) {
r <- x %% y
return(ifelse(r, gcd(y, r), y))
}

# must check a and m are coprime
if (gcd(a, m) != 1) {
stop("a and 26 must be co-prime")
}

# E(x) = (ax + b) mod m
return(paste(letters[ ((a * (match(strsplit(tolower(gsub(" ", "", message)), "")[[1]], letters) - 1) + b) %% m) + 1], collapse = ""))
}

# decrypts encryption
decrypt <- function(encryption, a, b) {
m <- 26

# computes the greatest common divisor of numbers x and y
gcd <- function(x, y) {
r <- x %% y
return(ifelse(r, gcd(y, r), y))
}

# must check a and m are coprime
if (gcd(a, m) != 1) {
stop("a and 26 must be co-prime")
}

# computes the modulo multiplicative inverse of a^m
# a^-1 = mmi( a mod m)=mmi(a,m)
mmi <- function(a, m) {
a <- a %% m
for (x in 1:m) {
if ((a * x) %% m == 1) {
return(x)
}
}
return(1)
}

# D(y) = a^-1(y - b) mod m
return(paste(letters[((mmi(a, m) * ((match(strsplit(gsub(" ", "", encryption), "")[[1]], letters) - 1) - b)) %% m) + 1], collapse = ""))
}
36 changes: 36 additions & 0 deletions exercises/affine-cipher/test_affine-cipher.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
source("./affine-cipher.R")
library(testthat)

# check encrypt() function
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))
})

# check decrypt() function
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")
2 changes: 2 additions & 0 deletions exercises/fizz-buzz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ See [this guide](https://exercism.io/tracks/r/installation) for instructions on
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

"Fizz-Buzz" originated as a children's game that helped children learn to divide. It is now regularly used in coding interviews.
Expand Down