# -*- coding: utf-8 -*-
# Copyright 2015 Sameer Suhas Marathe
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
.. module:: eulerlib.numtheory
:synopsis: Miscellaneous functions.
.. moduleauthor:: Sameer Marathe
"""
from ._exceptions import EulerlibInputError
__all__ = ["decimal_to_base", "is_palindrome", "is_pandigital", "num_to_list",
"list_to_num", "word_numerical_val", "write_number",
"collapse_lists"]
[docs]def decimal_to_base(num,b):
"""Converts *num* from decimal to base *b*
:param num: A decimal integer
:param b: A decimal integer value for the base. 1 < b <= 36
:returns: String that represents decimal *num* in base *b*
For example::
>>> decimal_to_base(8,2)
'1000'
"""
symbols = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
'q','r','s','t','u','v','w','x','y','z']
numstring = ""
if(b > 36 or b < 2):
return numstring
else:
done = False
while(not done):
cur_digit = num%b
if(cur_digit > 9):
cur_str = symbols[cur_digit - 10]
else:
cur_str = str(cur_digit)
numstring = cur_str+numstring
num = num//b
if(num < b):
if(num != 0):
if(num > 9):
cur_str = symbols[num - 10]
else:
cur_str = str(num)
numstring = cur_str + numstring
done = True
return numstring
[docs]def is_palindrome(num,base=10):
"""Returns *True* if decimal number *num* is a Palindrome number in *base*.
:param num: Decimal number to be tested
:param base: Base of the number system in which *num* is to be tested.
(Default = 10)
:returns: *True* if *num* is palindromic in *base*
For example::
>>> is_palidrome(3875783) #Test in default base = 10
>>> True
>>> is_palidrome(9,2) # 9 in base 2 is 1001
>>> True
"""
str_num = str(num)
if base != 10:
str_num = decimal_to_base(num,base)
rev_num = str_num[::-1]
if(str_num == rev_num):
return True
else:
return False
[docs]def is_pandigital(num,start,stop):
"""Returns *True* if *num* is Pandigital.
:param num: An integer to be checked for pandigitalness.
:param start: Strating digit. 0 <= start <= 9
:param stop: Ending digit start < stop <= 9
For example::
>>> is_pandigital(2134, 1, 4)
True
"""
if(start < 0 or stop < 0 or start >9 or stop >9):
raise EulerlibInputError('etc','is_pandigital',
'start or stop parameter value is incorrect')
elif(stop < start):
raise EulerlibInputError('etc','is_pandigital',
'start should be smaller than stop')
else:
test = list(range(start,stop+1))
nl = num_to_list(num)
if(len(nl) != (stop-start)+1):
return False
else:
nl.sort()
if(nl == test):
return True
else:
return False
[docs]def num_to_list(num):
"""Returns a list of digits in num (most significant digit first)."""
result = []
if (num < 0):
return result
else:
str_num = str(num)
for c in str_num:
result.append(int(c))
return result
[docs]def list_to_num(list_of_digits):
"""Returns an integer from a *list_of_digits*."""
num = 0
if(list_of_digits == []):
return num
test = range(10)
for digit in list_of_digits:
if(not (digit in test)):
raise EulerlibInputError('etc','list_to_num',
'Invalid member in list_of_digits')
num = 0
break
num = num*10 + digit
return num
[docs]def word_numerical_val(word):
"""Returns the sum of numerical value of each letter in the
word based on its position in the alphabet.
"""
import string
num_val = 0
lcase = string.ascii_lowercase
test_word = word.lower()
for char in test_word:
if not char in lcase:
raise EulerlibInputError('etc','word_numerical_val',
'Illegal character in the word')
num_val = 0
break
else:
loc = lcase.find(char)
if (loc != -1):
num_val = num_val + loc + 1
else:
raise EulerlibInputError('etc','word_numerical_val',
'Illegal character in the word')
num_val = 0
break
return num_val
[docs]def write_number(num):
"""Return the written English language string representation of *num*
:param num: An integer number
For example::
>>> write_num(132)
>>> 'one hundred and thirty-two'
"""
ones = ["zero","one","two","three","four","five","six","seven","eight",
"nine"]
teens = ["eleven","twelve","thirteen","fourteen","fifteen","sixteen",
"seventeen","eighteen","nineteen"]
tens = ["ten","twenty","thirty","forty","fifty","sixty","seventy","eighty",
"ninety"]
hundred = " hundred"
hundred_and = " hundred and "
thousand = " thousand"
thousand_and = " thousand "
minus = "minus "
num1 = abs(num)
number = ""
if num1 < 1000000:
if num1 < 10:
number = ones[num1]
elif num1 >= 10 and num1 < 100:
if num1%10 == 0:
#divisible by 10
number = tens[(num1//10)-1]
elif num1 > 10 and num1 < 20:
#teens
number = teens[num1-11]
else:
#create number by recursion
tens_val = num1//10
ones_val = num1%10
number = tens[tens_val-1] + "-" + ones[ones_val]
elif num1 >= 100 and num1 < 1000:
if num1%100 == 0:
hundreds_val = num1//100
number = write_number(hundreds_val) + hundred
else:
hundreds_val = num1//100
rest_of_num = num1%100
number = ones[hundreds_val] + hundred_and + \
write_number(rest_of_num)
else:
thousands_val = num1//1000
rest_of_num = num1%1000
if rest_of_num == 0:
number = write_number(thousands_val) + thousand
else:
number = write_number(thousands_val) + thousand_and + \
write_number(rest_of_num)
if num < 0:
number = minus + number
else:
number = "out of range"
return number
[docs]def collapse_lists(list1,list2,compf,pathf):
"""Function to collapse two lists into a single list based on comparison
and path functions.
:param list1: First list
:param list2: Second list
:param compf: Comparator function to compare values returned by
path function.
:param pathf: Function that takes a list of elements and returns a
value of the same type.
:returns: A list with length = len(list2)
.. note::
* Both lists must have values of the same type 'T'
* Comparison function should take a list of values of type 'T' and
return a value of type 'T'.
* Path function should take a list of values of type 'T' and return a
value of type 'T'.
* The function calculates path totals based on paths from list1 to
list2.
* The difference between lengths of list1 and list2 should be 1
For example: To calculate maximum sum of path values -
path function => sum, comparison function => max::
>>> list1 = [12,37,53,46]
>>> list2 = [23,34,47]
>>> compf = max
>>> pathf = sum
>>> collapse_lists(list1,list2,compf,pathf)
>>> [60, 87, 100]
"""
result = []
len1 = len(list1)
len2 = len(list2)
if (abs(len1-len2) != 1):
pass
elif(list1 == []):
result = list(list2)
elif(list2 == []):
result = list(list1)
else:
small2large = True
if(len1 > len2):
small2large = False
for j in range(len2):
if(small2large):
if(j == 0):
val = pathf([list2[j],list1[0]])
elif(j == len2-1):
val = pathf([list2[j],list1[len1-1]])
else:
pv1 = pathf([list2[j],list1[j-1]])
pv2 = pathf([list2[j],list1[j]])
val = compf([pv1, pv2])
result.append(val)
else:
pv1 = pathf([list2[j],list1[j+1]])
pv2 = pathf([list2[j],list1[j]])
val = compf([pv1, pv2])
result.append(val)
return result