From c9158dc31df38520b961a86d887bc32ab6af402c Mon Sep 17 00:00:00 2001 From: "Wojciech S. Gac" Date: Wed, 27 Sep 2023 22:30:46 +0200 Subject: [PATCH] Add 4 new Common Lisp exercises --- .../isbn-verifier/.exercism/config.json | 19 ++++ .../isbn-verifier/.exercism/metadata.json | 1 + common-lisp/isbn-verifier/HELP.md | 94 ++++++++++++++++ common-lisp/isbn-verifier/README.md | 57 ++++++++++ .../isbn-verifier/isbn-verifier-test.lisp | 96 +++++++++++++++++ common-lisp/isbn-verifier/isbn-verifier.lisp | 32 ++++++ .../matching-brackets/.exercism/config.json | 21 ++++ .../matching-brackets/.exercism/metadata.json | 1 + common-lisp/matching-brackets/HELP.md | 94 ++++++++++++++++ common-lisp/matching-brackets/README.md | 23 ++++ .../matching-brackets-test.lisp | 101 ++++++++++++++++++ .../matching-brackets/matching-brackets.lisp | 20 ++++ .../#run-length-encoding-test.lisp# | 72 +++++++++++++ .../.#run-length-encoding-test.lisp | 1 + .../run-length-encoding/.exercism/config.json | 19 ++++ .../.exercism/metadata.json | 1 + common-lisp/run-length-encoding/HELP.md | 94 ++++++++++++++++ common-lisp/run-length-encoding/README.md | 35 ++++++ .../run-length-encoding-test.lisp | 72 +++++++++++++ .../run-length-encoding.lisp | 32 ++++++ .../sum-of-multiples/.exercism/config.json | 22 ++++ .../sum-of-multiples/.exercism/metadata.json | 1 + common-lisp/sum-of-multiples/HELP.md | 94 ++++++++++++++++ common-lisp/sum-of-multiples/README.md | 53 +++++++++ .../sum-of-multiples-test.lisp | 100 +++++++++++++++++ .../sum-of-multiples/sum-of-multiples.lisp | 11 ++ 26 files changed, 1166 insertions(+) create mode 100644 common-lisp/isbn-verifier/.exercism/config.json create mode 100644 common-lisp/isbn-verifier/.exercism/metadata.json create mode 100644 common-lisp/isbn-verifier/HELP.md create mode 100644 common-lisp/isbn-verifier/README.md create mode 100644 common-lisp/isbn-verifier/isbn-verifier-test.lisp create mode 100644 common-lisp/isbn-verifier/isbn-verifier.lisp create mode 100644 common-lisp/matching-brackets/.exercism/config.json create mode 100644 common-lisp/matching-brackets/.exercism/metadata.json create mode 100644 common-lisp/matching-brackets/HELP.md create mode 100644 common-lisp/matching-brackets/README.md create mode 100644 common-lisp/matching-brackets/matching-brackets-test.lisp create mode 100644 common-lisp/matching-brackets/matching-brackets.lisp create mode 100644 common-lisp/run-length-encoding/#run-length-encoding-test.lisp# create mode 120000 common-lisp/run-length-encoding/.#run-length-encoding-test.lisp create mode 100644 common-lisp/run-length-encoding/.exercism/config.json create mode 100644 common-lisp/run-length-encoding/.exercism/metadata.json create mode 100644 common-lisp/run-length-encoding/HELP.md create mode 100644 common-lisp/run-length-encoding/README.md create mode 100644 common-lisp/run-length-encoding/run-length-encoding-test.lisp create mode 100644 common-lisp/run-length-encoding/run-length-encoding.lisp create mode 100644 common-lisp/sum-of-multiples/.exercism/config.json create mode 100644 common-lisp/sum-of-multiples/.exercism/metadata.json create mode 100644 common-lisp/sum-of-multiples/HELP.md create mode 100644 common-lisp/sum-of-multiples/README.md create mode 100644 common-lisp/sum-of-multiples/sum-of-multiples-test.lisp create mode 100644 common-lisp/sum-of-multiples/sum-of-multiples.lisp diff --git a/common-lisp/isbn-verifier/.exercism/config.json b/common-lisp/isbn-verifier/.exercism/config.json new file mode 100644 index 00000000..a2c621ce --- /dev/null +++ b/common-lisp/isbn-verifier/.exercism/config.json @@ -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" +} diff --git a/common-lisp/isbn-verifier/.exercism/metadata.json b/common-lisp/isbn-verifier/.exercism/metadata.json new file mode 100644 index 00000000..2200ecb8 --- /dev/null +++ b/common-lisp/isbn-verifier/.exercism/metadata.json @@ -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} \ No newline at end of file diff --git a/common-lisp/isbn-verifier/HELP.md b/common-lisp/isbn-verifier/HELP.md new file mode 100644 index 00000000..56294f59 --- /dev/null +++ b/common-lisp/isbn-verifier/HELP.md @@ -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 \ No newline at end of file diff --git a/common-lisp/isbn-verifier/README.md b/common-lisp/isbn-verifier/README.md new file mode 100644 index 00000000..d8407a6f --- /dev/null +++ b/common-lisp/isbn-verifier/README.md @@ -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 \ No newline at end of file diff --git a/common-lisp/isbn-verifier/isbn-verifier-test.lisp b/common-lisp/isbn-verifier/isbn-verifier-test.lisp new file mode 100644 index 00000000..bb386a8e --- /dev/null +++ b/common-lisp/isbn-verifier/isbn-verifier-test.lisp @@ -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)) diff --git a/common-lisp/isbn-verifier/isbn-verifier.lisp b/common-lisp/isbn-verifier/isbn-verifier.lisp new file mode 100644 index 00000000..4cf1660f --- /dev/null +++ b/common-lisp/isbn-verifier/isbn-verifier.lisp @@ -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))) diff --git a/common-lisp/matching-brackets/.exercism/config.json b/common-lisp/matching-brackets/.exercism/config.json new file mode 100644 index 00000000..b76a0701 --- /dev/null +++ b/common-lisp/matching-brackets/.exercism/config.json @@ -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" +} diff --git a/common-lisp/matching-brackets/.exercism/metadata.json b/common-lisp/matching-brackets/.exercism/metadata.json new file mode 100644 index 00000000..e5aaae34 --- /dev/null +++ b/common-lisp/matching-brackets/.exercism/metadata.json @@ -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} \ No newline at end of file diff --git a/common-lisp/matching-brackets/HELP.md b/common-lisp/matching-brackets/HELP.md new file mode 100644 index 00000000..bd081317 --- /dev/null +++ b/common-lisp/matching-brackets/HELP.md @@ -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 \ No newline at end of file diff --git a/common-lisp/matching-brackets/README.md b/common-lisp/matching-brackets/README.md new file mode 100644 index 00000000..106ae494 --- /dev/null +++ b/common-lisp/matching-brackets/README.md @@ -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 \ No newline at end of file diff --git a/common-lisp/matching-brackets/matching-brackets-test.lisp b/common-lisp/matching-brackets/matching-brackets-test.lisp new file mode 100644 index 00000000..9524219f --- /dev/null +++ b/common-lisp/matching-brackets/matching-brackets-test.lisp @@ -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)) + diff --git a/common-lisp/matching-brackets/matching-brackets.lisp b/common-lisp/matching-brackets/matching-brackets.lisp new file mode 100644 index 00000000..9f183284 --- /dev/null +++ b/common-lisp/matching-brackets/matching-brackets.lisp @@ -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)))) diff --git a/common-lisp/run-length-encoding/#run-length-encoding-test.lisp# b/common-lisp/run-length-encoding/#run-length-encoding-test.lisp# new file mode 100644 index 00000000..486de7d7 --- /dev/null +++ b/common-lisp/run-length-encoding/#run-length-encoding-test.lisp# @@ -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)) diff --git a/common-lisp/run-length-encoding/.#run-length-encoding-test.lisp b/common-lisp/run-length-encoding/.#run-length-encoding-test.lisp new file mode 120000 index 00000000..b6b728cf --- /dev/null +++ b/common-lisp/run-length-encoding/.#run-length-encoding-test.lisp @@ -0,0 +1 @@ +wsg@incitatus.3764723:1691421405 \ No newline at end of file diff --git a/common-lisp/run-length-encoding/.exercism/config.json b/common-lisp/run-length-encoding/.exercism/config.json new file mode 100644 index 00000000..ed150c1c --- /dev/null +++ b/common-lisp/run-length-encoding/.exercism/config.json @@ -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" +} diff --git a/common-lisp/run-length-encoding/.exercism/metadata.json b/common-lisp/run-length-encoding/.exercism/metadata.json new file mode 100644 index 00000000..54f0007c --- /dev/null +++ b/common-lisp/run-length-encoding/.exercism/metadata.json @@ -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} \ No newline at end of file diff --git a/common-lisp/run-length-encoding/HELP.md b/common-lisp/run-length-encoding/HELP.md new file mode 100644 index 00000000..ec9a5027 --- /dev/null +++ b/common-lisp/run-length-encoding/HELP.md @@ -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 \ No newline at end of file diff --git a/common-lisp/run-length-encoding/README.md b/common-lisp/run-length-encoding/README.md new file mode 100644 index 00000000..b61d52ea --- /dev/null +++ b/common-lisp/run-length-encoding/README.md @@ -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 \ No newline at end of file diff --git a/common-lisp/run-length-encoding/run-length-encoding-test.lisp b/common-lisp/run-length-encoding/run-length-encoding-test.lisp new file mode 100644 index 00000000..486de7d7 --- /dev/null +++ b/common-lisp/run-length-encoding/run-length-encoding-test.lisp @@ -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)) diff --git a/common-lisp/run-length-encoding/run-length-encoding.lisp b/common-lisp/run-length-encoding/run-length-encoding.lisp new file mode 100644 index 00000000..2b7e6299 --- /dev/null +++ b/common-lisp/run-length-encoding/run-length-encoding.lisp @@ -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))))) diff --git a/common-lisp/sum-of-multiples/.exercism/config.json b/common-lisp/sum-of-multiples/.exercism/config.json new file mode 100644 index 00000000..43d5696e --- /dev/null +++ b/common-lisp/sum-of-multiples/.exercism/config.json @@ -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" +} diff --git a/common-lisp/sum-of-multiples/.exercism/metadata.json b/common-lisp/sum-of-multiples/.exercism/metadata.json new file mode 100644 index 00000000..8a376b25 --- /dev/null +++ b/common-lisp/sum-of-multiples/.exercism/metadata.json @@ -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} \ No newline at end of file diff --git a/common-lisp/sum-of-multiples/HELP.md b/common-lisp/sum-of-multiples/HELP.md new file mode 100644 index 00000000..0c481735 --- /dev/null +++ b/common-lisp/sum-of-multiples/HELP.md @@ -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 \ No newline at end of file diff --git a/common-lisp/sum-of-multiples/README.md b/common-lisp/sum-of-multiples/README.md new file mode 100644 index 00000000..618ef30c --- /dev/null +++ b/common-lisp/sum-of-multiples/README.md @@ -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 \ No newline at end of file diff --git a/common-lisp/sum-of-multiples/sum-of-multiples-test.lisp b/common-lisp/sum-of-multiples/sum-of-multiples-test.lisp new file mode 100644 index 00000000..da2b5083 --- /dev/null +++ b/common-lisp/sum-of-multiples/sum-of-multiples-test.lisp @@ -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)) diff --git a/common-lisp/sum-of-multiples/sum-of-multiples.lisp b/common-lisp/sum-of-multiples/sum-of-multiples.lisp new file mode 100644 index 00000000..f68a8e6c --- /dev/null +++ b/common-lisp/sum-of-multiples/sum-of-multiples.lisp @@ -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))