# methods testing whether numbers have certain properties
from math import sqrt
from projecteuler.Divisors import prime_factor_array
from functools import reduce
from operator import mul

# is the number's digits the same forward and backward?
def is_palindrome(n):
    digit_array = [int(c) for c in str(n)]
    length = len(digit_array)
    for i in range(0, length // 2):
        if digit_array[i] != digit_array[length - i - 1]:
            return False
    return True

# does the number contain every digit from 1-d
# (0-d if include_zero is true) exactly once?
def is_pandigital(n, d, include_zero):
    return sorted(str(n)) == sorted(str(i) for i in range((not include_zero), d + 1))

# is x a permutation of y?
# works for both numbers and strings
def is_permutation(x, y):
    return sorted(str(x)) == sorted(str(y))

# proof for is_pentagonal:
# formula for nth pentagonal number, x, is x = n(3n-1)/2
# rearranging the formula: 3n^2 - n - 2x = 0
# using quadratic formula: n = (1 +/- sqrt(1 + 24x)) / 6
# therefore if (1 + sqrt(1 + 24x)) / 6 is an integer, the number is pentagonal
# so (1 + sqrt(1 + 24x)) % 6 must equal 0
# and sqrt(1 + 24x) % 6 must equal 5

# is the given number a pentagonal number?
def is_pentagonal(n):
    return sqrt(1 + 24 * n) % 6 == 5

# is the given number a triangular number?
# same logic applies as for is_pentagonal
def is_triangular(n):
    return sqrt(1 + 8 * n) % 1 == 0

# is the given number an s-gonal number?
# logic abstracted from is_pentagonal and
# from wikipedia's polygonal number formula
def is_polygonal(x, s):
    a = s / 2 - 1
    b = -(s / 2 - 2)
    c = -x
    return ((-b + sqrt(b**2 - 4 * a * c)) / (2 * a)) % 1 == 0

# returns the nth s-gonal number
# using wikipedia's polygonal number formula
def nth_polygonal(n, s):
    return (n**2 * (s - 2) - n * (s - 4)) // 2

# returns the totient of n using wikipedia's totient formula:
# n * (product of 1 - 1/p for all primes dividing n) == totient(n)
# BEFORE USING: generate_primes must be called with at least n+1
def totient(n):
    if n == 1:
        return 1
    return (int) (n * reduce(mul, [1 - 1 / p for p, e in prime_factor_array(n)]))
