Skip to content

Add 4 new Common Lisp exercises #93

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
19 changes: 19 additions & 0 deletions common-lisp/isbn-verifier/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"PaulT89"
],
"files": {
"solution": [
"isbn-verifier.lisp"
],
"test": [
"isbn-verifier-test.lisp"
],
"example": [
".meta/example.lisp"
]
},
"blurb": "Check if a given string is a valid ISBN-10 number.",
"source": "Converting a string into a number and some basic processing utilizing a relatable real world example.",
"source_url": "https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation"
}
1 change: 1 addition & 0 deletions common-lisp/isbn-verifier/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"common-lisp","exercise":"isbn-verifier","id":"15547c7317a04ba0ba72f69af94e25d7","url":"https://exercism.org/tracks/common-lisp/exercises/isbn-verifier","handle":"wsgac","is_requester":true,"auto_approve":false}
94 changes: 94 additions & 0 deletions common-lisp/isbn-verifier/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Help

## Running the tests

Start the REPL from the directory that you downloaded the exercise to.

You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests.

If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`.

If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`.

## Submitting your solution

You can submit your solution using the `exercism submit isbn-verifier.lisp` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp)
- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

## Setup

Check out [Installing Common
Lisp][track-install] for
instructions to get started or take a look at the guides available in
the [track's documentation pages][track-docs].

## Where to look for help

Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information.

There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help.

## Formatting

While Common Lisp doesn't care about indentation and layout of code,
nor whether you use spaces or tabs, this is an important consideration
for submissions to exercism.
Exercism.org's code widget cannot handle mixing of tab and space characters well so using only spaces is recommended to make
the code more readable to the human reviewers.
Please review your editors settings on how to accomplish this.
Below are instructions for popular editors for Common Lisp.

### VIM

Use the following commands to ensure VIM uses only spaces for
indentation:

```vimscript
:set tabstop=2
:set shiftwidth=2
:set expandtab
```

(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can
be added to your `~/.vimrc` file to use it all the time.

### Emacs

Emacs is very well suited for editing Common Lisp and has many
powerful add-on packages available. The only thing that one needs to
do with a stock emacs to make it work well with exercism.org is to
evaluate the following code:

`(setq-default indent-tabs-mode nil)`

This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in
order to have it set whenever Emacs is launched.

One suggested add-on for Emacs and Common Lisp is
[SLIME][slime] which offers tight integration
with the REPL; making iterative coding and testing very easy.

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution.

[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community
[slime]: https://github.com/slime/slime
[track-docs]: /docs/tracks/common-lisp
[track-install]: /docs/tracks/common-lisp/installation
[track-resources]: /docs/tracks/common-lisp/resources
57 changes: 57 additions & 0 deletions common-lisp/isbn-verifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ISBN Verifier

Welcome to ISBN Verifier on Exercism's Common Lisp Track.
If you need help running the tests or submitting your code, check out `HELP.md`.

## Instructions

The [ISBN-10 verification process][isbn-verification] is used to validate book identification numbers.
These normally contain dashes and look like: `3-598-21508-8`

## ISBN

The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only).
In the case the check character is an X, this represents the value '10'.
These may be communicated with or without hyphens, and can be checked for their validity by the following formula:

```text
(d₁ * 10 + d₂ * 9 + d₃ * 8 + d₄ * 7 + d₅ * 6 + d₆ * 5 + d₇ * 4 + d₈ * 3 + d₉ * 2 + d₁₀ * 1) mod 11 == 0
```

If the result is 0, then it is a valid ISBN-10, otherwise it is invalid.

## Example

Let's take the ISBN-10 `3-598-21508-8`.
We plug it in to the formula, and get:

```text
(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0
```

Since the result is 0, this proves that our ISBN is valid.

## Task

Given a string the program should check if the provided string is a valid ISBN-10.
Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN.

The program should be able to verify ISBN-10 both with and without separating dashes.

## Caveats

Converting from strings to numbers can be tricky in certain languages.
Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10').
For instance `3-598-21507-X` is a valid ISBN-10.

[isbn-verification]: https://en.wikipedia.org/wiki/International_Standard_Book_Number

## Source

### Created by

- @PaulT89

### Based on

Converting a string into a number and some basic processing utilizing a relatable real world example. - https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation
96 changes: 96 additions & 0 deletions common-lisp/isbn-verifier/isbn-verifier-test.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
;; Ensures that isbn-verifier.lisp and the testing library are always loaded
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "isbn-verifier")
(quicklisp-client:quickload :fiveam))

