Skip to content

Commit 7fc95d1

Browse files
authored
Add palindrome-products practice exercise (#521)
* Add palindrome products practice exercise * Suggestion from keiravillekode
1 parent 8822a00 commit 7fc95d1

File tree

9 files changed

+333
-0
lines changed

9 files changed

+333
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,14 @@
11711171
"text_parsing"
11721172
]
11731173
},
1174+
{
1175+
"slug": "palindrome-products",
1176+
"name": "Palindrome Products",
1177+
"uuid": "928d3505-7780-4d2d-8fd8-7c8f0aac4033",
1178+
"practices": [],
1179+
"prerequisites": [],
1180+
"difficulty": 5
1181+
},
11741182
{
11751183
"slug": "anagram",
11761184
"name": "Anagram",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
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+
"ryanplusplus"
4+
],
5+
"files": {
6+
"solution": [
7+
"palindrome-products.lua"
8+
],
9+
"test": [
10+
"palindrome-products_spec.lua"
11+
],
12+
"example": [
13+
".meta/example.lua"
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
local function reversed(s)
2+
local reversed = ''
3+
for c in s:gmatch('.') do
4+
reversed = c .. reversed
5+
end
6+
return reversed
7+
end
8+
9+
local function is_palindrome(n)
10+
local s = tostring(n)
11+
return s == reversed(s)
12+
end
13+
14+
local function validate_inputs(min, max)
15+
assert(min <= max, 'min must be <= max')
16+
end
17+
18+
local function smallest(min, max)
19+
validate_inputs(min, max)
20+
21+
local value = math.huge
22+
local factors = {}
23+
24+
for i = min, max do
25+
for j = i, max do
26+
local n = i * j
27+
28+
if n > value then
29+
break
30+
end
31+
32+
if is_palindrome(n) then
33+
if n ~= value then
34+
value = n
35+
factors = {}
36+
end
37+
table.insert(factors, { i, j })
38+
end
39+
end
40+
end
41+
42+
return { value = #factors > 0 and value or nil, factors = factors }
43+
end
44+
45+
local function largest(min, max)
46+
validate_inputs(min, max)
47+
48+
local value = -math.huge
49+
local factors = {}
50+
51+
for i = max, min, -1 do
52+
for j = i, min, -1 do
53+
local n = i * j
54+
55+
if n < value then
56+
break
57+
end
58+
59+
if is_palindrome(n) then
60+
if n ~= value then
61+
value = n
62+
factors = {}
63+
end
64+
table.insert(factors, { j, i })
65+
end
66+
end
67+
end
68+
69+
return { value = #factors > 0 and value or nil, factors = factors }
70+
end
71+
72+
return { smallest = smallest, largest = largest }
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
local function render_factors(factors)
2+
local rendered = {}
3+
for _, factor in ipairs(factors) do
4+
table.insert(rendered, string.format('{ %d, %d }', factor[1], factor[2]))
5+
end
6+
return '{ ' .. table.concat(rendered, ', ') .. ' }'
7+
end
8+
9+
return {
10+
module_name = 'palindrome_products',
11+
12+
generate_test = function(case)
13+
if case.expected.error then
14+
local template = [[
15+
assert.has_error(function()
16+
palindrome_products.%s(%d, %d)
17+
end, '%s')]]
18+
19+
return template:format(case.property, case.input.min, case.input.max, case.expected.error)
20+
else
21+
local template = [[
22+
local expected = {
23+
value = %s, --
24+
factors = %s
25+
}
26+
assert.are.same(expected, palindrome_products.%s(%d, %d))]]
27+
28+
return template:format(case.expected.value or 'nil', render_factors(case.expected.factors), case.property,
29+
case.input.min, case.input.max)
30+
end
31+
end
32+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
45+
[eeeb5bff-3f47-4b1e-892f-05829277bd74]
46+
description = "error result for largest if min is more than max"
47+
48+
[16481711-26c4-42e0-9180-e2e4e8b29c23]
49+
description = "smallest product does not use the smallest factor"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
local function smallest(min, max)
2+
3+
end
4+
5+
local function largest(min, max)
6+
7+
end
8+
9+
return { smallest = smallest, largest = largest }
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
local palindrome_products = require('palindrome-products')
2+
3+
describe('palindrome-products', function()
4+
it('find the smallest palindrome from single digit factors', function()
5+
local expected = {
6+
value = 1, --
7+
factors = { { 1, 1 } }
8+
}
9+
assert.are.same(expected, palindrome_products.smallest(1, 9))
10+
end)
11+
12+
it('find the largest palindrome from single digit factors', function()
13+
local expected = {
14+
value = 9, --
15+
factors = { { 1, 9 }, { 3, 3 } }
16+
}
17+
assert.are.same(expected, palindrome_products.largest(1, 9))
18+
end)
19+
20+
it('find the smallest palindrome from double digit factors', function()
21+
local expected = {
22+
value = 121, --
23+
factors = { { 11, 11 } }
24+
}
25+
assert.are.same(expected, palindrome_products.smallest(10, 99))
26+
end)
27+
28+
it('find the largest palindrome from double digit factors', function()
29+
local expected = {
30+
value = 9009, --
31+
factors = { { 91, 99 } }
32+
}
33+
assert.are.same(expected, palindrome_products.largest(10, 99))
34+
end)
35+
36+
it('find the smallest palindrome from triple digit factors', function()
37+
local expected = {
38+
value = 10201, --
39+
factors = { { 101, 101 } }
40+
}
41+
assert.are.same(expected, palindrome_products.smallest(100, 999))
42+
end)
43+
44+
it('find the largest palindrome from triple digit factors', function()
45+
local expected = {
46+
value = 906609, --
47+
factors = { { 913, 993 } }
48+
}
49+
assert.are.same(expected, palindrome_products.largest(100, 999))
50+
end)
51+
52+
it('find the smallest palindrome from four digit factors', function()
53+
local expected = {
54+
value = 1002001, --
55+
factors = { { 1001, 1001 } }
56+
}
57+
assert.are.same(expected, palindrome_products.smallest(1000, 9999))
58+
end)
59+
60+
it('find the largest palindrome from four digit factors', function()
61+
local expected = {
62+
value = 99000099, --
63+
factors = { { 9901, 9999 } }
64+
}
65+
assert.are.same(expected, palindrome_products.largest(1000, 9999))
66+
end)
67+
68+
it('empty result for smallest if no palindrome in the range', function()
69+
local expected = {
70+
value = nil, --
71+
factors = {}
72+
}
73+
assert.are.same(expected, palindrome_products.smallest(1002, 1003))
74+
end)
75+
76+
it('empty result for largest if no palindrome in the range', function()
77+
local expected = {
78+
value = nil, --
79+
factors = {}
80+
}
81+
assert.are.same(expected, palindrome_products.largest(15, 15))
82+
end)
83+
84+
it('error result for smallest if min is more than max', function()
85+
assert.has_error(function()
86+
palindrome_products.smallest(10000, 1)
87+
end, 'min must be <= max')
88+
end)
89+
90+
it('error result for largest if min is more than max', function()
91+
assert.has_error(function()
92+
palindrome_products.largest(2, 1)
93+
end, 'min must be <= max')
94+
end)
95+
96+
it('smallest product does not use the smallest factor', function()
97+
local expected = {
98+
value = 10988901, --
99+
factors = { { 3297, 3333 } }
100+
}
101+
assert.are.same(expected, palindrome_products.smallest(3215, 4000))
102+
end)
103+
end)

0 commit comments

Comments
 (0)