Skip to content

Commit a66214c

Browse files
Adding exercise "palindrome-products" to track "r" (#399)
1 parent ca67320 commit a66214c

File tree

7 files changed

+282
-0
lines changed

7 files changed

+282
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,14 @@
939939
"practices": [],
940940
"prerequisites": [],
941941
"difficulty": 5
942+
},
943+
{
944+
"slug": "palindrome-products",
945+
"name": "Palindrome Products",
946+
"uuid": "2776fe9f-922b-4731-804b-0901c3b514cc",
947+
"practices": [],
948+
"prerequisites": [],
949+
"difficulty": 6
942950
}
943951
]
944952
},
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Instructions
2+
3+
Detect palindrome products in a given range.
4+
5+
A palindromic number is a number that remains the same when its digits are reversed.
6+
For example, `121` is a palindromic number but `112` is not.
7+
8+
Given a range of numbers, find the largest and smallest palindromes which
9+
are products of two numbers within that range.
10+
11+
Your solution should return the largest and smallest palindromes, along with the factors of each within the range.
12+
If the largest or smallest palindrome has more than one pair of factors within the range, then return all the pairs.
13+
14+
## Example 1
15+
16+
Given the range `[1, 9]` (both inclusive)...
17+
18+
And given the list of all possible products within this range:
19+
`[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 15, 21, 24, 27, 20, 28, 32, 36, 25, 30, 35, 40, 45, 42, 48, 54, 49, 56, 63, 64, 72, 81]`
20+
21+
The palindrome products are all single digit numbers (in this case):
22+
`[1, 2, 3, 4, 5, 6, 7, 8, 9]`
23+
24+
The smallest palindrome product is `1`.
25+
Its factors are `(1, 1)`.
26+
The largest palindrome product is `9`.
27+
Its factors are `(1, 9)` and `(3, 3)`.
28+
29+
## Example 2
30+
31+
Given the range `[10, 99]` (both inclusive)...
32+
33+
The smallest palindrome product is `121`.
34+
Its factors are `(11, 11)`.
35+
The largest palindrome product is `9009`.
36+
Its factors are `(91, 99)`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"frequenter-at-exercism"
4+
],
5+
"files": {
6+
"solution": [
7+
"palindrome-products.R"
8+
],
9+
"test": [
10+
"test_palindrome-products.R"
11+
],
12+
"example": [
13+
".meta/example.R"
14+
]
15+
},
16+
"blurb": "Detect palindrome products in a given range.",
17+
"source": "Problem 4 at Project Euler",
18+
"source_url": "https://projecteuler.net/problem=4"
19+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
verso <- \(n) {
2+
v <- 0
3+
while (n > 0) {
4+
v <- 10 * v + n %% 10
5+
n <- n %/% 10
6+
}
7+
v
8+
}
9+
10+
nlen <- \(n) ceiling(log10(n + 1))
11+
12+
haflen <- \(n) (log10(n) + 1) %/% 2
13+
14+
nconc <- \(m, n) m * 10 ^ nlen(n) + n
15+
16+
succ <- \(n) {
17+
if (n == 0) return(1) # kludge
18+
nl <- nlen(n)
19+
if (nl != nlen(n + 1)) return(n + 2)
20+
hl <- haflen(n)
21+
left <- n %/% 10 ^ (nl - hl)
22+
leftmid <- n %/% 10 ^ hl
23+
right <- n %% 10 ^ hl
24+
v <- verso(left)
25+
if (v > right) return(nconc(leftmid, v))
26+
newleftmid <- leftmid + 1
27+
newright <- verso(newleftmid %/% 10 ^ (nl - 2 * hl))
28+
newleftmid * 10 ^ hl + newright
29+
}
30+
31+
pred <- \(n) {
32+
nl <- nlen(n)
33+
if (n - 1 == 10 ^ (nl - 1)) return(n - 2)
34+
hl <- haflen(n)
35+
left <- n %/% 10 ^ (nl - hl)
36+
leftmid <- n %/% 10 ^ hl
37+
right <- n %% 10 ^ hl
38+
v <- verso(left)
39+
if (v < right) return(nconc(leftmid, v))
40+
newleftmid <- leftmid - 1
41+
newright <- verso(newleftmid %/% 10 ^ (nl - 2 * hl))
42+
newleftmid * 10 ^ hl + newright
43+
}
44+
45+
listify <- \(x) lapply(1:nrow(x), \(i) as.integer(x[i, ]))
46+
47+
palindrome_products <- \(input) {
48+
if (input$min > input$max) stop()
49+
result <- list()
50+
lb <- input$min ^ 2 - 1
51+
ub <- input$max ^ 2 + 1
52+
cand <- lb
53+
while (cand < ub) {
54+
cand <- succ(cand)
55+
i <- input$min:floor(sqrt(cand))
56+
r <- cand %% i
57+
w <- which(r == 0)
58+
if (length(w) == 0) next
59+
i <- i[w]
60+
j <- cand %/% i
61+
w <- which(j %in% input$min:input$max)
62+
if (length(w) == 0) next
63+
result$smallest <- list(value = cand, factors = listify(cbind(i[w], j[w])))
64+
break
65+
}
66+
cand <- ub
67+
while (cand > lb) {
68+
cand <- pred(cand)
69+
i <- input$min:floor(sqrt(cand))
70+
r <- cand %% i
71+
w <- which(r == 0)
72+
if (length(w) == 0) next
73+
i <- i[w]
74+
j <- cand %/% i
75+
w <- which(j %in% input$min:input$max)
76+
if (length(w) == 0) next
77+
result$largest <- list(value = cand, factors = listify(cbind(i[w], j[w])))
78+
break
79+
}
80+
result
81+
}
82+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[5cff78fe-cf02-459d-85c2-ce584679f887]
13+
description = "find the smallest palindrome from single digit factors"
14+
15+
[0853f82c-5fc4-44ae-be38-fadb2cced92d]
16+
description = "find the largest palindrome from single digit factors"
17+
18+
[66c3b496-bdec-4103-9129-3fcb5a9063e1]
19+
description = "find the smallest palindrome from double digit factors"
20+
21+
[a10682ae-530a-4e56-b89d-69664feafe53]
22+
description = "find the largest palindrome from double digit factors"
23+
24+
[cecb5a35-46d1-4666-9719-fa2c3af7499d]
25+
description = "find the smallest palindrome from triple digit factors"
26+
27+
[edab43e1-c35f-4ea3-8c55-2f31dddd92e5]
28+
description = "find the largest palindrome from triple digit factors"
29+
30+
[4f802b5a-9d74-4026-a70f-b53ff9234e4e]
31+
description = "find the smallest palindrome from four digit factors"
32+
33+
[787525e0-a5f9-40f3-8cb2-23b52cf5d0be]
34+
description = "find the largest palindrome from four digit factors"
35+
36+
[58fb1d63-fddb-4409-ab84-a7a8e58d9ea0]
37+
description = "empty result for smallest if no palindrome in the range"
38+
39+
[9de9e9da-f1d9-49a5-8bfc-3d322efbdd02]
40+
description = "empty result for largest if no palindrome in the range"
41+
42+
[12e73aac-d7ee-4877-b8aa-2aa3dcdb9f8a]
43+
description = "error result for smallest if min is more than max"
44+
include = false # redundant, assumes instantiation before calculation of largest and smallest
45+
46+
[eeeb5bff-3f47-4b1e-892f-05829277bd74]
47+
description = "error result for largest if min is more than max"
48+
49+
[16481711-26c4-42e0-9180-e2e4e8b29c23]
50+
description = "smallest product does not use the smallest factor"