;; Defines the testing package with symbols from isbn-verifier and FiveAM in scope
;; The `run-tests` function is exported for use by both the user and test-runner
(defpackage :isbn-verifier-test
(:use :cl :fiveam)
(:export :run-tests))

;; Enter the testing package
(in-package :isbn-verifier-test)

;; Define and enter a new FiveAM test-suite
(def-suite* isbn-verifier-suite)

(test valid-isbn
(let ((isbn "3-598-21508-8"))
(is-true (isbn-verifier:validp isbn))))

(test invalid-isbn-check-digit
(let ((isbn "3-598-21508-9"))
(is-false (isbn-verifier:validp isbn))))

(test valid-isbn-with-a-check-digit-of-10
(let ((isbn "3-598-21507-X"))
(is-true (isbn-verifier:validp isbn))))

(test check-digit-is-a-character-other-than-x
(let ((isbn "3-598-21507-A"))
(is-false (isbn-verifier:validp isbn))))

(test invalid-check-digit-in-isbn-is-not-treated-as-zero
(let ((isbn "4-598-21507-B"))
(is-false (isbn-verifier:validp isbn))))

(test invalid-character-in-isbn-is-not-treated-as-zero
(let ((isbn "3-598-P1581-X"))
(is-false (isbn-verifier:validp isbn))))

(test x-is-only-valid-as-a-check-digit
(let ((isbn "3-598-2X507-9"))
(is-false (isbn-verifier:validp isbn))))

(test valid-isbn-without-separating-dashes
(let ((isbn "3598215088"))
(is-true (isbn-verifier:validp isbn))))

(test isbn-without-separating-dashes-and-x-as-check-digit
(let ((isbn "359821507X"))
(is-true (isbn-verifier:validp isbn))))

(test isbn-without-check-digit-and-dashes
(let ((isbn "359821507"))
(is-false (isbn-verifier:validp isbn))))

(test too-long-isbn-and-no-dashes
(let ((isbn "3598215078X"))
(is-false (isbn-verifier:validp isbn))))

(test too-short-isbn
(let ((isbn "00"))
(is-false (isbn-verifier:validp isbn))))

(test isbn-without-check-digit
(let ((isbn "3-598-21507"))
(is-false (isbn-verifier:validp isbn))))

(test check-digit-of-x-should-not-be-used-for-0
(let ((isbn "3-598-21515-X"))
(is-false (isbn-verifier:validp isbn))))

(test empty-isbn
(let ((isbn ""))
(is-false (isbn-verifier:validp isbn))))

(test input-is-9-characters
(let ((isbn "134456729"))
(is-false (isbn-verifier:validp isbn))))

(test invalid-characters-are-not-ignored-after-checking-length
(let ((isbn "3132P34035"))
(is-false (isbn-verifier:validp isbn))))

(test invalid-characters-are-not-ignored-before-checking-length
(let ((isbn "3598P215088"))
(is-false (isbn-verifier:validp isbn))))

(test input-is-too-long-but-contains-a-valid-isbn
(let ((isbn "98245726788"))
(is-false (isbn-verifier:validp isbn))))

