import numpy as np

def kink(x, t, v, x0):
    g = 1.0/np.sqrt(1.0 - v*v)
    return 4.0*np.arctan(np.exp(g*(x - x0 - v*t)))

def antikink(x, t, v, x0):
    g = 1.0/np.sqrt(1.0 - v*v)
    return 4.0*np.arctan(np.exp(-g*(x - x0 - v*t)))

def kink_t(x, t, v, x0):
    g = 1.0/np.sqrt(1.0 - v*v)
    cosh_arg = g*(x-x0-v*t)
    return -2*g*v/np.cosh(cosh_arg)

def antikink_t(x, t, v, x0):
    return -kink_t(x, t, x0, v)

def kink_kink(x, t, v):
    gamma = 1/(1-v**2)
    frac = v*np.sinh(x*gamma)/np.cosh(v*t*gamma)
    return 4*np.arctan(frac)

def kink_antikink(x, t, v):
    gamma = 1/(1-v**2)
    frac = v*np.cosh(x*gamma)/np.sinh(v*t*gamma)
    return 4*np.arctan(frac)

def breather(x, t,v,w, gamma):
    sq = np.sqrt(1-w**2)
    frac = sq*np.cos(w*gamma*(t-v*x))/w/np.cosh(sq*gamma*(x-v*t))
    return 4*np.arctan(frac)

def breather_t(x,t,v,w,gamma):
    sq = np.sqrt(1.0 - w**2)
    alpha = w * gamma * v * x
    beta  = sq * gamma * x
    F0 = (sq * np.cos(-w * gamma * v * x)) / (w * np.cosh(beta))

    numer = 4.0 * sq * gamma * (sq * v * np.cos(alpha) * np.tanh(beta) + w * np.sin(alpha))
    denom = w * np.cosh(beta)

    return  numer / (denom * (1.0 + F0**2))

def two_breathers(x,t,v,w,gamma,x0=15):
    al1 = np.sqrt((1-v)/(1+v))
    al2 = np.sqrt((1+v)/(1-v))
    cosq = np.sqrt(1-w**2)
    a1, a2 = (x+x0-v*t)*gamma*cosq,(x-x0+v*t)*gamma*cosq
    b1, b2 = w*gamma*(t-v*x), w*gamma*(t+v*x)
    
    B, C ,D = 4*al1**2*al2**2*(1-2*w), -al1**2+al2**2, al1**2+al2**2
    
    u1,w1 = al1*cosq,al1*w
    u2,w2 = al2*cosq, al2*w

    fi = -u1*w2*C**2*(np.cosh(a1)*np.cos(b2) + np.cosh(a2)*np.cos(b1)) + 4*(u1*w2)**2*C*(
        np.sinh(a1)*np.sin(b2) - np.sinh(a2)*np.sin(b1))
    
    fr = -w1*w2*(D**2+B)*np.cosh(a1)*np.cosh(a2)+u1*u2*(D**2-B)*np.cos(b2)*np.cos(b1) + 4*(u1*w2)**2*D*(
        np.sinh(a1)*np.sinh(a2)+np.sin(b1)*np.sin(b2))
    return 4*np.arctan(fi/fr)

def numeric_2_breathers_dt0(x, v, w, gamma, x0=15, dt=1e-6):
    phi_plus = two_breathers(x, dt, v, w, gamma, x0)
    phi_minus = two_breathers(x, -dt, v, w, gamma, x0)
    return (phi_plus - phi_minus) / (2 * dt)



def breather_plus_breather(x,t,v,w,gamma,x0=15):
    return breather(x-x0,t,-v,w,gamma)+breather(x+x0,t,v,w,gamma)

def kink_plus_antikink(x,t,v,x0):
   return kink(x, t, -x0,  v) + antikink(x, t,  x0, -v) - 2*np.pi

def kink_plus_kink(x,t,v,x0):
    return kink(x, t, -x0,  v) + kink(x, t,  x0, -v)-2*np.pi