#!/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, 'numpy.log', 'np.log', True, 'import numpy as np\n'), (60, 'numpy.log', 'np.log', True, 'import numpy as np\n'), (33, 'numpy.log', 'np.log', True, 'import numpy as np\n'), (76, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (42, 'numpy.divide', 'np.divide', True, 'import numpy as np\n'), (65, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (49, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (49, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (54, 'numpy.exp', 'np.exp', True, 'import numpy as np\n'), (54, 'numpy.exp', 'np.exp', True, 'import numpy as np\n')]