diff --git a/bin/palindrome-product.py b/bin/palindrome-product.py new file mode 100755 index 0000000..f35c88c --- /dev/null +++ b/bin/palindrome-product.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +import sys +import datetime +from datetime import timedelta +from palindrome.products import largest_palindrome + +desc = '''Find the largest palindrome made from the\ +product of two 3-digit numbers.''' + +start = datetime.datetime.now() +result = largest_palindrome(3) +finish = datetime.datetime.now() + +msg = '''That largest palindrome with two 3-digit factors is {0:,} +It's factors are {1} & {2} +Finished in {3}.{4} seconds''' + +delta = finish - start +print msg.format(result.palindrome, + result.factor1, + result.factor2, + delta.seconds, + delta.microseconds) diff --git a/palindrome/__init__.py b/palindrome/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/palindrome/products.py b/palindrome/products.py new file mode 100644 index 0000000..2cef8ff --- /dev/null +++ b/palindrome/products.py @@ -0,0 +1,52 @@ +from collections import namedtuple + + +Range = namedtuple('Range', ['low', 'high']) + + +class Results: + + def __init__(self, palindrome, factor1, factor2): + self.palindrome = palindrome + self.factor1 = factor1 + self.factor2 = factor2 + + +def reverse_num(num): + if num < 0: + return None + rev = 0 + while num != 0: + rev = rev * 10 + num % 10 + num = num / 10 + return rev + + +def range_for_n_digit_nums(n): + return Range(low=10 ** (n - 1), high=10 ** n) + + +def generate_palindromes(num_digits): + palindromes = [] + factor_range = range_for_n_digit_nums(num_digits) + uppers = range(factor_range.low, factor_range.high) + uppers.reverse() + upper_bound = factor_range.high * factor_range.high + for u in uppers: + palindrome = (u * factor_range.high) + reverse_num(u) + if palindrome <= upper_bound: + palindromes.append(palindrome) + + return palindromes + + +def largest_palindrome(num_digits): + factor_range = range_for_n_digit_nums(num_digits) + palindromes = generate_palindromes(num_digits) + factors = range(factor_range.low, factor_range.high) + factors.reverse() + for palindrome in palindromes: + for factor in factors: + if palindrome % factor == 0: + if factor_range.low <= palindrome / factor < factor_range.high: + return Results(palindrome, factor, palindrome / factor) diff --git a/test/test_palindromes.py b/test/test_palindromes.py new file mode 100644 index 0000000..8c2abde --- /dev/null +++ b/test/test_palindromes.py @@ -0,0 +1,36 @@ +import pytest +from palindrome.products import * + + +class TestPalindromes: + + @pytest.mark.parametrize(("value", "expected_low", "expected_high"), [ + [2, 10, 100], + [3, 100, 1000], + [4, 1000, 10000], ]) + def test_range_for_n_digit_nums(self, value, expected_low, expected_high): + r = range_for_n_digit_nums(value) + assert r.low == expected_low and r.high == expected_high + + def test_palindromes(self): + assert generate_palindromes(num_digits=3) + + def test_palindrome_range(self): + palindromes = generate_palindromes(num_digits=3) + assert len(palindromes) == 900 + for p in palindromes: + assert p > 100000 and p < 1000000 + + @pytest.mark.parametrize(("value", "expected"), [ + [411, 114], + [515, 515], + [666, 666], + [723, 327], ]) + def test_reverse_num(self, value, expected): + assert reverse_num(value) == expected + + @pytest.mark.parametrize(("value", "expected"), [ + [2, 9009], + [3, 906609], ]) + def test_largest_palindrome(self, value, expected): + assert largest_palindrome(value).palindrome == expected