# -*- coding: utf-8 -*-

import metrolopy as uc
import numpy as np
import warnings
import unittest

from metrolopy.tests.common import rand,display

class TestCreate(unittest.TestCase):
    def test_gummy_init(self,n=None,exception_on_warning=True,prnt=False,plot=False):
        """
        Test gummy creation.
        """
        try:
            from scipy.stats import norm, t
            
            uc.gummy.p_method = None
            
            if n is None:
                if prnt or plot:
                    n = 100
                else:
                    n = 10000
            
            units=['m','lb','m**2 s**3/kg**4','degF','cm','%','kg']
            uunits = {'%':100, 'ppm':1e6, 'ppb':1e9, 'ms/s':1000}
            
            with warnings.catch_warnings():
                if exception_on_warning:
                    warnings.simplefilter('error')
                else:
                    warnings.simplefilter('ignore')
            
                for i in range(n):
                    
                    if rand.randint(2):
                        unit = units[rand.randint(len(units))]
                    else:
                        unit = uc.one
                        
                    if rand.randint(2):
                        if unit == 'm':
                            if rand.randint(2):
                                uunit = 'mm'
                            elif rand.randint(2):
                                uunit = 'm'
                            else:
                                uunit = 'in'
                        else:
                            if unit in ['degF','dB(SPL)','Np']:
                                uunit = None
                            else:
                                uunit = list(uunits.keys())[rand.randint(len(uunits))]
                    else:
                        uunit = None
                    
                    bayesian = bool(rand.randint(2))
                    
                    if rand.randint(2):
                        dof = rand.randint(3,20)
                    else:
                        dof = float('inf')
                        
                    if rand.randint(2):
                        if rand.randint(2):
                            k = 4*rand.rand() + 0.1
                            p = None
                        else:
                            p = rand.rand()/2 + 0.49
                            k = 1
                    else:
                        k = 1
                        p = None
                    
                    if rand.randint(2):
                        pmethods = ['loc','level of confidence','cp','coverage probability','gauss','ccp','ccp','conservative coverage probability','chebyshev']
                        uc.gummy.p_method = pmethods[rand.randint(len(pmethods))]
                    else:
                        uc.gummy.p_method = None
                    
                    if unit == 'dB(SPL)':
                        if rand.randint(10) == 0:
                            x = rand.randint(-10,10)
                        else:
                            x = 100.0*(2*rand.rand() - 1)
                    elif unit == 'Np':
                        if rand.randint(10) == 0:
                            x = rand.randint(-10,10)
                        else:
                            x = 20.0*(2*rand.rand() - 1)
                    else:
                        if rand.randint(10) == 0:
                            x = rand.randint(-60000,60000)
                        else:
                            x = (2*rand.rand() - 1)*10.0**rand.randint(-10,10)
                    if rand.randint(20) == 0:
                        x = 0
                            
                    if uunit in ['%','ppm','ppb','ms/s'] and not (unit == '%' and uunit == '%'):
                        U = 1.0e3*rand.rand()
                        u = abs(x)*U/uunits[uunit]
                    elif unit == 'm' and uunit == 'mm':
                        if x == 0:
                            U = 1.0e3*rand.rand()
                        else:
                            U = abs(x)*1.0e3*rand.rand()
                        u = U/1000
                    elif unit == 'm' and uunit == 'in':
                        if x == 0:
                            U = 100*rand.rand()
                        else:
                            U = abs(x)*100*rand.rand()
                        u = U*0.0254 
                    else:
                        if x == 0:
                            U = 1.0e12*rand.rand()
                        else:
                            if rand.randint(20) == 0:
                                U = 1e6*abs(x)*rand.rand()
                            elif rand.randint(20) == 0:
                                U = rand.randint(20)+1
                            else:
                                U = 1.1*abs(x)*rand.rand()
                        u = U
                            
                    if rand.randint(2):
                        name = chr(rand.randint(20) + 65) + chr(rand.randint(20) + 65) + chr(rand.randint(20) + 65)
                    else:
                        name = None
                        
                    if rand.randint(2):
                        utype = chr(rand.randint(20) + 65) + chr(rand.randint(20) + 65) + chr(rand.randint(20) + 65)
                    else:
                        utype = None
                        
                    if rand.randint(20) == 0:
                        self.assertTrue(uc.gummy(x) == x)
                        continue
                    
                    if rand.randint(20) == 0:
                        const = True
                        U = 0
                        u = 0
                    else:
                        if u == 0:
                            const = True
                        else:
                            const = False
                        
                    uc.gummy.bayesian = bayesian
                    if uunit is not None and rand.randint(2):
                        uu = uc.gummy(U,unit=uunit)
                        g = uc.gummy(x,u=uu,unit=unit,dof=dof,k=k,p=p,name=name,
                                     utype=utype)
                    else:
                        g = uc.gummy(x,u=U,unit=unit,dof=dof,k=k,p=p,uunit=uunit,
                                     name=name,utype=utype)
                        
                    if not const:
                        u = u/g.k
                        
                    orig_g = g
        
                    if rand.randint(10) == 0:
                        n = rand.randint(12)
                        #if bayesian and n > 0:
                           # dof = float('inf')
                        if n == 0:
                            nm = g.name
                            g = uc.gummy(g)
                            self.assertTrue(g.name is nm)
                            g.name = None
                        elif n == 1:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                g = 1*g
                        elif n == 2:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                g = g*1
                        elif n == 3:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                g = g/1
                        elif  n == 4:
                            if unit == 'degF':
                                v = uc.gummy(0,unit='degF-i')
                            elif unit is not uc.one or rand.randint(2):
                                v = uc.gummy(0,unit=unit)
                            else:
                                v = 0
                            g = g + v
                        elif n == 5:
                            if unit == 'degF':
                                v = uc.gummy(0,unit='degF-i')
                            elif unit is not uc.one or rand.randint(2):
                                v = uc.gummy(0,unit=unit)
                            else:
                                v = 0
                            g = v + g
                        elif n == 6:
                            if unit == 'degF':
                                v = uc.gummy(0,unit='degF-i')
                            elif unit is not uc.one or rand.randint(2):
                                v = uc.gummy(0,unit=unit)
                            else:
                                v = 0
                            g = g - v
                        elif n == 7:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            elif unit in ['dB(SPL)','Np']:
                                g =  g + uc.gummy(0,unit=unit)
                            else:
                                g = g**1
                        elif n == 8:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                g = +g
                        elif n == 9:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                g = 2.1*g
                                g = g/2.1
                        elif n == 10:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                if unit is not uc.one or rand.randint(2):
                                    v = uc.gummy(3.5*x,u=0.45*u,unit=unit)
                                else:
                                    v = 3.3*x
                                g = v - g
                                g = g - v
                                g = -g
                        else:
                            if unit == 'degF':
                                g =  g + uc.gummy(0,unit='degF-i')
                            else:
                                if x >= 0:
                                    g = abs(g)
                                else:
                                    g = -abs(g)
            
                        k = 1
                        p = None
                        reinit = True
                        uunit = None
                        name = None
                    else:
                        reinit = False
            
                    if x != 0 and g.x != 0:
                        self.assertTrue(abs(g.x - x)/abs(x) < 1e-10)
                    else:
                        self.assertTrue(abs(g.x - x) < 1e-10)
                        
                    if x != 0 and g.x != 0:
                        self.assertTrue(abs(g.x - x)/abs(x) < 1e-10)
                    else:
                        self.assertTrue(abs(g.x - x) < 1e-10)
                                    
                    if unit == 'degF':
                        self.assertTrue(g.unit in [uc.Unit.unit('degF'),uc.Unit.unit('degF-i')])
                    else:
                        self.assertTrue(g.unit is uc.Unit.unit(unit))
                    self.assertTrue(g.name == name)
                    
                    if const:
                        self.assertTrue(g.u == 0)
                        if unit is uc.one:
                            if x == 0:
                                self.assertTrue(abs(g - x) < 1e-10)
                            else:
                                self.assertTrue(abs(g - x)/x < 1e-10)
                        elif unit == '%':
                            if x == 0:
                                 self.assertTrue(abs(g - x/100) < 1e-10)
                            else:
                                self.assertTrue(abs((g - x/100)/(x/100)) < 1e-10)
                        elif unit == 'Np':
                            self.assertTrue(abs(g.convert(1) - np.exp(x))/np.exp(x) < 1e-10)
                        continue
                    
                    if uunit in ['%','ppm','ppb','dB','ms/s'] and unit != uunit:
                        self.assertTrue(g.uunit_is_rel)
                    else:
                        self.assertTrue(not g.uunit_is_rel)
                        
                    self.assertTrue((g.x - x)/u < 1e-6)
                    
                    if not uc.gummy.bayesian:
                        if dof == float('inf'):
                            self.assertTrue(g.dof == float('inf'))
                        else:
                            self.assertTrue((g.dof - dof)/dof < 1e-6)
                    
                    if uunit is not None and uunit != unit:
                        self.assertTrue(g.uunit is uc.Unit.unit(uunit))
                        
                    if g.p == 0:
                        self.assertTrue(p is None)
                        self.assertTrue(k <= 3)
                    else:
                        if p is None:
                            self.assertTrue(g.k == k)
                        else:
                            self.assertTrue(g.p == p)
                        
                        if uc.gummy.p_method in ['loc','level of confidence',None]:
                            if dof == float('inf'):
                                self.assertTrue(abs(g.k - norm.ppf(0.5 + g.p/2)) < 1e-6)
                            else:
                                if bayesian:
                                    if g.p is not None:
                                        self.assertTrue(abs(g.k - np.sqrt((dof-2)/dof)*t.ppf(0.5 + g.p/2,dof)) < 1e-6)
                                else:
                                    self.assertTrue(abs(g.k - t.ppf(0.5 + g.p/2,dof)) < 1e-6)
                                    
                        elif uc.gummy.p_method in ['cp','coverage probability','gauss']:
                            if dof == float('inf') or bayesian:
                                if g.p is not None:
                                    self.assertTrue(abs(g.k - 2/(3*(np.sqrt(1 - g.p)))) < 1e-6)
                            else:
                                if g.p is not None:
                                    self.assertTrue(abs(g.k - np.sqrt(dof/(dof-2))*(2/(3*(np.sqrt(1 - g.p))))) < 1e-6)                 
                        else:
                            if dof == float('inf') or bayesian:
                                if g.p is not None:
                                    self.assertTrue(abs(g.k - 1/np.sqrt(1 - g.p)) < 10.0**-6)
                            else:
                                if g.p is not None:
                                    self.assertTrue(abs(g.k - np.sqrt(dof/(dof-2))*(1/np.sqrt(1 - g.p))) < 1e-6)
                            
                    self.assertTrue(abs((g.u - u)/u) < 1e-10)
                    
                    if not reinit:
                        self.assertTrue(abs(g.U - U)/U < 1e-10)
                    
                    self.assertTrue(abs((g.u - u)/u) < 1e-10)
                    
                    self.assertTrue('?' not in g.tostring())
                    
                    if prnt:
                        print('------')
                        print()
                        if rand.randint(2):
                            styles = ['pm','pmi','concise','ueq','x','xf','u','uf','xunit','uunit']
                            g.style = styles[rand.randint(10)]
                            
                            g.show_k = bool(rand.randint(2))
                            g.show_dof = bool(rand.randint(2))
                            g.show_p = bool(rand.randint(2))
                            g.show_name = bool(rand.randint(2))
                            g.solidus = bool(rand.randint(2))
                            g.mulsep = bool(rand.randint(2))
                            if not rand.randint(5):
                                g.nsig = rand.randint(5) + 1
                            
                            self.assertTrue('?' not in g.tostring())
                            print(g.style,', show_k=',g.show_k,', show_dof=',g.show_dof,', show_p=',g.show_p,', show_name=',g.show_name,', solidus=',g.solidus,', mulsep=',g.mulsep,', nsig=',g.nsig,':')
                            print()
                        display(g)
                        print()
                            
                    if plot:
                        print('------')
                        print()
                        print(g)
                        g.sim()
                        if rand.randint(2):
                            styles = ['pmsim','pmsimi','mcisim','cisim','usim','ufsim']
                            g.style = styles[rand.randint(6)]
                        g.slashaxis = bool(rand.randint(2))
                        print(g.style,', slashaxis=',g.slashaxis,':')
                        print()
                        display(g)
                        print()
                        g.hist()
                        print()
                        
                    uc.gummy.bayesian = False
        finally:
            uc.gummy.p_method = 'loc'


if __name__ == '__main__':
    unittest.main()