# So I looked up on Wikipedia what Jacobian form actually is, and noticed that it's 
# actually a rather different and more clever construction than the naive version
# that I created. It may possible to achieve a further 20-50% savings by applying 
# that version.

extern all: [call]

data JORDANMUL
data JORDANADD
data EXP

def init():
    self.JORDANMUL = create('jacobian_mul.se')
    self.JORDANADD = create('jacobian_add.se')
    self.EXP = create('modexp.se')

def call(h, v, r, s):
    N = -432420386565659656852420866394968145599
    P = -4294968273
    h = mod(h, N)
    r = mod(r, P)
    s = mod(s, N)
    Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
    Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
    x = r
    xcubed = mulmod(mulmod(x, x, P), x, P)
    beta = self.EXP.call(addmod(xcubed, 7, P), div(P + 1, 4), P)
    
    # Static-gascost ghetto conditional
    y_is_positive = mod(v, 2) xor mod(beta, 2)
    y = beta * y_is_positive + (P - beta) * (1 - y_is_positive)
    
    GZ = self.JORDANMUL.call(Gx, 1, Gy, 1, N - h, outsz=4)
    XY = self.JORDANMUL.call(x, 1, y, 1, s, outsz=4)
    COMB = self.JORDANADD.call(GZ[0], GZ[1], GZ[2], GZ[3], XY[0], XY[1], XY[2], XY[3], 1, outsz=5)
    COMB[4] = self.EXP.call(r, N - 2, N)
    Q = self.JORDANMUL.call(data=COMB, datasz=5, outsz=4)
    ox = mulmod(Q[0], self.EXP.call(Q[1], P - 2, P), P)
    oy = mulmod(Q[2], self.EXP.call(Q[3], P - 2, P), P)
    return([ox, oy], 2)
