/* Montgomery curve arithmetic and X25519 key exchange.
 *
 * We build this on top of Magma's built-in Crv (algebraic curve) type,
 * adding attributes to the type for the various parameters, maps,
 * and arithmetic operations.
 */

AddAttribute(Crv,"AConstant");
AddAttribute(Crv,"BConstant");
AddAttribute(Crv,"xMap");
AddAttribute(Crv,"WeierstrassIsomorphism");
AddAttribute(Crv,"xADDOperation");
AddAttribute(Crv,"xDBLOperation");

function AParameter(C)
    // Return the A-parameter value for a Montgomery curve C.
    ok, A := HasAttribute(C,"AConstant");
    error if not ok, "C has no value for A defined";
    return A;
end function;

function BParameter(C)
    // Return the B-parameter value for a Montgomery curve C.
    ok, B := HasAttribute(C,"BConstant");
    error if not ok, "C has no value for B defined";
    return B;
end function;

function xMap(C)
    // Return the x-line mapping C -> PP^1
    ok, x := HasAttribute(C,"xMap");
    error if not ok, "C has no x-map defined";
    return x;
end function;

function WeierstrassModel(C)
    // Return a Weierstrass model E for a Montgomery curve C,
    // together with an isomorphism from E to C.
    ok, i := HasAttribute(C,"WeierstrassIsomorphism");
    error if not ok, "C has no Weierstrass model defined";
    E := Domain(i);
    return E, i;
end function;

function xADDOperation(C)
    // Return a function defining the xADD operation
    // (x(P),x(Q),x(P-Q)) mapsto x(P+Q)
    ok, xADD := HasAttribute(C,"xADDOperation");
    error if not ok, "C has no xADD operation defined";
    return xADD;
end function;

function xDBLOperation(C)
    // Return a function defining the xDBL operation x(P) mapsto x([2]P)
    ok, xDBL := HasAttribute(C,"xDBLOperation");
    error if not ok, "C has no xDBL operation defined";
    return xDBL;
end function;

/*** End of the attribute stuff: the real work starts here! ***/

function MontgomeryModel(A,B)
    // Returns the Montgomery curve C: BY^2Z = X(X^2 + AXZ + Z^2)
    // together with the projection x: C -> PP^1 onto the x-axis
    // and an isomorphism i: E -> C where E is the Weierstrass model
    // E: y^2 = x(x^2 + ABx + B^2) (as a Magma CrvEll object).

    k := Parent(A);
    assert B in k;

    // EXERCISE 1.1: Defining C.
    PP2<X,Y,Z> := ProjectiveSpace(k,2);
    C := Curve(PP2, /* FILL IN DEFINING EQUATION HERE */);
    // set attributes :
    C`AConstant := A; 
    C`BConstant := B;

    // EXERCISE 1.2: Construct PP^1 and x: C -> PP^1
    PP1 := ProjectiveSpace(k,1);
    /* uncomment and complete the following lines: */
    // x := map< C -> PP1 | /* FILL IN IMAGES HERE */ >;
    // C`xMap := x;  // set corresponding attribute

    // EXERCISE 1.3: Construct E and the isomorphism C <-> E
    E := EllipticCurve(Polynomial([0,B^2,A*B,1]));
    /* uncomment and fill in the following lines: */
    // i := iso< E -> C | /* FILL IN IMAGES HERE */ , /* PREIMAGES HERE */ >;
    // C`WeierstrassIsomorphism := i;  // set corresponding attribute

    // EXERCISE 1.4: Pseudo-doubling
    /* uncomment and fill in the following lines: */
    // xDBL := func< xP | /* FILL IN IMAGE HERE */ >;
    // C`xDBLOperation := xDBL;  // set corresponding attribute

    
    // EXERCISE 1.5: Pseudo-addition
    /* uncomment and fill in the following lines: */
    // xADD := func< xP, xQ, xD | /* FILL IN IMAGE HERE */ >;
    // C`xADDOperation := xADD;  // set corresponding attribute

    // Done! Return C.
    return C;
end function;

