from Rings_v2 import *
from random import random, randint

def Ring(char=0,**variables):
    for key, value in variables.items():
        globals()[key] = AbGrpElt({(AbGrpElt({(key,value):1}),char):1})

def createVars(container,char=0,values=0):
    """Creates algebraic variables from an iterable object such as a string"""
    Ring(char,**{x:values for x in container})

def standard(v,w):
    """Standard Velocity Addition formula for two vectors, with norm <1"""
    dot = (v|w)
    norm1 = (v|v)
    piece = (dot*v)*(1/norm1)
    return (piece + v + ((1-norm1)**0.5)*(w-piece))*(1/(1+dot))


UnitQuaternionize = lambda v: Quaternion((1-(v|v))**0.5,v)

def UnitBiQuaternionize(v):
    gamma = 1/((1-(v|v))**0.5)
    return Quaternion(gamma,1j*gamma*v)

def HL_origin(v,w):
    v = UnitQuaternionize(v)
    w = UnitQuaternionize(w)
    return (1/(1+v))*((v+w+v*w-1)/(v+w-v*w+1))*(1+v)

def HL_altern(v,w):
    v = UnitQuaternionize(v)
    w = UnitQuaternionize(w)
    Rv = v.time
    Iv = Quaternion(0,v.space)
    return (Iv+Rv*w+w)/(Rv-Iv*w+1)

def HL_transfo(v,w):
    v = UnitQuaternionize(v)
    w = UnitQuaternionize(w)
    b = (v-1)/(v+1)
    return (b+w)/(1-b*w)

def HL_biquat(u,v):
    u = UnitBiQuaternionize(u)
    v = UnitBiQuaternionize(v)
    return ((1+u)(v))*u

def TAN1(v,w):
    v = Quaternion(0,v)
    w = Quaternion(0,w)
    return (v+w)/(1-v*w)

def TAN2(v,w):
    return TAN1(w,v)

def TAN3(v,w):
    v = Quaternion(0,v)
    w = Quaternion(0,w)
    return (1/(1-v*w))*(v+w)

def TAN4(v,w):
    return TAN3(w,v)


def tanSecWtest(v,w):
    y = Quaternion(0,standard(v,w))
    b = Quaternion(0,v)
    return (y-b)/(b*y+1)


def lin1(a,b):
    return lambda q: a*q*b

def lin2(a,b):
    B = b.inverse()
    return lambda q: a*((B*(q.inverse())).inverse())


def Rfunc(a,b):
    """Quaternionic Mobius Transformation q->(aq-b)/(bq+a)"""
    return lambda q: (a*q-b)/(b*q+a)

def R(a,b):
    return Moebius(a,-b,b,a)

def Fix(a,b):
    """Fixed points of (aq-b)/(bq+a)"""
    c = a+b*1j
    print("c:",c)
    print("-----")
    s = c.time
    print("s:",s)
    print("-----")
    #d = ((((c-(~c))**2).time)**0.5)/2
    d = (((c+~c)*(c+~c)-4*c*(~c)).time**0.5)/2
    print("d:",d)
    print("-----")
    K1 = s+d
    K2 = s-d
    print("K1:",K1,"K2:",K2)
    print("-----")
    t1 = K1.real
    t2 = K2.real
    r1 = K1.imag
    r2 = K2.imag
    print("t1:",t1,"t2:",t2,"r1:",r1,"r2:",r2)
    print("-----")
    l1 = (1/(b-r1))*(t1-a)
    l2 = (1/(b-r2))*(t2-a)
    print("l1:",l1,"l2:",l2)
    print("-----")
    print("a+bl1:",a+b*l1, "a+bl2", a+b*l2)
    print("-----")
    return (l1,l2)
    

def randQuat(m = -9, M = 9, T=None,X=None,Y=None,Z=None):
    (t,x,y,z) = (x if x != None else randint(m,M) for x in (T,X,Y,Z))
    return Quat(t,x,y,z)

def randQuats(container,m = -9, M = 9, T=None,X=None,Y=None,Z=None):
    for q in container:
        globals()[q] = randQuat(m,M,T,X,Y,Z)

def randV(arg=None):
    if arg == None:
        return Vec3(random()-0.5,random()-0.5,random()-0.5).unit()*random() #however, will be positive, but OK
    else:
        for v in arg:
            globals()[v] = randV()


def N_(a,b):
    return (a*b-b*a).unit()

