#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    This file contains the generators and their inverses for common archimedean copulas.
"""

import numpy as np

def boundsConditions(x):
    if x < 0 or x > 1:
        raise ValueError("Unable to compute generator for x equals to {}".format(x))

def claytonGenerator(x, theta):
    boundsConditions(x)
    if theta == 0:
        raise ValueError("The parameter of a Clayton copula must not be equal to 0.")
    if theta < -1:
        raise ValueError("The parameter of a Clayton copula must be greater than -1 and different from 0.")
    return (1. / theta) * (x**(-theta) - 1.)

def claytonGeneratorInvert(x, theta):
    if theta == 0:
        raise ValueError("The parameter of a Clayton copula must not be equal to 0.")
    if theta < -1:
        raise ValueError("The parameter of a Clayton copula must be greater than -1 and different from 0.")
    return (1. + theta * x)**(-1. / max(theta,1e-6))

def gumbelGenerator(x, theta):
    boundsConditions(x)
    if theta < 1:
        raise ValueError("The parameter of a Gumbel copula must be greater than 1.")
    return (-np.log(x))**theta

def gumbelGeneratorInvert(x, theta):
    if len(theta) > 1: 
        theta = theta[0]
    if theta < 1:
        raise ValueError("The parameter of a Gumbel copula must be greater than 1.")
    if (x < 1 and theta != 1):
        raise(ValueError("The inverse Gumbel generator cannot be evaluated for negative input and theta > 1"))
    return np.exp(-np.power(x,np.divide(1, theta)))


def frankGenerator(x, theta):
    boundsConditions(x)
    if theta == 0:
        raise ValueError("The parameter of a Frank copula must not be equal to 0.")
    return -np.log((np.exp(-theta[0] * x) - 1) / (np.exp(-theta[0]) - 1))

def frankGeneratorInvert(x, theta):
    if theta == 0:
        raise ValueError("The parameter of a Frank copula must not be equal to 0.")
    return -1. / theta * np.log(1. + np.exp(-x) * (np.exp(-theta) - 1.))

def joeGenerator(x, theta):
    boundsConditions(x)
    if theta < 1:
        raise ValueError("The parameter of a Joe copula must be greater than 1.")
    return -np.log(1. - (1. - x)**theta)

def joeGeneratorInvert(x, theta):
    if theta < 1:
        raise ValueError("The parameter of a Joe copula must be greater than 1.")
    return 1. - (1. - np.exp(-x))**(1. / max(theta,1e-6))

def aliMikhailHaqGenerator(x, theta):
    boundsConditions(x)
    if theta < -1 or theta >= 1:
        raise ValueError("The parameter of an Ali-Mikhail-Haq copula must be between -1 included and 1 excluded.")
    return np.log((1. - theta * (1. - x)) / x)

def aliMikhailHaqGeneratorInvert(x, theta):
    if theta < -1 or theta >= 1:
        raise ValueError("The parameter of an Ali-Mikhail-Haq copula must be between -1 included and 1 excluded.")
    return (1. - theta) / (np.exp(x) - theta)

[((71, 11, 71, 46), 'numpy.log', 'np.log', ({(71, 18, 71, 45): '((1.0 - theta * (1.0 - x)) / x)'}, {}), '((1.0 - theta * (1.0 - x)) / x)', True, 'import numpy as np\n'), ((60, 12, 60, 40), 'numpy.log', 'np.log', ({(60, 19, 60, 39): '(1.0 - (1.0 - x) ** theta)'}, {}), '(1.0 - (1.0 - x) ** theta)', True, 'import numpy as np\n'), ((33, 13, 33, 22), 'numpy.log', 'np.log', ({(33, 20, 33, 21): 'x'}, {}), '(x)', True, 'import numpy as np\n'), ((76, 27, 76, 36), 'numpy.exp', 'np.exp', ({(76, 34, 76, 35): 'x'}, {}), '(x)', True, 'import numpy as np\n'), ((42, 30, 42, 49), 'numpy.divide', 'np.divide', ({(42, 40, 42, 41): '(1)', (42, 43, 42, 48): 'theta'}, {}), '(1, theta)', True, 'import numpy as np\n'), ((65, 22, 65, 32), 'numpy.exp', 'np.exp', ({(65, 29, 65, 31): '(-x)'}, {}), '(-x)', True, 'import numpy as np\n'), ((49, 20, 49, 41), 'numpy.exp', 'np.exp', ({(49, 27, 49, 40): '(-theta[0] * x)'}, {}), '(-theta[0] * x)', True, 'import numpy as np\n'), ((49, 50, 49, 67), 'numpy.exp', 'np.exp', ({(49, 57, 49, 66): '(-theta[0])'}, {}), '(-theta[0])', True, 'import numpy as np\n'), ((54, 37, 54, 47), 'numpy.exp', 'np.exp', ({(54, 44, 54, 46): '(-x)'}, {}), '(-x)', True, 'import numpy as np\n'), ((54, 51, 54, 65), 'numpy.exp', 'np.exp', ({(54, 58, 54, 64): '(-theta)'}, {}), '(-theta)', True, 'import numpy as np\n')]