Skip to content

Commit 9289a53

Browse files
Add medium exercises (#168)
1 parent fe6405f commit 9289a53

171 files changed

Lines changed: 5461 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config.json

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,91 @@
299299
],
300300
"difficulty": 4
301301
},
302+
{
303+
"slug": "luhn",
304+
"name": "Luhn",
305+
"uuid": "e83e333e-612a-4ec8-8d9f-da387327acdc",
306+
"practices": [],
307+
"prerequisites": [],
308+
"difficulty": 4
309+
},
310+
{
311+
"slug": "robot-simulator",
312+
"name": "Robot Simulator",
313+
"uuid": "69868e6a-7e87-4cff-8f36-dd24c042e879",
314+
"practices": [],
315+
"prerequisites": [],
316+
"difficulty": 4
317+
},
318+
{
319+
"slug": "sieve",
320+
"name": "Sieve",
321+
"uuid": "8b1fc515-71c2-436c-98dc-8789d25c7ddb",
322+
"practices": [],
323+
"prerequisites": [],
324+
"difficulty": 4
325+
},
326+
{
327+
"slug": "space-age",
328+
"name": "Space Age",
329+
"uuid": "92fc6937-1b33-49f1-b66e-7ec7e3786906",
330+
"practices": [],
331+
"prerequisites": [],
332+
"difficulty": 4
333+
},
334+
{
335+
"slug": "word-count",
336+
"name": "Word Count",
337+
"uuid": "f0a2b8f5-b244-4c35-86a7-9bf86843093c",
338+
"practices": [],
339+
"prerequisites": [],
340+
"difficulty": 4
341+
},
302342
{
303343
"slug": "accumulate",
304344
"name": "Accumulate",
305345
"uuid": "8a373305-98c9-4b19-9011-085abe19cfed",
346+
"practices": [],
347+
"prerequisites": [],
348+
"difficulty": 5,
349+
"status": "deprecated"
350+
},
351+
{
352+
"slug": "affine-cipher",
353+
"name": "Affine Cipher",
354+
"uuid": "3e018380-7d24-403e-a951-b6f33ccc8afd",
355+
"practices": [],
356+
"prerequisites": [],
357+
"difficulty": 5
358+
},
359+
{
360+
"slug": "anagram",
361+
"name": "Anagram",
362+
"uuid": "ec107dbf-5123-4405-934e-3e0846433fd0",
363+
"practices": [],
364+
"prerequisites": [],
365+
"difficulty": 5
366+
},
367+
{
368+
"slug": "clock",
369+
"name": "Clock",
370+
"uuid": "2a606909-644b-4fd3-bd6c-bf0bb605a48d",
371+
"practices": [],
372+
"prerequisites": [],
373+
"difficulty": 5
374+
},
375+
{
376+
"slug": "crypto-square",
377+
"name": "Crypto Square",
378+
"uuid": "c4b23be2-92e7-40ee-ad93-19f9f79eebc9",
379+
"practices": [],
380+
"prerequisites": [],
381+
"difficulty": 5
382+
},
383+
{
384+
"slug": "list-ops",
385+
"name": "List Ops",
386+
"uuid": "9eb1ed19-7c5a-4c8f-98e3-7d99826deb18",
306387
"practices": [
307388
"quotations",
308389
"collections",
@@ -315,6 +396,38 @@
315396
],
316397
"difficulty": 5
317398
},
399+
{
400+
"slug": "matching-brackets",
401+
"name": "Matching Brackets",
402+
"uuid": "afef364d-12dd-42fd-bbc8-f2c6228f0f53",
403+
"practices": [],
404+
"prerequisites": [],
405+
"difficulty": 5
406+
},
407+
{
408+
"slug": "run-length-encoding",
409+
"name": "Run-Length Encoding",
410+
"uuid": "009090ef-d7fd-4ba5-9942-c6e686069e12",
411+
"practices": [],
412+
"prerequisites": [],
413+
"difficulty": 5
414+
},
415+
{
416+
"slug": "square-root",
417+
"name": "Square Root",
418+
"uuid": "53cef38b-ad56-4462-bf37-48902fd3745c",
419+
"practices": [],
420+
"prerequisites": [],
421+
"difficulty": 5
422+
},
423+
{
424+
"slug": "yacht",
425+
"name": "Yacht",
426+
"uuid": "c94bbdbb-cdfe-4b53-913c-fc7f705cc0d0",
427+
"practices": [],
428+
"prerequisites": [],
429+
"difficulty": 5
430+
},
318431
{
319432
"slug": "all-your-base",
320433
"name": "All Your Base",
@@ -336,6 +449,62 @@
336449
"topics": [
337450
"math"
338451
]
452+
},
453+
{
454+
"slug": "game-of-life",
455+
"name": "Conway's Game of Life",
456+
"uuid": "36771f7d-aee3-44bd-a718-60efeaca3a8a",
457+
"practices": [],
458+
"prerequisites": [],
459+
"difficulty": 6
460+
},
461+
{
462+
"slug": "ocr-numbers",
463+
"name": "OCR Numbers",
464+
"uuid": "725a35a9-b2ae-4c71-8dc9-a801a8699bd2",
465+
"practices": [],
466+
"prerequisites": [],
467+
"difficulty": 6
468+
},
469+
{
470+
"slug": "rail-fence-cipher",
471+
"name": "Rail Fence Cipher",
472+
"uuid": "7f404c18-968e-41de-8067-7be5596a87ef",
473+
"practices": [],
474+
"prerequisites": [],
475+
"difficulty": 6
476+
},
477+
{
478+
"slug": "say",
479+
"name": "Say",
480+
"uuid": "613a30e5-d545-40c7-b41e-f5ccb2c4ec88",
481+
"practices": [],
482+
"prerequisites": [],
483+
"difficulty": 6
484+
},
485+
{
486+
"slug": "dominoes",
487+
"name": "Dominoes",
488+
"uuid": "cbbd1c5f-587b-4274-9889-1332d9446c63",
489+
"practices": [],
490+
"prerequisites": [],
491+
"difficulty": 7
492+
},
493+
{
494+
"slug": "perfect-numbers",
495+
"name": "Perfect Numbers",
496+
"uuid": "0420eb1a-c607-4a65-8acb-aa3f60c01302",
497+
"practices": [],
498+
"prerequisites": [],
499+
"difficulty": 7
500+
},
501+
{
502+
"slug": "rectangles",
503+
"name": "Rectangles",
504+
"uuid": "bfcd7856-38df-48e5-9bf1-438962ac44f8",
505+
"practices": [],
506+
"prerequisites": [],
507+
"difficulty": 7
339508
}
340509
]
341510
},