def NlM(a,b):
    N = N_(a,b)
    l = N_(a,N)
    M = N*l
    return (N,l,M)

def H_CN(a,b):
    (N,l,M) = NlM(a,b)
    S = Moebius(1,N,N,1)
    L = Moebius(1,0,0,l)
    T = L@S
    return (T,~T)
    

def multiDual(listA,listB):
    for x in zip(listA,listB):
        dualize(*x)

def dualVars(lst):
    LST = lst.swapcase()
    createVars(lst)
    createVars(LST)
    multiDual((eval(x) for x in lst),(eval(X) for X in LST))

Ta = Moebius(1j,0,0,1)
Tb = Moebius(-1j,0,0,1)
Sa = Moebius(1,1j,1j,1)
Sb = Moebius(1,-1j,-1j,1)
Ra = Moebius(1,-1,1,1)
Rb = Moebius(1,1,-1,1)

RR = lambda f: Ra@f@Rb
SS = lambda f: Sa@f@Sb
TT = lambda f: Ta@f@Tb

def coth(a,b):
    return (1+a).inverse()((a*b+1)/(a+b))

def coth2(a,b):
    return (1+b)((a+b).inverse()*(a*b+1))

def coth3(a,b):
    return (1-b).inverse()((b*a+1)/(a+b))

def coth4(a,b):
    return (1-a)((a+b).inverse()*(b*a+1))

def MobLeftS(a):
    """a@"""
    A = (1+a).inverse()
    return Moebius(A,0,0,A)@Moebius(a,1,1,a)

def MobRightS(b):
    """@(-b)"""
    B = (1+b).inverse()
    return Moebius(B,0,0,B)@Moebius(b,-1,-1,b)


def ReSym(q):
    return coth(q,~q)

def normSym(q):
    RRR = ReSym(q)
    return RRR+(RRR**2-1)**0.5

def conormSym(q):
    return coth(q,-normSym(q))

def ImSym(q):
    return coth(q,-(~q))


def testlittlelaw(a,b):
    somme = a+b
    prodm1 = a*b-1
    return (prodm1-somme)/(prodm1+somme)

def testlilawdirect(a,b):
    return testlittlelaw(Quaternion(0,a),Quaternion(0,b))

def testlilawindirect(a,b):
    A = UnitQuaternionize(a)
    B = UnitQuaternionize(b)
    return testlittlelaw(Ra(A),Ra(B))

tandef = lambda a,b: (a+b)/(1-a*b)

def tanDirect(a,b):
    a = Quaternion(0,a)
    b = Quaternion(0,b)
    return tandef(a,b)

def tanInDirect(a,b):
    a = UnitQuaternionize(a)
    b = UnitQuaternionize(b)
    return Rb(tandef(Ra(a),Ra(b)))

def Fix2(a,b):#Foire
    A = a.space
    B = b.space
    X = (B|B)-(A|A) #(A*A-B*B).time
    Y = -2*(A|B) #(A*B+B*A).time
    R = (X*X+Y*Y)**0.5
    t = ((R+X)/2)**0.5
    r = ((R-X)/2)**0.5
    print("t=", t )
    print("r=", r)
    return (Quaternion(t,A)/Quaternion(r,B),Quaternion(-t,A)/Quaternion(-r,B))

def Fix3(a,b):
    A = a - a.time
    B = b - b.time
    X = (A*A-B*B).time
    Y = (A*B+B*A).time
    R = (X*X+Y*Y)**0.5
    t = ((R+X)/2)**0.5
    r = ((R-X)/2)**0.5
    print("t=", t )
    print("r=", r)
    return ((t+A)/(r+B),(-t+A)/(-r+B))

def Fix4(a,b): #Fonctionne! :D
    A = a - a.time
    B = b - b.time
    beta = A+1j*B
    chi = ((beta*beta).time)**0.5
    print("chi=", chi)
    t = chi.real
    r = chi.imag
    print("t=", t )
    print("r=", r)
    return ((t+A)/(r+B),(-t+A)/(-r+B))

def Fix5(a,b):
    A = a - a.time
    B = b - b.time
    X = (A*A-B*B).time
    Y = (A*B+B*A).time
    R = (X*X+Y*Y)**0.5
    t = ((R+X)/2)**0.5
    r = Y/(2*t)#r = ((R-X)/2)**0.5
    print("t=", t )
    print("r=", r)
    return ((t+A)/(r+B),(-t+A)/(-r+B))
    