(defun run-tests (&optional (test-or-suite 'isbn-verifier-suite))
"Provides human readable results of test run. Default to entire suite."
(run! test-or-suite))
32 changes: 32 additions & 0 deletions common-lisp/isbn-verifier/isbn-verifier.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(defpackage :isbn-verifier
(:use :cl)
(:export :validp))

(in-package :isbn-verifier)

(define-condition invalid-character (error)
())

(defun char-to-number (c i)
(cond
((char<= #\0 c #\9)
(- (char-code c) (char-code #\0)))
((and (char-equal c #\x) (= i 10)) 10)
((char-equal c #\-) nil)
(t (error 'invalid-character))))

(defun validp (isbn)
(handler-case (loop
with i = 1
for c in (coerce isbn 'list)
for cc = (char-to-number c i)
if cc
do (incf i)
and collect cc into digits
finally (return (and (= 10 (length digits))
(zerop (mod (loop
for d in digits
for i from 10 downto 1
sum (* d i)) 11)))))
(invalid-character ()
nil)))
21 changes: 21 additions & 0 deletions common-lisp/matching-brackets/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"authors": [
"PaulT89"
],
"contributors": [
"verdammelt"
],
"files": {
"solution": [
"matching-brackets.lisp"
],
"test": [
"matching-brackets-test.lisp"
],
"example": [
".meta/example.lisp"
]
},
"blurb": "Make sure the brackets and braces all match.",
"source": "Ginna Baker"
}
1 change: 1 addition & 0 deletions common-lisp/matching-brackets/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"common-lisp","exercise":"matching-brackets","id":"4a62f984a53c42ea8d6e4ab6ed08a8a1","url":"https://exercism.org/tracks/common-lisp/exercises/matching-brackets","handle":"wsgac","is_requester":true,"auto_approve":false}
94 changes: 94 additions & 0 deletions common-lisp/matching-brackets/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Help

## Running the tests

Start the REPL from the directory that you downloaded the exercise to.

You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests.

If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`.

If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`.

## Submitting your solution

You can submit your solution using the `exercism submit matching-brackets.lisp` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp)
- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

## Setup

Check out [Installing Common
Lisp][track-install] for
instructions to get started or take a look at the guides available in
the [track's documentation pages][track-docs].

## Where to look for help

Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information.

There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help.

## Formatting

While Common Lisp doesn't care about indentation and layout of code,
nor whether you use spaces or tabs, this is an important consideration
for submissions to exercism.
Exercism.org's code widget cannot handle mixing of tab and space characters well so using only spaces is recommended to make
the code more readable to the human reviewers.
Please review your editors settings on how to accomplish this.
Below are instructions for popular editors for Common Lisp.

### VIM

Use the following commands to ensure VIM uses only spaces for
indentation:

```vimscript
:set tabstop=2
:set shiftwidth=2
:set expandtab
```

(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can
be added to your `~/.vimrc` file to use it all the time.

### Emacs

Emacs is very well suited for editing Common Lisp and has many
powerful add-on packages available. The only thing that one needs to
do with a stock emacs to make it work well with exercism.org is to
evaluate the following code:

`(setq-default indent-tabs-mode nil)`

This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in
order to have it set whenever Emacs is launched.

One suggested add-on for Emacs and Common Lisp is
[SLIME][slime] which offers tight integration
with the REPL; making iterative coding and testing very easy.

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution.

[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community
[slime]: https://github.com/slime/slime
[track-docs]: /docs/tracks/common-lisp
[track-install]: /docs/tracks/common-lisp/installation
[track-resources]: /docs/tracks/common-lisp/resources
23 changes: 23 additions & 0 deletions common-lisp/matching-brackets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Matching Brackets

Welcome to Matching Brackets on Exercism's Common Lisp Track.
If you need help running the tests or submitting your code, check out `HELP.md`.

## Instructions

Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly.
The string may also contain other characters, which for the purposes of this exercise should be ignored.

## Source

### Created by

- @PaulT89

### Contributed to by

- @verdammelt

### Based on

Ginna Baker
101 changes: 101 additions & 0 deletions common-lisp/matching-brackets/matching-brackets-test.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
;; Ensures that matching-brackets.lisp and the testing library are always loaded
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "matching-brackets")
(quicklisp-client:quickload :fiveam))

;; Defines the testing package with symbols from matching-brackets and FiveAM in scope
;; The `run-tests` function is exported for use by both the user and test-runner
(defpackage :matching-brackets-test
(:use :cl :fiveam)
(:export :run-tests))

;; Enter the testing package
(in-package :matching-brackets-test)

;; Define and enter a new FiveAM test-suite
(def-suite* matching-brackets-suite)

(test paired-square-brackets
(let ((value "[]"))
(is-true (matching-brackets:pairedp value))))

(test empty-string
(let ((value ""))
(is-true (matching-brackets:pairedp value))))

(test unpaired-brackets
(let ((value "[["))
(is-false (matching-brackets:pairedp value))))

(test wrong-ordered-brackets
(let ((value "}{"))
(is-false (matching-brackets:pairedp value))))

(test wrong-closing-bracket
(let ((value "{]"))
(is-false (matching-brackets:pairedp value))))

(test paired-with-whitespace
(let ((value "{ }"))
(is-true (matching-brackets:pairedp value))))

(test partially-paired-brackets
(let ((value "{[])"))
(is-false (matching-brackets:pairedp value))))

(test simple-nested-brackets
(let ((value "{[]}"))
(is-true (matching-brackets:pairedp value))))

(test several-paired-brackets
(let ((value "{}[]"))
(is-true (matching-brackets:pairedp value))))

(test paired-and-nested-brackets
(let ((value "([{}({}[])])"))
(is-true (matching-brackets:pairedp value))))

(test paired-and-wrong-nested-brackets-but-innermost-are-correct
(let ((value "[({}])"))
(is-false (matching-brackets:pairedp value))))

(test unopened-closing-brackets
(let ((value "{[)][]}"))
(is-false (matching-brackets:pairedp value))))

(test unpaired-and-nested-brackets
(let ((value "([{])"))
(is-false (matching-brackets:pairedp value))))

(test paired-and-wrong-nested-brackets
(let ((value "[({]})"))
(is-false (matching-brackets:pairedp value))))

(test paired-and-incomplete-brackets
(let ((value "{}["))
(is-false (matching-brackets:pairedp value))))

(test too-many-closing-brackets
(let ((value "[]]"))
(is-false (matching-brackets:pairedp value))))

(test early-unexpected-brackets
(let ((value ")()"))
(is-false (matching-brackets:pairedp value))))

(test early-mismatched-brackets
(let ((value "{)()"))
(is-false (matching-brackets:pairedp value))))

(test math-expression
(let ((value "(((185 + 223.85) * 15) - 543)/2"))
(is-true (matching-brackets:pairedp value))))

(test complex-latex-expression
(let ((value "\left(\begin{array}{cc} \frac{1}{3} & x\\ \mathrm{e}^{x} &... x^2 \end{array}\right)"))
(is-true (matching-brackets:pairedp value))))

(defun run-tests (&optional (test-or-suite 'matching-brackets-suite))
"Provides human readable results of test run. Default to entire suite."
(run! test-or-suite))

20 changes: 20 additions & 0 deletions common-lisp/matching-brackets/matching-brackets.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(defpackage :matching-brackets
(:use :cl)
(:export :pairedp))

(in-package :matching-brackets)

(defvar *matching-pairs* '((#\[ . #\]) (#\( . #\)) (#\{ . #\})))

(defun pairedp (value)
(loop
with stack = '()
with closing = (mapcar #'cdr *matching-pairs*)
for c in (coerce value 'list)
do (cond
((assoc c *matching-pairs*) (push c stack))
((member c closing)
(if (and stack (eq (cdr (assoc (car stack) *matching-pairs*)) c))
(pop stack)
(return-from pairedp nil))))
finally (return (null stack))))
72 changes: 72 additions & 0 deletions common-lisp/run-length-encoding/#run-length-encoding-test.lisp#
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
;; Ensures that run-length-encoding.lisp and the testing library are always loaded
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "run-length-encoding")
(quicklisp-client:quickload :fiveam))

;; Defines the testing package with symbols from run-length-encoding and FiveAM in scope
;; The `run-tests` function is exported for use by both the user and test-runner
(defpackage :run-length-encoding-test
(:use :cl :fiveam)
(:export :run-tests))

;; Enter the testing package
(in-package :run-length-encoding-test)

;; Define and enter a new FiveAM test-suite
(def-suite* run-length-encoding-suite)

(test empty-string
(let ((plain ""))
(is (string= "" (run-length-encoding:encode plain)))))

(test single-characters-only-are-encoded-without-count
(let ((plain "XYZ"))
(is (string= "XYZ" (run-length-encoding:encode plain)))))

(test string-with-no-single-characters
(let ((plain "AABBBCCCC"))
(is (string= "2A3B4C" (run-length-encoding:encode plain)))))

(test single-characters-mixed-with-repeated-characters
(let ((plain "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"))
(is (string= "12WB12W3B24WB" (run-length-encoding:encode plain)))))

(test multiple-whitespace-mixed-in-string
(let ((plain " hsqq qww "))
(is (string= "2 hs2q q2w2 " (run-length-encoding:encode plain)))))

(test lowercase-characters
(let ((plain "aabbbcccc"))
(is (string= "2a3b4c" (run-length-encoding:encode plain)))))

(test empty-string
(let ((compressed ""))
(is (string= "" (run-length-encoding:decode compressed)))))

(test single-characters-only
(let ((compressed "XYZ"))
(is (string= "XYZ" (run-length-encoding:decode compressed)))))

(test string-with-no-single-characters
(let ((compressed "2A3B4C"))
(is (string= "AABBBCCCC" (run-length-encoding:decode compressed)))))

(test single-characters-with-repeated-characters
(let ((compressed "12WB12W3B24WB"))
(is (string= "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" (run-length-encoding:decode compressed)))))

(test multiple-whitespace-mixed-in-string
(let ((compressed "2 hs2q q2w2 "))
(is (string= " hsqq qww " (run-length-encoding:decode compressed)))))

(test lowercase-string
(let ((compressed "2a3b4c"))
(is (string= "aabbbcccc" (run-length-encoding:decode compressed)))))

(test encode-followed-by-decode-gives-original-string
(let ((plain "zzz ZZ zZ"))
(is (string= plain (run-length-encoding:decode (run-length-encoding:encode plain))))))

(defun run-tests (&optional (test-or-suite 'run-length-encoding-suite))
"Provides human readable results of test run. Default to entire suite."
(run! test-or-suite))
19 changes: 19 additions & 0 deletions common-lisp/run-length-encoding/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"PaulT89"
],
"files": {
"solution": [
"run-length-encoding.lisp"
],
"test": [
"run-length-encoding-test.lisp"
],
"example": [
".meta/example.lisp"
]
},
"blurb": "Implement run-length encoding and decoding.",
"source": "Wikipedia",
"source_url": "https://en.wikipedia.org/wiki/Run-length_encoding"
}
1 change: 1 addition & 0 deletions common-lisp/run-length-encoding/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"common-lisp","exercise":"run-length-encoding","id":"741cf6916d5245139ad53e0a00c5370d","url":"https://exercism.org/tracks/common-lisp/exercises/run-length-encoding","handle":"wsgac","is_requester":true,"auto_approve":false}
94 changes: 94 additions & 0 deletions common-lisp/run-length-encoding/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Help

## Running the tests

Start the REPL from the directory that you downloaded the exercise to.

You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests.

If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`.

If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`.

## Submitting your solution

You can submit your solution using the `exercism submit run-length-encoding.lisp` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp)
- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

## Setup

Check out [Installing Common
Lisp][track-install] for
instructions to get started or take a look at the guides available in
the [track's documentation pages][track-docs].

## Where to look for help

Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information.

There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help.

## Formatting

While Common Lisp doesn't care about indentation and layout of code,
nor whether you use spaces or tabs, this is an important consideration
for submissions to exercism.
Exercism.org's code widget cannot handle mixing of tab and space characters well so using only spaces is recommended to make
the code more readable to the human reviewers.
Please review your editors settings on how to accomplish this.
Below are instructions for popular editors for Common Lisp.

### VIM

Use the following commands to ensure VIM uses only spaces for
indentation:

```vimscript
:set tabstop=2
:set shiftwidth=2
:set expandtab
```

(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can
be added to your `~/.vimrc` file to use it all the time.

### Emacs

Emacs is very well suited for editing Common Lisp and has many
powerful add-on packages available. The only thing that one needs to
do with a stock emacs to make it work well with exercism.org is to
evaluate the following code:

`(setq-default indent-tabs-mode nil)`

This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in
order to have it set whenever Emacs is launched.

One suggested add-on for Emacs and Common Lisp is
[SLIME][slime] which offers tight integration
with the REPL; making iterative coding and testing very easy.

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution.

[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community
[slime]: https://github.com/slime/slime
[track-docs]: /docs/tracks/common-lisp
[track-install]: /docs/tracks/common-lisp/installation
[track-resources]: /docs/tracks/common-lisp/resources
35 changes: 35 additions & 0 deletions common-lisp/run-length-encoding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Run-Length Encoding

Welcome to Run-Length Encoding on Exercism's Common Lisp Track.
If you need help running the tests or submitting your code, check out `HELP.md`.

## Instructions

Implement run-length encoding and decoding.

Run-length encoding (RLE) is a simple form of data compression, where runs (consecutive data elements) are replaced by just one data value and count.

For example we can represent the original 53 characters with only 13.

```text
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
```

RLE allows the original data to be perfectly reconstructed from the compressed data, which makes it a lossless data compression.

```text
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
```

For simplicity, you can assume that the unencoded string will only contain the letters A through Z (either lower or upper case) and whitespace.
This way data to be encoded will never contain any numbers and numbers inside data to be decoded always represent the count for the following character.

## Source

### Created by

- @PaulT89

### Based on

Wikipedia - https://en.wikipedia.org/wiki/Run-length_encoding
72 changes: 72 additions & 0 deletions common-lisp/run-length-encoding/run-length-encoding-test.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
;; Ensures that run-length-encoding.lisp and the testing library are always loaded
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "run-length-encoding")
(quicklisp-client:quickload :fiveam))

;; Defines the testing package with symbols from run-length-encoding and FiveAM in scope
;; The `run-tests` function is exported for use by both the user and test-runner
(defpackage :run-length-encoding-test
(:use :cl :fiveam)
(:export :run-tests))

;; Enter the testing package
(in-package :run-length-encoding-test)

;; Define and enter a new FiveAM test-suite
(def-suite* run-length-encoding-suite)

(test empty-string
(let ((plain ""))
(is (string= "" (run-length-encoding:encode plain)))))

(test single-characters-only-are-encoded-without-count
(let ((plain "XYZ"))
(is (string= "XYZ" (run-length-encoding:encode plain)))))

(test string-with-no-single-characters
(let ((plain "AABBBCCCC"))
(is (string= "2A3B4C" (run-length-encoding:encode plain)))))

(test single-characters-mixed-with-repeated-characters
(let ((plain "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB"))
(is (string= "12WB12W3B24WB" (run-length-encoding:encode plain)))))

(test multiple-whitespace-mixed-in-string
(let ((plain " hsqq qww "))
(is (string= "2 hs2q q2w2 " (run-length-encoding:encode plain)))))

(test lowercase-characters
(let ((plain "aabbbcccc"))
(is (string= "2a3b4c" (run-length-encoding:encode plain)))))

(test empty-string
(let ((compressed ""))
(is (string= "" (run-length-encoding:decode compressed)))))

(test single-characters-only
(let ((compressed "XYZ"))
(is (string= "XYZ" (run-length-encoding:decode compressed)))))

(test string-with-no-single-characters
(let ((compressed "2A3B4C"))
(is (string= "AABBBCCCC" (run-length-encoding:decode compressed)))))

(test single-characters-with-repeated-characters
(let ((compressed "12WB12W3B24WB"))
(is (string= "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" (run-length-encoding:decode compressed)))))

(test multiple-whitespace-mixed-in-string
(let ((compressed "2 hs2q q2w2 "))
(is (string= " hsqq qww " (run-length-encoding:decode compressed)))))

(test lowercase-string
(let ((compressed "2a3b4c"))
(is (string= "aabbbcccc" (run-length-encoding:decode compressed)))))

(test encode-followed-by-decode-gives-original-string
(let ((plain "zzz ZZ zZ"))
(is (string= plain (run-length-encoding:decode (run-length-encoding:encode plain))))))

(defun run-tests (&optional (test-or-suite 'run-length-encoding-suite))
"Provides human readable results of test run. Default to entire suite."
(run! test-or-suite))
32 changes: 32 additions & 0 deletions common-lisp/run-length-encoding/run-length-encoding.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(defpackage :run-length-encoding
(:use :cl)
(:export :encode
:decode))

(in-package :run-length-encoding)

(defun encode (plain)
(loop
with alist = '()
for c in (coerce plain 'list)
if (not (eq (cadar alist) c))
do (push (list 0 c) alist)
do (incf (caar alist))
finally (return (format nil "~{~[~;~:;~:*~d~]~c~}" (mapcan #'identity (reverse alist))))))

(defun decode (compressed)
(loop
with i = 0
with n = 0
while (< i (length compressed))
do (multiple-value-bind (nn ii)
(parse-integer compressed :start i :junk-allowed t)
(if nn
(if (eq (elt compressed i) #\space)
(setf n 1)
(setf n nn i ii))
(setf n 1)))
collect (cons n (elt compressed i)) into groups
do (incf i)
finally (return (with-output-to-string (s)
(mapc #'(lambda (g) (princ (make-string (car g) :initial-element (cdr g)) s)) groups)))))
22 changes: 22 additions & 0 deletions common-lisp/sum-of-multiples/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"authors": [
"PaulT89"
],
"contributors": [
"verdammelt"
],
"files": {
"solution": [
"sum-of-multiples.lisp"
],
"test": [
"sum-of-multiples-test.lisp"
],
"example": [
".meta/example.lisp"
]
},
"blurb": "Given a number, find the sum of all the multiples of particular numbers up to but not including that number.",
"source": "A variation on Problem 1 at Project Euler",
"source_url": "https://projecteuler.net/problem=1"
}
1 change: 1 addition & 0 deletions common-lisp/sum-of-multiples/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"common-lisp","exercise":"sum-of-multiples","id":"dc9e74d409904c21a922afc7cb9e7f5d","url":"https://exercism.org/tracks/common-lisp/exercises/sum-of-multiples","handle":"wsgac","is_requester":true,"auto_approve":false}
94 changes: 94 additions & 0 deletions common-lisp/sum-of-multiples/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Help

## Running the tests

Start the REPL from the directory that you downloaded the exercise to.

You can run the tests by loading the test file into the REPL with `(load "exercise-test")` (replacing "exercise" with appropriate name). Then evaluate `(exercise-test:run-tests)` to run all the tests.

If you write your code directly in the REPL then simply evaluate `(exercise-test:run-tests)`.

If you write your code in the exercise lisp file then load it with `(load "exercise")` then evaluate `(exercise-test:run-tests)`.

## Submitting your solution

You can submit your solution using the `exercism submit sum-of-multiples.lisp` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Common Lisp track's documentation](https://exercism.org/docs/tracks/common-lisp)
- The [Common Lisp track's programming category on the forum](https://forum.exercism.org/c/programming/common-lisp)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

## Setup

Check out [Installing Common
Lisp][track-install] for
instructions to get started or take a look at the guides available in
the [track's documentation pages][track-docs].

## Where to look for help

Any of the resources listed in the list of [Useful Common Lisp Resources][track-resources] are good places to look for information.

There are also some [Online Communities][awesome-cl-communities] which may be good places to go for help.

## Formatting

While Common Lisp doesn't care about indentation and layout of code,
nor whether you use spaces or tabs, this is an important consideration
for submissions to exercism.
Exercism.org's code widget cannot handle mixing of tab and space characters well so using only spaces is recommended to make
the code more readable to the human reviewers.
Please review your editors settings on how to accomplish this.
Below are instructions for popular editors for Common Lisp.

### VIM

Use the following commands to ensure VIM uses only spaces for
indentation:

```vimscript
:set tabstop=2
:set shiftwidth=2
:set expandtab
```

(or as a oneliner `:set tabstop=2 shiftwidth=2 expandtab`). This can
be added to your `~/.vimrc` file to use it all the time.

### Emacs

Emacs is very well suited for editing Common Lisp and has many
powerful add-on packages available. The only thing that one needs to
do with a stock emacs to make it work well with exercism.org is to
evaluate the following code:

`(setq-default indent-tabs-mode nil)`

This can be placed in your `~/.emacs` (or `~/.emacs.d/init.el`) in
order to have it set whenever Emacs is launched.

One suggested add-on for Emacs and Common Lisp is
[SLIME][slime] which offers tight integration
with the REPL; making iterative coding and testing very easy.

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can ask for mentoring to help you come to the correct solution.

[awesome-cl-communities]: https://github.com/GustavBertram/awesome-common-lisp-learning#online-community
[slime]: https://github.com/slime/slime
[track-docs]: /docs/tracks/common-lisp
[track-install]: /docs/tracks/common-lisp/installation
[track-resources]: /docs/tracks/common-lisp/resources
53 changes: 53 additions & 0 deletions common-lisp/sum-of-multiples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Sum of Multiples

Welcome to Sum of Multiples on Exercism's Common Lisp Track.
If you need help running the tests or submitting your code, check out `HELP.md`.

## Introduction

You work for a company that makes an online, fantasy-survival game.

When a player finishes a level, they are awarded energy points.
The amount of energy awarded depends on which magical items the player found while exploring that level.

## Instructions

Your task is to write the code that calculates the energy points that get awarded to players when they complete a level.

The points awarded depend on two things:

- The level (a number) that the player completed.
- The base value of each magical item collected by the player during that level.

The energy points are awarded according to the following rules:

1. For each magical item, take the base value and find all the multiples of that value that are less than the level number.
2. Combine the sets of numbers.
3. Remove any duplicates.
4. Calculate the sum of all the numbers that are left.

Let's look at an example:

**The player completed level 20 and found two magical items with base values of 3 and 5.**

To calculate the energy points earned by the player, we need to find all the unique multiples of these base values that are less than level 20.

- Multiples of 3 less than 20: `{3, 6, 9, 12, 15, 18}`
- Multiples of 5 less than 20: `{5, 10, 15}`
- Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}`
- Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78`
- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5.

## Source

### Created by

- @PaulT89

### Contributed to by

- @verdammelt

### Based on

A variation on Problem 1 at Project Euler - https://projecteuler.net/problem=1
100 changes: 100 additions & 0 deletions common-lisp/sum-of-multiples/sum-of-multiples-test.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
;; Ensures that sum-of-multiples.lisp and the testing library are always loaded
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "sum-of-multiples")
(quicklisp-client:quickload :fiveam))

;; Defines the testing package with symbols from sum-of-multiples and FiveAM in scope
;; The `run-tests` function is exported for use by both the user and test-runner
(defpackage :sum-of-multiples-test
(:use :cl :fiveam)
(:export :run-tests))

;; Enter the testing package
(in-package :sum-of-multiples-test)

;; Define and enter a new FiveAM test-suite
(def-suite* sum-of-multiples-suite)

(test no-multiples-within-limit
(let ((factors (list 3 5))
(limit 1))
(is (= 0 (sum-of-multiples:sum factors limit)))))

(test one-factor-has-multiples-within-limit
(let ((factors (list 3 5))
(limit 4))
(is (= 3 (sum-of-multiples:sum factors limit)))))

(test more-than-one-multiple-within-limit
(let ((factors (list 3))
(limit 7))
(is (= 9 (sum-of-multiples:sum factors limit)))))

(test more-than-one-factor-with-multiples-within-limit
(let ((factors (list 3 5))
(limit 10))
(is (= 23 (sum-of-multiples:sum factors limit)))))

(test each-multiple-is-only-counted-once
(let ((factors (list 3 5))
(limit 100))
(is (= 2318 (sum-of-multiples:sum factors limit)))))

(test a-much-larger-limit
(let ((factors (list 3 5))
(limit 1000))
(is (= 233168 (sum-of-multiples:sum factors limit)))))

(test three-factors
(let ((factors (list 7 13 17))
(limit 20))
(is (= 51 (sum-of-multiples:sum factors limit)))))

(test factors-not-relatively-prime
(let ((factors (list 4 6))
(limit 15))
(is (= 30 (sum-of-multiples:sum factors limit)))))

(test some-pairs-of-factors-relatively-prime-and-some-not
(let ((factors (list 5 6 8))
(limit 150))
(is (= 4419 (sum-of-multiples:sum factors limit)))))

(test one-factor-is-a-multiple-of-another
(let ((factors (list 5 25))
(limit 51))
(is (= 275 (sum-of-multiples:sum factors limit)))))

(test much-larger-factors
(let ((factors (list 43 47))
(limit 10000))
(is (= 2203160 (sum-of-multiples:sum factors limit)))))

(test all-numbers-are-multiples-of-1
(let ((factors (list 1))
(limit 100))
(is (= 4950 (sum-of-multiples:sum factors limit)))))

(test no-factors-means-an-empty-sum
(let ((factors (list ))
(limit 10000))
(is (= 0 (sum-of-multiples:sum factors limit)))))

(test the-only-multiple-of-0-is-0
(let ((factors (list 0))
(limit 1))
(is (= 0 (sum-of-multiples:sum factors limit)))))

(test the-factor-0-does-not-affect-the-sum-of-multiples-of-other-factors
(let ((factors (list 3 0))
(limit 4))
(is (= 3 (sum-of-multiples:sum factors limit)))))

(test solutions-using-include-exclude-must-extend-to-cardinality-greater-than-3
(let ((factors (list 2 3 5 7 11))
(limit 10000))
(is (= 39614537 (sum-of-multiples:sum factors limit)))))

(defun run-tests (&optional (test-or-suite 'sum-of-multiples-suite))
"Provides human readable results of test run. Default to entire suite."
(run! test-or-suite))
11 changes: 11 additions & 0 deletions common-lisp/sum-of-multiples/sum-of-multiples.lisp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defpackage :sum-of-multiples
(:use :cl)
(:export :sum))

(in-package :sum-of-multiples)

(defun sum (factors limit)
(loop
for i from 1 below limit
if (some #'(lambda (f) (ignore-errors (zerop (mod i f)))) factors)
sum i))