diff --git a/config.json b/config.json index e15129b0..1a75b9b7 100644 --- a/config.json +++ b/config.json @@ -557,6 +557,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "prime-factors", + "name": "Prime Factors", + "uuid": "48187959-1ed3-4327-94e9-aac4013f223c", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "run-length-encoding", "name": "Run-Length Encoding", diff --git a/exercises/practice/prime-factors/.docs/instructions.md b/exercises/practice/prime-factors/.docs/instructions.md new file mode 100644 index 00000000..252cc8ee --- /dev/null +++ b/exercises/practice/prime-factors/.docs/instructions.md @@ -0,0 +1,36 @@ +# Instructions + +Compute the prime factors of a given natural number. + +A prime number is only evenly divisible by itself and 1. + +Note that 1 is not a prime number. + +## Example + +What are the prime factors of 60? + +- Our first divisor is 2. + 2 goes into 60, leaving 30. +- 2 goes into 30, leaving 15. + - 2 doesn't go cleanly into 15. + So let's move on to our next divisor, 3. +- 3 goes cleanly into 15, leaving 5. + - 3 does not go cleanly into 5. + The next possible factor is 4. + - 4 does not go cleanly into 5. + The next possible factor is 5. +- 5 does go cleanly into 5. +- We're left only with 1, so now, we're done. + +Our successful divisors in that computation represent the list of prime factors of 60: 2, 2, 3, and 5. + +You can check this yourself: + +```text +2 * 2 * 3 * 5 += 4 * 15 += 60 +``` + +Success! diff --git a/exercises/practice/prime-factors/.meta/config.json b/exercises/practice/prime-factors/.meta/config.json new file mode 100644 index 00000000..3f0077b8 --- /dev/null +++ b/exercises/practice/prime-factors/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "erikschierboom" + ], + "files": { + "solution": [ + "prime-factors.8th" + ], + "test": [ + "test.8th" + ], + "example": [ + ".meta/example.8th" + ] + }, + "blurb": "Compute the prime factors of a given natural number.", + "source": "The Prime Factors Kata by Uncle Bob", + "source_url": "https://web.archive.org/web/20221026171801/http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata" +} diff --git a/exercises/practice/prime-factors/.meta/example.8th b/exercises/practice/prime-factors/.meta/example.8th new file mode 100644 index 00000000..f4b5d7b3 --- /dev/null +++ b/exercises/practice/prime-factors/.meta/example.8th @@ -0,0 +1,23 @@ +: increment-factor \ n n -- n n + repeat + 2dup n:mod !if ;then + dup 2 n:= !if n:1+ then n:1+ + again +; + +: factors \ n -- a + a:new >r 2 + + repeat + over 1 n:> + !if + 2drop break + else + increment-factor + swap over n:/ swap + dup r> a:push >r + then + again + + r> +; diff --git a/exercises/practice/prime-factors/.meta/tests.toml b/exercises/practice/prime-factors/.meta/tests.toml new file mode 100644 index 00000000..6f9cc8ce --- /dev/null +++ b/exercises/practice/prime-factors/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[924fc966-a8f5-4288-82f2-6b9224819ccd] +description = "no factors" + +[17e30670-b105-4305-af53-ddde182cb6ad] +description = "prime number" + +[238d57c8-4c12-42ef-af34-ae4929f94789] +description = "another prime number" + +[f59b8350-a180-495a-8fb1-1712fbee1158] +description = "square of a prime" + +[756949d3-3158-4e3d-91f2-c4f9f043ee70] +description = "product of first prime" + +[bc8c113f-9580-4516-8669-c5fc29512ceb] +description = "cube of a prime" + +[7d6a3300-a4cb-4065-bd33-0ced1de6cb44] +description = "product of second prime" + +[073ac0b2-c915-4362-929d-fc45f7b9a9e4] +description = "product of third prime" + +[6e0e4912-7fb6-47f3-a9ad-dbcd79340c75] +description = "product of first and second prime" + +[00485cd3-a3fe-4fbe-a64a-a4308fc1f870] +description = "product of primes and non-primes" + +[02251d54-3ca1-4a9b-85e1-b38f4b0ccb91] +description = "product of primes" + +[070cf8dc-e202-4285-aa37-8d775c9cd473] +description = "factors include a large prime" diff --git a/exercises/practice/prime-factors/libs/exercism/test b/exercises/practice/prime-factors/libs/exercism/test new file mode 100644 index 00000000..270681fe --- /dev/null +++ b/exercises/practice/prime-factors/libs/exercism/test @@ -0,0 +1,174 @@ +needs console/loaded +needs utils/latebind + +\ ----------------------------------------------------------------- + +ns: test + +-1 var, test-count +var tests-passed +var tests-failed +var tests-skipped +true var, run-test + +\ Some utility words + + +: test-passed \ s x x -- \\ test name, expected value, actual value + 2drop + 1 tests-passed n:+! + con:green con:onBlack . space " ... OK" . con:white con:onBlack cr +; + +: test-skipped \ s -- + 1 tests-skipped n:+! + con:cyan con:onBlack . space " ... SKIPPED" . con:white con:onBlack cr +; + +: test-failed \ s x x -- \\ test name, expected value, actual value + 1 tests-failed n:+! + rot + con:red con:onBlack . space " ... FAIL" . con:white con:onBlack cr + " Actual: «" . . "»" . cr + " Expected: «" . . "»" . cr cr +; + +: isword? \ x -- x f + dup >kind ns:w n:= +; + +: run-test? \ -- T + run-test @ if true else "RUN_ALL_TESTS" getenv n:>bool then +; + +\ Num passed + num skipped + num failed should == num tests +: all-tests-run? \ -- T + tests-passed @ tests-skipped @ tests-failed @ n:+ n:+ + test-count @ n:= +; + +\ returns true if x is a date, false otherwise +: date? \ x -- x T + dup >kind ns:d n:= +; + +\ adapted from 8th forum -- https://8th-dev.com/forum/index.php/topic,2745.0.html +: eq? \ x x -- T + \ are the items the same kind? + 2dup >kind swap >kind n:= + !if 2drop false ;then + + \ same kind: try different comparators + number? if n:= ;then + string? if s:= ;then + array? if ' eq? a:= 2nip ;then + map? if ' eq? m:= 2nip ;then + date? if d:= ;then + + \ otherwise fall back to 'lazy evaluation' + G:l: = +; + +: eps_eq? \ n x x -- T + \ are the items the same kind? + 2dup >kind swap >kind n:= + !if 2drop false ;then + number? !if 2drop false ;then + rot n:~= +; + +: check-depth \ ... n -- ... + dup>r + n:1+ depth n:= + !if + con:red con:onBlack + "PANIC: expected stack depth to be " . r> . cr + "Stack is:" . cr + .s cr + 255 die + then + rdrop +; + +\ ----------------------------------------------------------------- + +\ status report at end of run +( all-tests-run? + !if con:red con:onBlack "... FAIL - not all tests completed" . con:white con:onBlack cr then +) onexit + +\ Print a summary of the tests run +( con:white con:onBlack + test-count @ . space "tests planned - " . + tests-passed @ . space "passed - " . + tests-skipped @ . space "skipped - " . + tests-failed @ . space "failed" . cr +) onexit + +\ ----------------------------------------------------------------- +\ The public-facing words +\ ----------------------------------------------------------------- + +: equal? \ s x w -- | s w x -- + run-test? !if 2drop test-skipped ;; then + isword? !if swap then + w:exec + 3 check-depth + 2dup \ so test-failed can show actual and expected + eq? if test-passed else test-failed then +; + +: approx_equal? \ s x w n -- | s w x n -- + run-test? !if 3drop test-skipped ;; then + -rot isword? !if swap then + w:exec + 4 check-depth + 3dup \ so test-failed can show actual and expected + eps_eq? + if rot drop test-passed else rot drop test-failed then +; + +: true? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + true swap dup \ so test-failed can show actual and expected + if test-passed else test-failed then +; + +: false? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + false swap dup \ so test-failed can show actual and expected + !if test-passed else test-failed then +; + +: null? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + 2 check-depth + null swap dup \ so test-failed can show actual and expected + G:null? nip if test-passed else test-failed then +; + +: SKIP-REST-OF-TESTS false run-test ! ; + +: tests \ n -- + test-count ! +; + +\ Set the exit status: +\ 0 = all OK +\ 1 = not all tests were run (some error occurred) +\ 2 = some tests failed +: end-of-tests \ -- + all-tests-run? + if + tests-failed @ 0 n:= if 0 else 2 then + else + 1 + then + die +; + diff --git a/exercises/practice/prime-factors/prime-factors.8th b/exercises/practice/prime-factors/prime-factors.8th new file mode 100644 index 00000000..c6d2dc91 --- /dev/null +++ b/exercises/practice/prime-factors/prime-factors.8th @@ -0,0 +1,3 @@ +: factors \ n -- a + +; diff --git a/exercises/practice/prime-factors/test.8th b/exercises/practice/prime-factors/test.8th new file mode 100644 index 00000000..5d102114 --- /dev/null +++ b/exercises/practice/prime-factors/test.8th @@ -0,0 +1,69 @@ +"prime-factors.8th" f:include +needs exercism/test +with: test +12 tests + +"No factors" + ( 1 factors ) + [] + equal? + +SKIP-REST-OF-TESTS + +"Prime number" + ( 2 factors ) + [2] + equal? + +"Another prime number" + ( 3 factors ) + [3] + equal? + +"Square of a prime" + ( 9 factors ) + [3, 3] + equal? + +"Product of first prime" + ( 4 factors ) + [2, 2] + equal? + +"Cube of a prime" + ( 8 factors ) + [2, 2, 2] + equal? + +"Product of second prime" + ( 27 factors ) + [3, 3, 3] + equal? + +"Product of third prime" + ( 625 factors ) + [5, 5, 5, 5] + equal? + +"Product of first and second prime" + ( 6 factors ) + [2, 3] + equal? + +"Product of primes and non-primes" + ( 12 factors ) + [2, 2, 3] + equal? + +"Product of primes" + ( 901255 factors ) + [5, 17, 23, 461] + equal? + +"Factors include a large prime" + ( 93819012551 factors ) + [11, 9539, 894119] + equal? + +end-of-tests +;with