exercises/practice/accumulate/accumulate/accumulate.factor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ USING: kernel ;
22
IN: accumulate
33

44
:: accum ( seq quot: ( x -- y ) -- newseq )
5-
"unimplemented" throw ;
5+
"unimplemented" throw ; inline
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Instructions
2+
3+
Create an implementation of the affine cipher, an ancient encryption system created in the Middle East.
4+
5+
The affine cipher is a type of monoalphabetic substitution cipher.
6+
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.
7+
Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the Atbash cipher, because it has many more keys.
8+
9+
[//]: # " monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic "
10+
11+
## Encryption
12+
13+
The encryption function is:
14+
15+
```text
16+
E(x) = (ai + b) mod m
17+
```
18+
19+
Where:
20+
21+
- `i` is the letter's index from `0` to the length of the alphabet - 1.
22+
- `m` is the length of the alphabet.
23+
For the Latin alphabet `m` is `26`.
24+
- `a` and `b` are integers which make up the encryption key.
25+
26+
Values `a` and `m` must be _coprime_ (or, _relatively prime_) for automatic decryption to succeed, i.e., they have number `1` as their only common factor (more information can be found in the [Wikipedia article about coprime integers][coprime-integers]).
27+
In case `a` is not coprime to `m`, your program should indicate that this is an error.
28+
Otherwise it should encrypt or decrypt with the provided key.
29+
30+
For the purpose of this exercise, digits are valid input but they are not encrypted.
31+
Spaces and punctuation characters are excluded.
32+
Ciphertext is written out in groups of fixed length separated by space, the traditional group size being `5` letters.
33+
This is to make it harder to guess encrypted text based on word boundaries.
34+
35+
## Decryption
36+
37+
The decryption function is:
38+
39+
```text
40+
D(y) = (a^-1)(y - b) mod m
41+
```
42+
43+
Where:
44+
45+
- `y` is the numeric value of an encrypted letter, i.e., `y = E(x)`
46+
- it is important to note that `a^-1` is the modular multiplicative inverse (MMI) of `a mod m`
47+
- the modular multiplicative inverse only exists if `a` and `m` are coprime.
48+
49+
The MMI of `a` is `x` such that the remainder after dividing `ax` by `m` is `1`:
50+
51+
```text
52+
ax mod m = 1
53+
```
54+
55+
More information regarding how to find a Modular Multiplicative Inverse and what it means can be found in the [related Wikipedia article][mmi].
56+
57+
## General Examples
58+
59+
- Encrypting `"test"` gives `"ybty"` with the key `a = 5`, `b = 7`
60+
- Decrypting `"ybty"` gives `"test"` with the key `a = 5`, `b = 7`
61+
- Decrypting `"ybty"` gives `"lqul"` with the wrong key `a = 11`, `b = 7`
62+
- Decrypting `"kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx"` gives `"thequickbrownfoxjumpsoverthelazydog"` with the key `a = 19`, `b = 13`
63+
- Encrypting `"test"` with the key `a = 18`, `b = 13` is an error because `18` and `26` are not coprime
64+
65+
## Example of finding a Modular Multiplicative Inverse (MMI)
66+
67+
Finding MMI for `a = 15`:
68+
69+
- `(15 * x) mod 26 = 1`
70+
- `(15 * 7) mod 26 = 1`, ie. `105 mod 26 = 1`
71+
- `7` is the MMI of `15 mod 26`
72+
73+
[mmi]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
74+
[coprime-integers]: https://en.wikipedia.org/wiki/Coprime_integers
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"keiravillekode"
4+
],
5+
"files": {
6+
"solution": [
7+
"affine-cipher/affine-cipher.factor"
8+
],
9+
"test": [
10+
"affine-cipher/affine-cipher-tests.factor"
11+
],
12+
"example": [
13+
".meta/example.factor"
14+
]
15+
},
16+
"blurb": "Create an implementation of the Affine cipher, an ancient encryption algorithm from the Middle East.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Affine_cipher"
19+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
USING: combinators grouping kernel locals math math.functions
2+
ranges sequences sets strings unicode ;
3+
IN: affine-cipher
4+
5+
: check-coprime ( a -- a )
6+
dup 26 gcd nip 1 = [ "a and m must be coprime." throw ] unless ;
7+
8+
: mmi ( a -- a^-1 )
9+
26 [1..b] [ over * 26 mod 1 = ] find nip nip ;
10+
11+
: group5 ( str -- str ) 5 group " " join ;
12+
13+
:: encode ( phrase a b -- cipher )
14+
a check-coprime drop
15+
phrase >lower
16+
[ [ Letter? ] [ digit? ] bi or ] filter
17+
[| ch |
18+
ch digit?
19+
[ ch ]
20+
[ ch CHAR: a - a * b + 26 mod CHAR: a + ] if
21+
] map group5 ;
22+
23+
:: decode ( phrase a b -- plain )
24+
a check-coprime mmi :> a-inv
25+
phrase " " without
26+
[| ch |
27+
ch digit?
28+
[ ch ]
29+
[ ch CHAR: a - b - a-inv * 26 mod dup 0 < [ 26 + ] when CHAR: a + ] if
30+
] map ;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module AffineCipher
2+
3+
function gen_test_case(case)
4+
phrase = escape_factor(case["input"]["phrase"])
5+
a = Int(case["input"]["key"]["a"])
6+
b = Int(case["input"]["key"]["b"])
7+
prop = case["property"]
8+
expected = case["expected"]
9+
if expected isa Dict
10+
msg = expected["error"]
11+
return """[ "$(phrase)" $(a) $(b) $(prop) ]\n[ "$(msg)" = ] must-fail-with"""
12+
else
13+
return """{ "$(expected)" }\n[ "$(phrase)" $(a) $(b) $(prop) ] unit-test"""
14+
end
15+
end
16+
17+
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"description": "encode boundary characters",
4+
"property": "encode",
5+
"input": {"phrase": "/09:@AMNZ[`amnz{", "key": {"a": 25, "b": 12}},
6+
"expected": "09maz nmazn"
7+
}
8+
]

0 commit comments

Comments
 (0)