aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2024-03-01 10:18:42 +0000
committerGitHub <noreply@github.com>2024-03-01 10:18:42 +0000
commitec7394aca9aada57aba1478adc300edcce2ade3b (patch)
tree3b43ff33812c9c9682f63fac8c14e7ccd4a6b067
parenta6dfa08f00229a9ca8c423e3829253e3b7d398df (diff)
parent534715243e65728db4ee90b5ee5dee644ce71dac (diff)
downloadperlweeklychallenge-club-ec7394aca9aada57aba1478adc300edcce2ade3b.tar.gz
perlweeklychallenge-club-ec7394aca9aada57aba1478adc300edcce2ade3b.tar.bz2
perlweeklychallenge-club-ec7394aca9aada57aba1478adc300edcce2ade3b.zip
Merge pull request #9677 from Archargelod/nim-archar-w254
weeks 14-26, 258 in Nim
-rw-r--r--challenge-014/archargelod/README1
-rwxr-xr-xchallenge-014/archargelod/nim/ch_1.nim52
-rwxr-xr-xchallenge-014/archargelod/nim/ch_2.nim38
-rw-r--r--challenge-015/archargelod/README1
-rwxr-xr-xchallenge-015/archargelod/nim/ch_1.nim48
-rwxr-xr-xchallenge-015/archargelod/nim/ch_2.nim49
-rw-r--r--challenge-016/archargelod/README1
-rwxr-xr-xchallenge-016/archargelod/nim/ch_1.nim21
-rwxr-xr-xchallenge-016/archargelod/nim/ch_2.nim49
-rw-r--r--challenge-017/archargelod/README1
-rwxr-xr-xchallenge-017/archargelod/nim/ch_1.nim16
-rwxr-xr-xchallenge-017/archargelod/nim/ch_2.nim96
-rw-r--r--challenge-018/archargelod/README1
-rwxr-xr-xchallenge-018/archargelod/nim/ch_1.nim33
-rwxr-xr-xchallenge-018/archargelod/nim/ch_2.nim66
-rw-r--r--challenge-019/archargelod/README1
-rwxr-xr-xchallenge-019/archargelod/nim/ch_1.nim16
-rwxr-xr-xchallenge-019/archargelod/nim/ch_2.nim40
-rw-r--r--challenge-020/archargelod/README1
-rwxr-xr-xchallenge-020/archargelod/nim/ch_1.nim22
-rwxr-xr-xchallenge-020/archargelod/nim/ch_2.nim28
-rw-r--r--challenge-021/archargelod/README1
-rwxr-xr-xchallenge-021/archargelod/nim/ch_1.nim16
-rwxr-xr-xchallenge-021/archargelod/nim/ch_2.nim71
-rw-r--r--challenge-022/archargelod/README1
-rwxr-xr-xchallenge-022/archargelod/nim/ch_1.nim48
-rwxr-xr-xchallenge-022/archargelod/nim/ch_2.nim55
-rw-r--r--challenge-023/archargelod/README1
-rwxr-xr-xchallenge-023/archargelod/nim/ch_1.nim25
-rwxr-xr-xchallenge-023/archargelod/nim/ch_2.nim39
-rw-r--r--challenge-024/archargelod/README1
-rwxr-xr-xchallenge-024/archargelod/nim/ch_1.nim1
-rwxr-xr-xchallenge-024/archargelod/nim/ch_2.nim43
-rw-r--r--challenge-025/archargelod/README1
-rwxr-xr-xchallenge-025/archargelod/nim/ch_1.nim68
-rwxr-xr-xchallenge-025/archargelod/nim/ch_2.nim57
-rw-r--r--challenge-026/archargelod/README1
-rwxr-xr-xchallenge-026/archargelod/nim/ch_1.nim26
-rwxr-xr-xchallenge-026/archargelod/nim/ch_2.nim23
-rwxr-xr-xchallenge-258/archargelod/nim/ch_1.nim22
-rwxr-xr-xchallenge-258/archargelod/nim/ch_2.nim22
41 files changed, 1103 insertions, 0 deletions
diff --git a/challenge-014/archargelod/README b/challenge-014/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-014/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-014/archargelod/nim/ch_1.nim b/challenge-014/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..f5f4009e0e
--- /dev/null
+++ b/challenge-014/archargelod/nim/ch_1.nim
@@ -0,0 +1,52 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/tables
+
+proc vanEckNth*(nth: Natural): int =
+ ## returns nth term of Van Eck's sequence
+ ## starting from 0-th term: [a(0) = 0]
+ var lastPos: Table[int, int]
+
+ var prev = 0
+ for n in 1 .. nth:
+ if prev notin lastPos:
+ lastPos[prev] = n - 1
+ prev = 0
+ continue
+
+ let m = lastPos[prev]
+ lastPos[prev] = n - 1
+ prev = n - 1 - m
+
+ result = prev
+
+proc vanEckSequence*(count: Positive): seq[int] =
+ ## blazingly fast!
+ ## sub 1ms with 20_000 terms
+ result = newSeq[int](count)
+ var lastPos: Table[int, int]
+
+ for n in 1 ..< count:
+ let prev = result[n - 1]
+ if prev notin lastPos:
+ lastPos[prev] = n - 1
+ continue
+
+ let m = lastPos[prev]
+ lastPos[prev] = n - 1
+ result[n] = n - 1 - m
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Count = 97
+ Expected = [
+ 0, 0, 1, 0, 2, 0, 2, 2, 1, 6, 0, 5, 0, 2, 6, 5, 4, 0, 5, 3, 0, 3, 2, 9, 0, 4, 9,
+ 3, 6, 14, 0, 6, 3, 5, 15, 0, 5, 3, 5, 2, 17, 0, 6, 11, 0, 3, 8, 0, 3, 3, 1, 42, 0,
+ 5, 15, 20, 0, 4, 32, 0, 3, 11, 18, 0, 4, 7, 0, 3, 7, 3, 2, 31, 0, 6, 31, 3, 6, 3,
+ 2, 8, 33, 0, 9, 56, 0, 3, 8, 7, 19, 0, 5, 37, 0, 3, 8, 8, 1
+ ]
+
+ suite "Van Eck's sequence":
+ test "first 97 terms":
+ check vanEckSequence(Count) == Expected
diff --git a/challenge-014/archargelod/nim/ch_2.nim b/challenge-014/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..cd87276a58
--- /dev/null
+++ b/challenge-014/archargelod/nim/ch_2.nim
@@ -0,0 +1,38 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/[sets]
+
+const USStates = [
+ "al", "ak", "az", "ar", "ca", "co", "ct", "de", "fl", "ga", "hi", "id", "il", "in",
+ "ia", "ks", "ky", "la", "me", "md", "ma", "mi", "mn", "ms", "mo", "mt", "ne", "nv",
+ "nh", "nj", "nm", "ny", "nc", "nd", "oh", "ok", "or", "pa", "ri", "sc", "sd", "tn",
+ "tx", "ut", "vt", "va", "wa", "wv", "wi", "wy"
+].toHashSet()
+
+proc isComposableFrom(word: string, syllables: HashSet[string]): bool =
+ if word.len mod 2 != 0:
+ return false
+
+ for i in countUp(0, word.len - 2, 2):
+ if word[i .. i + 1] notin syllables:
+ return false
+
+ true
+
+proc longestWordFromSyllables(dictFile: string, syllables: HashSet[string]): string =
+ let dict = open(dictFile)
+ defer:
+ dict.close()
+
+ for word in lines dict:
+ if word.len < result.len:
+ continue
+ if word.isComposableFrom syllables:
+ result = word
+
+when isMainModule:
+ import std/strformat
+
+ const DictPath = "/usr/share/dict/words"
+ let res = DictPath.longestWordFromSyllables(USStates)
+
+ echo &"Longest word composable from US 2-letter State names is '{res}'"
diff --git a/challenge-015/archargelod/README b/challenge-015/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-015/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-015/archargelod/nim/ch_1.nim b/challenge-015/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..dd645f1540
--- /dev/null
+++ b/challenge-015/archargelod/nim/ch_1.nim
@@ -0,0 +1,48 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/math
+
+proc primeSieve(max: int): seq[int] =
+ var sieve = newSeq[bool](max)
+
+ for p in 2 .. sieve.high:
+ if sieve[p]:
+ continue
+
+ for i in countUp(2 * p, sieve.high, p):
+ sieve[i] = true
+
+ for i, notPrime in sieve[2 ..^ 1]:
+ if not notPrime:
+ result.add i + 2
+
+proc tierPrimesByStrength(
+ primes: openArray[int]
+): tuple[weak, balanced, strong: seq[int]] =
+ for pInd, prime in primes[0 ..^ 2]:
+ if pInd == 0:
+ continue
+ let mean = (primes[pInd - 1] + primes[pInd + 1]) / 2
+
+ if almostEqual(prime.float, mean):
+ result.balanced.add prime
+ elif prime.float > mean:
+ result.strong.add prime
+ elif prime.float < mean:
+ result.weak.add prime
+ else:
+ raiseAssert("Error: bad float comparison.")
+
+when isMainModule:
+ import std/unittest
+
+ const
+ ExpectedWeak = [3, 7, 13, 19, 23, 31, 43, 47, 61, 73]
+ ExpectedStrong = [11, 17, 29, 37, 41, 59, 67, 71, 79, 97]
+
+ suite "Strong and weak primes":
+ let (weak, _, strong) = tierPrimesByStrength(primeSieve(200))
+
+ test "first 10 weak primes":
+ check weak[0 ..< 10] == ExpectedWeak
+ test "first 10 strong primes":
+ check strong[0 ..< 10] == ExpectedStrong
diff --git a/challenge-015/archargelod/nim/ch_2.nim b/challenge-015/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..18770f6d15
--- /dev/null
+++ b/challenge-015/archargelod/nim/ch_2.nim
@@ -0,0 +1,49 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/[strutils, math]
+
+type cipherMode = enum
+ Encode
+ Decode
+
+proc alphaOrd(c: char): int =
+ c.ord - 'a'.ord
+
+proc rot(c: sink char, shift: int): char =
+ char('a'.ord + euclMod(c.alphaOrd + shift, 26))
+
+proc vigenereCipher(plaintext: var string, key: string, mode = Encode) =
+ for i, c in plaintext:
+ if c notin Letters:
+ continue
+
+ let key = block:
+ let tmp = key[i mod key.len].toLowerAscii.alphaOrd
+ if mode == Decode: -tmp else: tmp
+
+ plaintext[i] = c.toLowerAscii.rot(key)
+
+proc encode*(plaintext: var string, key: string) =
+ vigenereCipher(plaintext, key, Encode)
+
+proc decode*(plaintext: var string, key: string) =
+ vigenereCipher(plaintext, key, Decode)
+
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Plaintext = "attacking tonight"
+ Key = "OCULORHINOLARINGOLOGY"
+ ExpectedEncoded = "ovnlqbpvt eoeqtnh"
+
+ suite "Vigenère cipher":
+ var text = Plaintext
+
+ test "encode sample text":
+ text.encode(Key)
+ check text == ExpectedEncoded
+
+ test "decode sample text":
+ text.decode(Key)
+ check text == Plaintext
diff --git a/challenge-016/archargelod/README b/challenge-016/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-016/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-016/archargelod/nim/ch_1.nim b/challenge-016/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..a5cc61c3f6
--- /dev/null
+++ b/challenge-016/archargelod/nim/ch_1.nim
@@ -0,0 +1,21 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+
+proc percent(value: float, pct: 0..100): float =
+ value / 100'f * pct.float
+
+proc largestPieceOfPieIndex(): int =
+ var amount = 1'f
+ var largest = 0'f
+
+ for i in 1..100:
+ let piece = amount.percent(i)
+ if piece <= largest:
+ return i - 1
+ largest = piece
+ amount -= piece
+
+when isMainModule:
+ import std/strformat
+
+ let index = largestPieceOfPieIndex()
+ echo &"{index}th guest gets the largest piece of pie."
diff --git a/challenge-016/archargelod/nim/ch_2.nim b/challenge-016/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..52f8e2202b
--- /dev/null
+++ b/challenge-016/archargelod/nim/ch_2.nim
@@ -0,0 +1,49 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/strutils
+import pkg/nimcrypto/sha2 ## `$ nimble install nimcrypto`
+
+const
+ DecodedLength = 25
+ ChecksumLength = 4
+
+func base58Decode(input: string): array[DecodedLength, byte] =
+ ## Taken from https://rosettacode.org/wiki/Bitcoin/address_validation#Nim
+ ## Tried to implement this myself, but failed to find easy to understand algorithm.
+ const Base = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+
+ for c in input:
+ var n = Base.find(c)
+ if n < 0:
+ raise newException(ValueError, "invalid character: " & c)
+
+ for i in countdown(result.high, 0):
+ n += Base.len * result[i].int
+ result[i] = byte(n and 255)
+ n = n div 256
+
+ if n != 0:
+ raise newException(ValueError, "decoded address is too long")
+
+func validBtcChecksum(address: openarray[byte]): bool =
+ let digest1 = sha256.digest(address.toOpenArray(0, address.high - ChecksumLength)).data
+ let digest2 = sha256.digest(digest1).data
+ digest2[0 ..< ChecksumLength] == address[^ChecksumLength ..^ 1]
+
+func isValidBTCAddress(address: string): bool =
+ address.len >= 26 and address[0] in {'1', '3'} and
+ validBtcChecksum(address.base58Decode())
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Valid = ["1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2", "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"]
+ Invalid = ["3J98t1WpEZ73CNmQviecrnyiWrDEADBEEF"]
+
+ suite "Validate bitcoin address":
+ test "Example 1":
+ check isValidBTCAddress Valid[0]
+ test "Example 2":
+ check isValidBTCAddress Valid[1]
+ test "Invalid address":
+ check not isValidBtcAddress Invalid[0]
diff --git a/challenge-017/archargelod/README b/challenge-017/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-017/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-017/archargelod/nim/ch_1.nim b/challenge-017/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..da820ff5d7
--- /dev/null
+++ b/challenge-017/archargelod/nim/ch_1.nim
@@ -0,0 +1,16 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+
+proc ackermann(m, n: Natural): int =
+ if m == 0:
+ n + 1
+ elif n == 0:
+ ackermann(m - 1, 1)
+ else:
+ ackermann(m - 1, ackermann(m, n - 1))
+
+when isMainModule:
+ import std/unittest
+
+ suite "Ackermann function":
+ test "A(1, 2) == 4":
+ check ackermann(1, 2) == 4
diff --git a/challenge-017/archargelod/nim/ch_2.nim b/challenge-017/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..d64c57515f
--- /dev/null
+++ b/challenge-017/archargelod/nim/ch_2.nim
@@ -0,0 +1,96 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/[strutils, parseutils, options]
+
+type Url = object
+ scheme, userinfo, host: string
+ port = 80
+ path, query, fragment: string
+
+template consume(input: string, chars: openarray[char]): bool =
+ var res = true
+ for i, c in chars:
+ if ind < input.len and input[ind] == c:
+ inc ind
+ else:
+ ind -= i
+ res = false
+ break
+ res
+
+proc parseUrl(input: string): Url =
+ result = Url()
+
+ var ind = 0
+ let schemeEnd = input.skipUntil({':'}, ind)
+ result.scheme = input[0 ..< schemeEnd]
+ ind += schemeEnd
+
+ if not input.consume(":"):
+ raiseAssert("expected ':'")
+
+ if input.consume("//"):
+ # user:password pair (optional)
+ let userinfoEnd = input.skipUntil({'@'}, ind)
+ if ind + userinfoEnd < input.len:
+ result.userinfo = input[ind ..< ind + userinfoEnd]
+ ind += userInfoEnd + 1
+
+ # hostname
+ let hostEnd = input.skipUntil({':', '/'}, ind)
+ if ind + hostEnd > input.high:
+ raiseAssert("expected ':' or '/'")
+ result.host = input[ind ..< ind + hostEnd]
+ ind += hostEnd
+
+ # port (optional)
+ if input.consume(":"):
+ let portEnd = input.skipWhile(Digits, ind)
+ result.port = parseInt input[ind ..< ind + portEnd]
+ ind += portEnd
+
+ # path
+ let pathEnd = input.skipUntil({'?', '#'}, ind)
+ result.path = input[ind ..< ind + pathEnd]
+ ind += pathEnd
+
+ # query (optional)
+ if input.consume("?"):
+ let queryEnd = input.skipUntil({'#'}, ind)
+ result.query = input[ind ..< ind + queryEnd]
+ ind += queryEnd
+
+ # match fragment (optional)
+ if input.consume("#"):
+ result.fragment = input[ind ..^ 1]
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Test = [
+ "jdbc://user:password@localhost:3306/pwc?profile=true#h1",
+ "file:/home/test/hello.txt"
+ ]
+ Expected = [
+ Url(
+ scheme: "jdbc",
+ userinfo: "user:password",
+ host: "localhost",
+ port: 3306,
+ path: "/pwc",
+ query: "profile=true",
+ fragment: "h1",
+ ),
+ Url(
+ scheme: "file",
+ port: 80,
+ path: "/home/test/hello.txt",
+ )
+ ]
+
+ suite "URL parsing":
+ test "Example 1":
+ check parseUrl(Test[0]) == Expected[0]
+
+ test "Minimal Example":
+ check parseUrl(Test[1]) == Expected[1]
diff --git a/challenge-018/archargelod/README b/challenge-018/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-018/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-018/archargelod/nim/ch_1.nim b/challenge-018/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..247f7682b5
--- /dev/null
+++ b/challenge-018/archargelod/nim/ch_1.nim
@@ -0,0 +1,33 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/[sets, strutils, sequtils]
+
+proc longest(lines: sink openarray[string]): string =
+ for line in lines:
+ if line.len > result.len:
+ result = line
+
+proc longestCommonSubstring(lines: openarray[string]): string =
+ var substrings: HashSet[string]
+
+ let first = lines[0]
+ for i in 0 .. first.high:
+ for j in i .. first.high:
+ if i == 0 and j == first.high: continue
+ substrings.incl first[i .. j]
+
+ for line in lines:
+ var newSubstrings: HashSet[string]
+ for sub in substrings:
+ if sub in line:
+ newSubstrings.incl sub
+ substrings = newSubstrings
+
+ substrings.toSeq.longest()
+
+
+when isMainModule:
+ import std/unittest
+
+ suite "Longest common substring":
+ test "Example 1":
+ check longestCommonSubstring(["ABABC", "BABCA", "ABCBA"]) == "ABC"
diff --git a/challenge-018/archargelod/nim/ch_2.nim b/challenge-018/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..5da5e8e0db
--- /dev/null
+++ b/challenge-018/archargelod/nim/ch_2.nim
@@ -0,0 +1,66 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+type
+ PriorityQueue*[T] = object
+ data: seq[tuple [item: T, priority: Natural]]
+
+proc isEmpty*(q: PriorityQueue): bool =
+ q.data.len == 0
+
+proc add*[T](q: var PriorityQueue[T], item: sink T, priority: Natural = 0) =
+ ## add item to the queue with specified priority
+ ## 0 is max priority, 1 is less priority than 0 and so on
+ proc findBestPos(data: seq[tuple [item: T, priority: Natural]], priority: int): int =
+ # binary search
+ var
+ pivot = data.len div 2
+ left = 0
+ right = data.high
+
+ while right - left > 1:
+ if priority < data[pivot].priority:
+ left = pivot
+ else:
+ right = pivot
+ pivot = left + (right - left) div 2
+
+ right
+
+ if q.isEmpty or priority <= q.data[^1].priority:
+ q.data.add (item, priority)
+ elif priority > q.data[0].priority:
+ q.data.insert((item, priority), 0)
+ else:
+ var pos = q.data.findBestPos(priority)
+ q.data.insert((item, priority), pos)
+
+proc pop*[T](q: var PriorityQueue[T]): T =
+ ## remove the item with minimum priority value from the queue
+ ## and return it
+ if q.isEmpty:
+ raise newException(ValueError, "can't pop value; the queue is empty")
+ q.data.pop().item
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Test = [
+ ("World", 20),
+ (",", 5),
+ ("!", 1000),
+ ("Hello", 0),
+ (" ", 10),
+ ]
+ Expected = "Hello, World!"
+
+ suite "Priority Queue":
+ test "Hello world ordered with priority":
+ var queue: PriorityQueue[string]
+ var res = newStringOfCap(Expected.len)
+ for (word, prio) in Test:
+ queue.add(word, prio)
+
+ while not queue.isEmpty:
+ res &= queue.pop()
+
+ check res == Expected
diff --git a/challenge-019/archargelod/README b/challenge-019/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-019/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-019/archargelod/nim/ch_1.nim b/challenge-019/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..56eaf65648
--- /dev/null
+++ b/challenge-019/archargelod/nim/ch_1.nim
@@ -0,0 +1,16 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+## The only way for a month to have 5 full weekends (Fri, Sat, Sun) is
+## to be 31 days long and start on Friday:
+##
+## .... 567
+## 1234 567
+## 1234 567
+## 1234 567
+## 1234 567
+import std/[times]
+
+for year in 1900..2019:
+ for month in Month:
+ if getDaysInMonth(month, year) == 31 and
+ getDayOfWeek(1, month, year) == dFri:
+ echo ($month)[0..2], '-', year
diff --git a/challenge-019/archargelod/nim/ch_2.nim b/challenge-019/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..e6e72d9db2
--- /dev/null
+++ b/challenge-019/archargelod/nim/ch_2.nim
@@ -0,0 +1,40 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import std/[parseutils, strutils]
+
+proc wrap*(text: var string, lineWidth: Positive = 80) =
+ var spaceLeft: int = lineWidth
+ var index = 0
+ while index < text.high:
+ let wordWidth = text.skipUntil(Whitespace, index)
+ let spaceWidth = text.skipWhile(Whitespace, index + wordWidth)
+
+ #echo (text[index..<index + wordWidth], wordWidth + spaceWidth, spaceLeft)
+ if wordWidth + spaceWidth > spaceLeft:
+ text.insert("\n", index)
+ spaceLeft = lineWidth - (wordWidth + spaceWidth)
+ inc index # for added newLine
+ else:
+ spaceLeft -= wordWidth + spaceWidth
+ index += wordWidth + spaceWidth
+
+proc wrapped*(text: sink string, lineWidth: Positive = 80): string =
+ result = text
+ result.wrap(lineWidth)
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Text = """
+A simple way to do word wrapping is to use a greedy algorithm that puts as many words on a line as possible, then moving on to the next line to do the same until there are no more words left to place. This method is used by many modern word processors, such as OpenOffice.org Writer and Microsoft Word. This algorithm always uses the minimum possible number of lines but may lead to lines of widely varying lengths."""
+ Expected = """
+A simple way to do word wrapping is to use a greedy algorithm that puts as many
+words on a line as possible, then moving on to the next line to do the same
+until there are no more words left to place. This method is used by many modern
+word processors, such as OpenOffice.org Writer and Microsoft Word. This
+algorithm always uses the minimum possible number of lines but may lead to
+lines of widely varying lengths."""
+
+ suite "Greedy line wrapping":
+ test "wikipedia paragraph":
+ check Text.wrapped(80) == Expected
diff --git a/challenge-020/archargelod/README b/challenge-020/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-020/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-020/archargelod/nim/ch_1.nim b/challenge-020/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..55823e7de9
--- /dev/null
+++ b/challenge-020/archargelod/nim/ch_1.nim
@@ -0,0 +1,22 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+
+proc splitSameChars(s: string): seq[string] =
+ result = newSeqOfCap[string](s.len)
+ result.add $s[0]
+
+ for c in s.toOpenArray(1, s.high):
+ if c != result[^1][^1]:
+ result.add $c
+ else:
+ result[^1] &= c
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Test = "ABBCDEEF"
+ Expected = ["A", "BB", "C", "D", "EE", "F"]
+
+ suite "Same-character groups":
+ test "Example 1":
+ check Test.splitSameChars() == Expected
diff --git a/challenge-020/archargelod/nim/ch_2.nim b/challenge-020/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..f8d47e2d01
--- /dev/null
+++ b/challenge-020/archargelod/nim/ch_2.nim
@@ -0,0 +1,28 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+
+proc aliquotSum[T: SomeInteger](n: T): T =
+ for d in T(1) .. T(n div 2):
+ if n mod d == 0:
+ result += d
+
+proc smallestAmicablePair(): (int, int) =
+ var invalid: set[int16] # could be useful for later pairs
+
+ for first in 1'i16 .. high(int16):
+ if first in invalid: continue
+
+ let second = aliquotSum(first)
+ if second != first and aliquotSum(second) == first:
+ return (first.int, second.int)
+
+ invalid.incl first
+ invalid.incl second
+
+when isMainModule:
+ import std/unittest
+
+ const Expected = (220, 284)
+
+ suite "Smallest Amicable Number pair":
+ test "first pair is 220 and 284":
+ check smallestAmicablePair() == Expected
diff --git a/challenge-021/archargelod/README b/challenge-021/archargelod/README
new file mode 100644
index 0000000000..6cd57e1074
--- /dev/null
+++ b/challenge-021/archargelod/README
@@ -0,0 +1 @@
+Solution by archargelod
diff --git a/challenge-021/archargelod/nim/ch_1.nim b/challenge-021/archargelod/nim/ch_1.nim
new file mode 100755
index 0000000000..b93d56e1ef
--- /dev/null
+++ b/challenge-021/archargelod/nim/ch_1.nim
@@ -0,0 +1,16 @@
+#!/usr/bin/env -S nim r -d:release --verbosity:0 --hints:off
+import pkg/mapm
+
+proc approximateE(precision: int): Mapm =
+ let n = 1e41'm
+ pow(1'm + divide(1'm, n, precision), n, precision).round(precision)
+
+when isMainModule:
+ import std/unittest
+
+ const
+ Expected = "2.7182818284590452353602874713526624977572"
+
+ suite "Euler's number":
+ test "40-digit precision":
+ check $approximateE(40) == Expected
diff --git a/challenge-021/archargelod/nim/ch_2.nim b/challenge-021/archargelod/nim/ch_2.nim
new file mode 100755
index 0000000000..ec3b22ded2
--- /dev/null
+++ b/