-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Palindrome products: sync expected test results and input data with problem-specifications. #1814
Merged
cmccandless
merged 8 commits into
exercism:master
from
BethanyG:palindrome-products-changes
Jun 17, 2019
+62
−64
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
05fabb6
Merge pull request #8 from exercism/master
BethanyG 3102456
Merge pull request #9 from exercism/master
BethanyG 1f216ad
palindrome-products: sync expected test results and input data with p…
BethanyG 4e401b3
palindrome-products: Changed fact ref to factor for clarity in examp…
BethanyG bbe1364
palindrome-products: Reverted the deletion of import statement in ex…
BethanyG c0ce05f
Palindrome products: Corrections and modifications to canonical-sync …
BethanyG ad2b3f3
Palindrome products: Correcting test note references to min and max.
BethanyG b07deb7
Merge branch 'master' into palindrome-products-changes
cmccandless File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,53 +1,51 @@ | ||||||
|
||||||
from __future__ import division | ||||||
from itertools import chain | ||||||
from math import log10, floor, ceil | ||||||
|
||||||
|
||||||
def largest_palindrome(max_factor, min_factor): | ||||||
return get_extreme_palindrome_with_factors(max_factor, min_factor, | ||||||
"largest") | ||||||
def largest(min, max): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
return get_extreme_palindrome_with_factors(max, min, "largest") | ||||||
|
||||||
|
||||||
def smallest_palindrome(max_factor, min_factor): | ||||||
return get_extreme_palindrome_with_factors(max_factor, min_factor, | ||||||
"smallest") | ||||||
def smallest(max, min): | ||||||
return get_extreme_palindrome_with_factors(max, min, "smallest") | ||||||
|
||||||
|
||||||
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 | ||||||
|
@@ -62,55 +60,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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
def largest_palindrome(max_factor, min_factor): | ||
def largest(min, max): | ||
pass | ||
|
||
|
||
def smallest_palindrome(max_factor, min_factor): | ||
def smallest(min, max): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,10 +1,10 @@ | ||||||
""" | ||||||
Notes regarding the implementation of smallest_palindrome and | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
largest_palindrome: | ||||||
largest: | ||||||
|
||||||
Both functions must take two keyword arguments: | ||||||
max_factor -- int | ||||||
min_factor -- int, default 0 | ||||||
max -- int | ||||||
min -- int, default 0 | ||||||
|
||||||
Their return value must be a tuple (value, factors) where value is the | ||||||
palindrome itself, and factors is an iterable containing both factors of the | ||||||
|
@@ -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=1, max=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=1, max=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=10, max=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=10, max=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=100, max=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=100, max=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=1000, max=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=1000, max=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=1002, max=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=15, max=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=10000, max=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=2, max=1) | ||||||
|
||||||
# Utility functions | ||||||
def setUp(self): | ||||||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.