Skip to content

Commit

Permalink
Palindrome products: sync expected test results and input data with p…
Browse files Browse the repository at this point in the history
…roblem-specifications. (#1814)
  • Loading branch information
BethanyG authored and cmccandless committed Jun 17, 2019
1 parent 8665813 commit 88cd48b
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 64 deletions.
71 changes: 35 additions & 36 deletions exercises/palindrome-products/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from math import log10, floor, ceil


def largest_palindrome(max_factor, min_factor):
def largest(min_factor, max_factor):
return get_extreme_palindrome_with_factors(max_factor, min_factor,
"largest")


def smallest_palindrome(max_factor, min_factor):
def smallest(max_factor, min_factor):
return get_extreme_palindrome_with_factors(max_factor, min_factor,
"smallest")

Expand All @@ -17,37 +17,36 @@ def get_extreme_palindrome_with_factors(max_factor, min_factor, extreme):
palindromes_found = palindromes(max_factor, min_factor,
reverse=(extreme == "largest"))
factor_pairs = None
for palin in palindromes_found:
factor_pairs = ((fact, palin // fact)
for fact in range(min_factor, max_factor + 1)
if palin % fact == 0)
for palindrome in palindromes_found:
factor_pairs = ((factor, palindrome // factor)
for factor in range(min_factor, max_factor + 1)
if palindrome % factor == 0)
factor_pairs = list(pair for pair in factor_pairs
if min_factor <= pair[1] <= max_factor)
if len(factor_pairs) > 0:
break

if factor_pairs is None or len(factor_pairs) == 0:
if not factor_pairs:
return (None, [])

return (palin, factor_pairs)
return (palindrome, factor_pairs)


def reverse_num(n):
rev = 0
while n > 0:
rev *= 10
rev += (n % 10)
n //= 10
return rev
def reverse_num(number):
reversed = 0
while number > 0:
reversed *= 10
reversed += (number % 10)
number //= 10
return reversed


def num_digits(n):
return int(floor(log10(n) + 1))
def num_digits(number):
return int(floor(log10(number) + 1))


def palindromes(max_factor, min_factor, reverse=False):
"""Generates all palindromes between `min_factor`**2 and max_factor`**2
If `reverse` is True, will produce the palindromes in decreasing order,
from `max_factor`**2 down to `min_factor`**2. This is needed for
`largest_palindrome`, since it won't have to iterate through a
Expand All @@ -62,55 +61,55 @@ def palindromes(max_factor, min_factor, reverse=False):
minimum = min_factor ** 2
maximum = max_factor ** 2

def gen_palins_of_length(nd, reverse=reverse):
def gen_palindromes_of_length(num_digits, reverse=reverse):
"""Generates all palindromes with `nd` number of digits that are
within the desired range.
Again, if `reverse` is True, the palindromes are generated in
reverse order.
"""
even_nd = (nd % 2 == 0)
even_nd = (num_digits % 2 == 0)

min_left_half = max(10 ** (int(ceil(nd / 2)) - 1),
minimum // (10 ** (nd // 2)))
max_left_half = min((10 ** int(ceil(nd / 2))) - 1,
maximum // (10 ** (nd // 2)))
min_left_half = max(10 ** (int(ceil(num_digits / 2)) - 1),
minimum // (10 ** (num_digits // 2)))
max_left_half = min((10 ** int(ceil(num_digits / 2))) - 1,
maximum // (10 ** (num_digits // 2)))

current_left_half = min_left_half if not reverse else max_left_half

def make_palindrome(left_half, even_nd=False):
right_half = (reverse_num(left_half)
if even_nd
else reverse_num(left_half // 10))
return (left_half * (10 ** (nd // 2))) + right_half
return (left_half * (10 ** (num_digits // 2))) + right_half

if not reverse:
while current_left_half <= max_left_half:
palin = make_palindrome(current_left_half, even_nd)
if minimum <= palin <= maximum:
yield palin
elif palin > maximum:
palindrome = make_palindrome(current_left_half, even_nd)
if minimum <= palindrome <= maximum:
yield palindrome
elif palindrome > maximum:
# since palindromes are generated in increasing order,
# we break out of the loop once we've exceeded the
# maximum value
break
current_left_half += 1
else:
while current_left_half >= min_left_half:
palin = make_palindrome(current_left_half, even_nd)
if minimum <= palin <= maximum:
yield palin
elif palin < minimum:
palindrome = make_palindrome(current_left_half, even_nd)
if minimum <= palindrome <= maximum:
yield palindrome
elif palindrome < minimum:
# since palindromes are generated in decreasing order,
# we break out of the loop once we've gone below the
# minimum value
break
current_left_half -= 1

min_nd, max_nd = num_digits(minimum), num_digits(maximum)
min_nd = num_digits(minimum)
max_nd = num_digits(maximum)

lengths = (range(min_nd, max_nd + 1)
if not reverse
else range(max_nd, min_nd - 1, -1))

return chain(*map(gen_palins_of_length, lengths))
return chain(*map(gen_palindromes_of_length, lengths))
4 changes: 2 additions & 2 deletions exercises/palindrome-products/palindrome_products.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
def largest_palindrome(max_factor, min_factor):
def largest(min_factor, max_factor):
pass


def smallest_palindrome(max_factor, min_factor):
def smallest(min_factor, max_factor):
pass
51 changes: 25 additions & 26 deletions exercises/palindrome-products/palindrome_products_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Notes regarding the implementation of smallest_palindrome and
largest_palindrome:
Notes regarding the implementation of smallest and
largest:
Both functions must take two keyword arguments:
max_factor -- int
Expand All @@ -13,70 +13,69 @@

import unittest

from palindrome_products import smallest_palindrome, largest_palindrome
from palindrome_products import smallest, largest


# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0

class PalindromeProductsTest(unittest.TestCase):
def test_smallest_palindrome_from_single_digit_factors(self):
value, factors = smallest_palindrome(min_factor=1, max_factor=9)
value, factors = smallest(min_factor=1, max_factor=9)
self.assertEqual(value, 1)
self.assertFactorsEqual(factors, {(1, 1)})
self.assertFactorsEqual(factors, [[1, 1]])

def test_largest_palindrome_from_single_digit_factors(self):
value, factors = largest_palindrome(min_factor=1, max_factor=9)
value, factors = largest(min_factor=1, max_factor=9)
self.assertEqual(value, 9)
self.assertFactorsEqual(factors, {(1, 9), (3, 3)})
self.assertFactorsEqual(factors, [[1, 9], [3, 3]])

def test_smallest_palindrome_from_double_digit_factors(self):
value, factors = smallest_palindrome(min_factor=10, max_factor=99)
value, factors = smallest(min_factor=10, max_factor=99)
self.assertEqual(value, 121)
self.assertFactorsEqual(factors, {(11, 11)})
self.assertFactorsEqual(factors, [[11, 11]])

def test_largest_palindrome_from_double_digit_factors(self):
value, factors = largest_palindrome(min_factor=10, max_factor=99)
value, factors = largest(min_factor=10, max_factor=99)
self.assertEqual(value, 9009)
self.assertFactorsEqual(factors, {(91, 99)})
self.assertFactorsEqual(factors, [[91, 99]])

def test_smallest_palindrome_from_triple_digit_factors(self):
value, factors = smallest_palindrome(min_factor=100, max_factor=999)
value, factors = smallest(min_factor=100, max_factor=999)
self.assertEqual(value, 10201)
self.assertFactorsEqual(factors, {(101, 101)})
self.assertFactorsEqual(factors, [[101, 101]])

def test_largest_palindrome_from_triple_digit_factors(self):
value, factors = largest_palindrome(min_factor=100, max_factor=999)
value, factors = largest(min_factor=100, max_factor=999)
self.assertEqual(value, 906609)
self.assertFactorsEqual(factors, {(913, 993)})
self.assertFactorsEqual(factors, [[913, 993]])

def test_smallest_palindrome_from_four_digit_factors(self):
value, factors = smallest_palindrome(min_factor=1000, max_factor=9999)
value, factors = smallest(min_factor=1000, max_factor=9999)
self.assertEqual(value, 1002001)
self.assertFactorsEqual(factors, {(1001, 1001)})
self.assertFactorsEqual(factors, [[1001, 1001]])

def test_largest_palindrome_from_four_digit_factors(self):
value, factors = largest_palindrome(min_factor=1000, max_factor=9999)
value, factors = largest(min_factor=1000, max_factor=9999)
self.assertEqual(value, 99000099)
self.assertFactorsEqual(factors, {(9901, 9999)})
self.assertFactorsEqual(factors, [[9901, 9999]])

def test_empty_for_smallest_palindrome_if_none_in_range(self):
value, factors = smallest_palindrome(min_factor=1002, max_factor=1003)
value, factors = smallest(min_factor=1002, max_factor=1003)
self.assertIsNone(value)
self.assertFactorsEqual(factors, [])

def test_empty_for_largest_palindrome_if_none_in_range(self):
value, factors = largest_palindrome(min_factor=15, max_factor=15)
value, factors = largest(min_factor=15, max_factor=15)
self.assertIsNone(value)
self.assertFactorsEqual(factors, [])

def test_error_for_smallest_if_min_is_more_than_max(self):
def test_error_for_smallest_palindrome_if_min_is_more_than_max(self):
with self.assertRaisesWithMessage(ValueError):
value, factors = smallest_palindrome(min_factor=10000,
max_factor=1)
value, factors = smallest(min_factor=10000, max_factor=1)

def test_error_for_largest_if_min_is_more_than_max(self):
def test_error_for_largest_palindrome_if_min_is_more_than_max(self):
with self.assertRaisesWithMessage(ValueError):
value, factors = largest_palindrome(min_factor=2, max_factor=1)
value, factors = largest(min_factor=2, max_factor=1)

# Utility functions
def setUp(self):
Expand Down

0 comments on commit 88cd48b

Please sign in to comment.