exercises/practice/palindrome-products/palindrome-products.R

Whitespace-only changes.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
source("./palindrome-products.R")
2+
library(testthat)
3+
4+
5+
test_that('find the smallest palindrome from single digit factors', {
6+
input <- list(min = 1, max = 9)
7+
actual <- palindrome_products(input)$smallest
8+
expect_equal(actual$value, 1)
9+
expect_equal(actual$factors, list(c(1, 1)))
10+
})
11+
12+
test_that('find the largest palindrome from single digit factors', {
13+
# skip()
14+
input <- list(min = 1, max = 9)
15+
actual <- palindrome_products(input)$largest
16+
expect_equal(actual$value, 9)
17+
expect_equal(actual$factors, list(c(1, 9), c(3, 3)))
18+
})
19+
20+
test_that('find the smallest palindrome from double digit factors', {
21+
input <- list(min = 10, max = 99)
22+
actual <- palindrome_products(input)$smallest
23+
expect_equal(actual$value, 121)
24+
expect_equal(actual$factors, list(c(11, 11)))
25+
})
26+
27+
test_that('find the largest palindrome from double digit factors', {
28+
input <- list(min = 10, max = 99)
29+
actual <- palindrome_products(input)$largest
30+
expect_equal(actual$value, 9009)
31+
expect_equal(actual$factors, list(c(91, 99)))
32+
})
33+
34+
test_that('find the largest palindrome from triple digit factors', {
35+
input <- list(min = 100, max = 999)
36+
actual <- palindrome_products(input)$largest
37+
expect_equal(actual$value, 906609)
38+
expect_equal(actual$factors, list(c(913, 993)))
39+
})
40+
41+
test_that('find the smallest palindrome from four digit factors', {
42+
input <- list(min = 1000, max = 9999)
43+
actual <- palindrome_products(input)$smallest
44+
expect_equal(actual$value, 1002001)
45+
expect_equal(actual$factors, list(c(1001, 1001)))
46+
})
47+
48+
test_that('find the largest palindrome from four digit factors', {
49+
input <- list(min = 1000, max = 9999)
50+
actual <- palindrome_products(input)$largest
51+
expect_equal(actual$value, 99000099)
52+
expect_equal(actual$factors, list(c(9901, 9999)))
53+
})
54+
55+
test_that('empty result for smallest if no palindrome in the range', {
56+
input <- list(min = 1002, max = 1003)
57+
actual <- palindrome_products(input)$smallest
58+
expect_null(actual$value)
59+
expect_length(actual$factors, 0)
60+
})
61+
62+
test_that('empty result for largest if no palindrome in the range', {
63+
# skip()
64+
input <- list(min = 15, max = 15)
65+
actual <- palindrome_products(input)$largest
66+
expect_null(actual$value)
67+
expect_length(actual$factors, 0)
68+
})
69+
70+
test_that('error result if min is more than max', {
71+
input <- list(min = 10000, max = 1)
72+
expect_error(palindrome_products(input))
73+
})
74+
75+
test_that('smallest product does not use the smallest factor', {
76+
input <- list(min = 3215, max = 4000)
77+
actual <- palindrome_products(input)$smallest
78+
expect_equal(actual$value, 10988901)
79+
expect_equal(actual$factors, list(c(3297, 3333)))
80+
})
81+
82+
test_that('arbitrary range within five digit numbers', {
83+
input <- list(min = 33740, max = 58504)
84+
actual <- palindrome_products(input)$largest
85+
expect_equal(actual$value, 3401441043)
86+
expect_equal(actual$factors, list(c(58201, 58443)))
87+
})

0 commit comments

Comments
 (0)