var ECP = function()

in src/ecp.js [22:1489]


var ECP = function(ctx) {
    "use strict";

    /**
     * Creates an instance of ECP
     *
     * @constructor
     * @this {ECP}
     */    
    var ECP = function(input) {
        if (input instanceof ECP) {
            // copy constructor
            this.x = new ctx.FP(input.x);
            this.y = new ctx.FP(input.y);
            this.z = new ctx.FP(input.z);
        } else {
            // default constructor (point at infinity)
            this.x = new ctx.FP(0);
            this.y = new ctx.FP(1);
            if (ECP.CURVETYPE != ECP.EDWARDS) {
                this.z = new ctx.FP(0);
            } else {
                this.z = new ctx.FP(1);
            }
        }
    };

    ECP.WEIERSTRASS = 0;
    ECP.EDWARDS = 1;
    ECP.MONTGOMERY = 2;
    ECP.NOT = 0;
    ECP.BN = 1;
    ECP.BLS = 2;
    ECP.D_TYPE = 0;
    ECP.M_TYPE = 1;
    ECP.POSITIVEX = 0;
    ECP.NEGATIVEX = 1;

    ECP.CURVETYPE = ctx.config["@CT"];
    ECP.CURVE_PAIRING_TYPE = ctx.config["@PF"];
    ECP.SEXTIC_TWIST = ctx.config["@ST"];
    ECP.SIGN_OF_X = ctx.config["@SX"];
    ECP.ATE_BITS = ctx.config["@AB"];

    ECP.HASH_TYPE = ctx.config["@HT"];
    ECP.AESKEY = ctx.config["@AK"];

    ECP.prototype = {
	
	/**
         * Tests for ECP point equal to infinity
         *
         * @this {ECP}
         * @param 1 if infinity, else returns 0
         */	
        is_infinity: function() {
  
            this.x.reduce();
            this.z.reduce();

            if (ECP.CURVETYPE == ECP.EDWARDS) {

                this.y.reduce();

                return (this.x.iszilch() && this.y.equals(this.z));
            } else if (ECP.CURVETYPE == ECP.WEIERSTRASS) {
                this.y.reduce();
                return (this.x.iszilch() && this.z.iszilch());
            } else if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                return (this.z.iszilch());
            }

            return true;
        },


	/**
         * conditional swap of this and Q dependant on dCopy ECP point to another ECP point
         *
         * @this {ECP}
         */	
        cswap: function(Q, d) {

            this.x.cswap(Q.x, d);
            if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                this.y.cswap(Q.y, d);
            }
            this.z.cswap(Q.z, d);

        },

	/**
         * conditional move of Q to P dependant on d 
         *
         * @this {ECP}
         */	
        cmove: function(Q, d) {

            this.x.cmove(Q.x, d);
            if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                this.y.cmove(Q.y, d);
            }
            this.z.cmove(Q.z, d);

        },

	/**
         * Constant time select from pre-computed table 
         *
         * @this {ECP}
         */	
        select: function(W, b) {
            var MP = new ECP(),
                m = b >> 31,
                babs = (b ^ m) - m;

            babs = (babs - 1) / 2;

            this.cmove(W[0], ECP.teq(babs, 0)); // conditional move
            this.cmove(W[1], ECP.teq(babs, 1));
            this.cmove(W[2], ECP.teq(babs, 2));
            this.cmove(W[3], ECP.teq(babs, 3));
            this.cmove(W[4], ECP.teq(babs, 4));
            this.cmove(W[5], ECP.teq(babs, 5));
            this.cmove(W[6], ECP.teq(babs, 6));
            this.cmove(W[7], ECP.teq(babs, 7));

            MP.copy(this);
            MP.neg();
            this.cmove(MP, (m & 1));
        },

        /* Test P == Q */

        equals: function(Q) {
            var a, b;

            a = new ctx.FP(0);
            b = new ctx.FP(0);
            a.copy(this.x);
            a.mul(Q.z);
            a.reduce();
            b.copy(Q.x);
            b.mul(this.z);
            b.reduce();

            if (!a.equals(b)) {
                return false;
            }

            if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                a.copy(this.y);
                a.mul(Q.z);
                a.reduce();
                b.copy(Q.y);
                b.mul(this.z);
                b.reduce();
                if (!a.equals(b)) {
                    return false;
                }
            }

            return true;
        },

	/**
         * Copy ECP point to another ECP point
         *
         * @this {ECP}
         * @param P ECP instance
         */	
        copy: function(P) {
            this.x.copy(P.x);
            if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                this.y.copy(P.y);
            }
            this.z.copy(P.z);
        },

	/**
         * set this=-this 
         *
         * @this {ECP}
         */	
        neg: function() {
            if (ECP.CURVETYPE == ECP.WEIERSTRASS) {
                this.y.neg();
                this.y.norm();
            } else if (ECP.CURVETYPE == ECP.EDWARDS) {
                this.x.neg();
                this.x.norm();
            }

            return;
        },

	/**
         * Set ECP to point-at-infinity
         *
         * @this {ECP}
         */	
        inf: function() {
            this.x.zero();

            if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                this.y.one();
            }

            if (ECP.CURVETYPE != ECP.EDWARDS) {
                this.z.zero();
            } else {
                this.z.one();
            }
        },

	/**
         * set this=(x,y)
         *
         * @this {ECP}
         * @param ix x-value
         * @param iy y-value
         */	
        setxy: function(ix, iy) {
            var rhs, y2;

            this.x = new ctx.FP(0);
            this.x.bcopy(ix);

            this.y = new ctx.FP(0);
            this.y.bcopy(iy);
            this.z = new ctx.FP(1);
			this.x.norm();
            rhs = ECP.RHS(this.x);

            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                if (rhs.jacobi() != 1) {
                    this.inf();
                }
 
            } else {
                y2 = new ctx.FP(0);
                y2.copy(this.y);
                y2.sqr();

                if (!y2.equals(rhs)) {
                    this.inf();
                }
            }
        },

	/**
         * set this=x, where x is ctx.BIG, y is derived from sign s 
         *
         * @this {ECP}
         * @param ix x-value
         * @param s sign to derive y
         */	
        setxi: function(ix, s) {
            var rhs, ny;

            this.x = new ctx.FP(0);
            this.x.bcopy(ix);
			this.x.norm();
            rhs = ECP.RHS(this.x);
            this.z = new ctx.FP(1);

            if (rhs.jacobi() == 1) {
                ny = rhs.sqrt();
                if (ny.redc().parity() != s) {
                    ny.neg();
                }
                this.y = ny;
            } else {
                this.inf();
            }
        },

	/**
         * 	
         * set this=x, y calculated from curve equation 
         *
         * @this {ECP}
         * @param ix x-value
         */	
        setx: function(ix) {
            var rhs;

            this.x = new ctx.FP(0);
            this.x.bcopy(ix);
			this.x.norm();
            rhs = ECP.RHS(this.x);
            this.z = new ctx.FP(1);

            if (rhs.jacobi() == 1) {
                if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                    this.y = rhs.sqrt();
                }
            } else {
                this.inf();
            }
        },

	/**
         * convert this to affine, from (x,y,z) to (x,y) 
         *
         * @this {ECP}
         */	
        affine: function() {
            var one;

            if (this.is_infinity()) {
                return;
            }

            one = new ctx.FP(1);

            if (this.z.equals(one)) {
                return;
            }

            this.z.inverse();

            if (ECP.CURVETYPE == ECP.EDWARDS || ECP.CURVETYPE == ECP.WEIERSTRASS) {
                this.x.mul(this.z);
                this.x.reduce();
                this.y.mul(this.z);
                this.y.reduce();
                this.z = one;
            }
            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                this.x.mul(this.z);
                this.x.reduce();
                this.z = one;
            }
        },

	/**
         * extract affine x as ctx.FP2 
         *
         * @this {ECP}
         */	
        getX: function() {
			var W=new ECP(); W.copy(this); W.affine();
            return W.x.redc();
        },

	/**
         * extract affine y as ctx.FP2
         *
         * @this {ECP}
         */	
        getY: function() {
			var W=new ECP(); W.copy(this); W.affine();
            return W.y.redc();
        },

	/**
         * get sign of Y 
         *
         * @this {ECP}
         */	
        getS: function() {
            var y = this.getY();
            return y.parity();
        },

	/**
         * extract x as ctx.FP 
         *
         * @this {ECP}
         */	
        getx: function() {
            return this.x;
        },

	/**
         * extract y as ctx.FP
         *
         * @this {ECP}
         */	
        gety: function() {
            return this.y;
        },

	/**
         * extract z as ctx.FP
         *
         * @this {ECP}
         */	
        getz: function() {
            return this.z;
        },

	/**
         * convert this to byte arrayextract projective x
         *
         * @this {ECP}
         * @param b byte array output
         */	
        toBytes: function(b,compress) {
            var t = [],
                i;
			var W=new ECP(); W.copy(this);
            W.affine();
            W.x.redc().toBytes(t);

            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 1] = t[i];
            }

            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                b[0] = 0x06;
                return;
            }

            if (compress) {
                b[0]=0x02;
                if (W.y.redc().parity()==1) {
                    b[0]=0x03;
                }
                return;
            }

            b[0]=0x04;

            W.y.redc().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + ctx.BIG.MODBYTES + 1] = t[i];
            }
        },

	/**
         * convert this to hex string 
         *
         * @this {ECP}
         * @return hex string
         */	
        toString: function() {
			var W=new ECP(); W.copy(this);
            if (W.is_infinity()) {
                return "infinity";
            }

            W.affine();

            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                return "(" + W.x.redc().toString() + ")";
            } else {
                return "(" + W.x.redc().toString() + "," + W.y.redc().toString() + ")";
            }
        },

	/**
         * this+=this 
         *
         * @this {ECP}
         */	
        dbl: function() {
            var t0, t1, t2, t3, x3, y3, z3, b,
                C, D, H, J,
                A, B, AA, BB;

            if (ECP.CURVETYPE == ECP.WEIERSTRASS) {

                if (ctx.ROM_CURVE.CURVE_A == 0) {
                    t0 = new ctx.FP(0);
                    t0.copy(this.y);                    
                    t0.sqr();
                    t1 = new ctx.FP(0);
                    t1.copy(this.y); 
                    t1.mul(this.z);
                    t2 = new ctx.FP(0);
                    t2.copy(this.z); 
                    t2.sqr();

                    this.z.copy(t0);
                    this.z.add(t0);
                    this.z.norm();
                    this.z.add(this.z);
                    this.z.add(this.z);
                    this.z.norm();

                    t2.imul(3 * ctx.ROM_CURVE.CURVE_B_I);

                    x3 = new ctx.FP(0);
                    x3.copy(t2); 
                    x3.mul(this.z);
                    y3 = new ctx.FP(0);
                    y3.copy(t0); 
                    y3.add(t2);
                    y3.norm();
                    this.z.mul(t1);
                    t1.copy(t2);
                    t1.add(t2);
                    t2.add(t1);
                    t0.sub(t2);
                    t0.norm();
                    y3.mul(t0);
                    y3.add(x3);
                    t1.copy(this.x);
                    t1.mul(this.y);
                    this.x.copy(t0);
                    this.x.norm();
                    this.x.mul(t1);
                    this.x.add(this.x);

                    this.x.norm();
                    this.y.copy(y3);
                    this.y.norm();
                } else {
                    t0 = new ctx.FP(0);
                    t0.copy(this.x); 
                    t1 = new ctx.FP(0);
                    t1.copy(this.y); 
                    t2 = new ctx.FP(0);
                    t2.copy(this.z); 
                    t3 = new ctx.FP(0);
                    t3.copy(this.x); 
                    z3 = new ctx.FP(0);
                    z3.copy(this.z); 
                    y3 = new ctx.FP(0); 
                    x3 = new ctx.FP(0); 
                    b = new ctx.FP(0); 
                    
                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        b.rcopy(ctx.ROM_CURVE.CURVE_B);
                    }
                   
                    t0.sqr(); //1    x^2
                    t1.sqr(); //2    y^2
                    t2.sqr(); //3

                    t3.mul(this.y); //4
                    t3.add(t3);
                    t3.norm(); //5
                    z3.mul(this.x); //6
                    z3.add(z3);
                    z3.norm(); //7
                    y3.copy(t2);

                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        y3.mul(b); //8
                    } else {
                        y3.imul(ctx.ROM_CURVE.CURVE_B_I);
                    }

                    y3.sub(z3); //9  ***
                    x3.copy(y3);
                    x3.add(y3);
                    x3.norm(); //10

                    y3.add(x3); //11
                    x3.copy(t1);
                    x3.sub(y3);
                    x3.norm(); //12
                    y3.add(t1);
                    y3.norm(); //13
                    y3.mul(x3); //14
                    x3.mul(t3); //15
                    t3.copy(t2);
                    t3.add(t2);  //16
                    t2.add(t3);  //17

                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        z3.mul(b); //18
                    } else {
                        z3.imul(ctx.ROM_CURVE.CURVE_B_I);
                    }

                    z3.sub(t2); //19
                    z3.sub(t0);
                    z3.norm(); //20  ***
                    t3.copy(z3);
                    t3.add(z3); //21

                    z3.add(t3);
                    z3.norm(); //22
                    t3.copy(t0);
                    t3.add(t0); //23
                    t0.add(t3); //24
                    t0.sub(t2);
                    t0.norm(); //25

                    t0.mul(z3); //26
                    y3.add(t0); //27
                    t0.copy(this.y);
                    t0.mul(this.z); //28
                    t0.add(t0);
                    t0.norm(); //29
                    z3.mul(t0); //30
                    x3.sub(z3); //31
                    t0.add(t0);
                    t0.norm(); //32
                    t1.add(t1);
                    t1.norm(); //33
                    z3.copy(t0);
                    z3.mul(t1); //34
                  
                    this.x.copy(x3);
                    this.x.norm();
                    this.y.copy(y3);
                    this.y.norm();
                    this.z.copy(z3);
                    this.z.norm();
                }
            }

            if (ECP.CURVETYPE == ECP.EDWARDS) {
                C = new ctx.FP(0);
                C.copy(this.x); 
                D = new ctx.FP(0);
                D.copy(this.y); 
                H = new ctx.FP(0);
                H.copy(this.z); 
                J = new ctx.FP(0); 
               
                this.x.mul(this.y);
                this.x.add(this.x);
                this.x.norm();
                C.sqr();
                D.sqr();
                if (ctx.ROM_CURVE.CURVE_A == -1) {
                    C.neg();
                }

                this.y.copy(C);
                this.y.add(D);
                this.y.norm();
                H.sqr();
                H.add(H);

                this.z.copy(this.y);
                J.copy(this.y);

                J.sub(H);
                J.norm();

                this.x.mul(J);
                C.sub(D);
                C.norm();
                this.y.mul(C);
                this.z.mul(J);
            }

            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                A = new ctx.FP(0);
                A.copy(this.x); 
                B = new ctx.FP(0);
                B.copy(this.x); 
                AA = new ctx.FP(0); 
                BB = new ctx.FP(0); 
                C = new ctx.FP(0); 

                A.add(this.z);
                A.norm();
                AA.copy(A);
                AA.sqr();
                B.sub(this.z);
                B.norm();
                BB.copy(B);
                BB.sqr();
                C.copy(AA);
                C.sub(BB);
                C.norm();
                this.x.copy(AA);
                this.x.mul(BB);

                A.copy(C);
                A.imul((ctx.ROM_CURVE.CURVE_A + 2) >> 2);

                BB.add(A);
                BB.norm();
                this.z.copy(BB);
                this.z.mul(C);
            }

            return;
        },

	/**
         * Adds ECP instances
         *
         * param Q ECP instance
         * @this {ECP}
         */		
        add: function(Q) {
            var b, t0, t1, t2, t3, t4, x3, y3, z3,
                A, B, C, D, E, F, G;

            if (ECP.CURVETYPE == ECP.WEIERSTRASS) {
                if (ctx.ROM_CURVE.CURVE_A == 0) {

                    b = 3 * ctx.ROM_CURVE.CURVE_B_I;
                    t0 = new ctx.FP(0);
                    t0.copy(this.x); 
                    t0.mul(Q.x);
                    t1 = new ctx.FP(0);
                    t1.copy(this.y); 
                    t1.mul(Q.y);
                    t2 = new ctx.FP(0);
                    t2.copy(this.z); 
                    t2.mul(Q.z);
                    t3 = new ctx.FP(0);
                    t3.copy(this.x); 
                    t3.add(this.y);
                    t3.norm();
                    t4 = new ctx.FP(0);
                    t4.copy(Q.x); 
                    t4.add(Q.y);
                    t4.norm();
                    t3.mul(t4);
                    t4.copy(t0);
                    t4.add(t1);

                    t3.sub(t4);
                    t3.norm();
                    t4.copy(this.y);
                    t4.add(this.z);
                    t4.norm();
                    x3 = new ctx.FP(0);
                    x3.copy(Q.y); 
                    x3.add(Q.z);
                    x3.norm();

                    t4.mul(x3);
                    x3.copy(t1);
                    x3.add(t2);

                    t4.sub(x3);
                    t4.norm();
                    x3.copy(this.x);
                    x3.add(this.z);
                    x3.norm();
                    y3 = new ctx.FP(0);
                    y3.copy(Q.x); 
                    y3.add(Q.z);
                    y3.norm();
                    x3.mul(y3);
                    y3.copy(t0);
                    y3.add(t2);
                    y3.rsub(x3);
                    y3.norm();
                    x3.copy(t0);
                    x3.add(t0);
                    t0.add(x3);
                    t0.norm();
                    t2.imul(b);

                    z3 = new ctx.FP(0);
                    z3.copy(t1); 
                    z3.add(t2);
                    z3.norm();
                    t1.sub(t2);
                    t1.norm();
                    y3.imul(b);

                    x3.copy(y3);
                    x3.mul(t4);
                    t2.copy(t3);
                    t2.mul(t1);
                    x3.rsub(t2);
                    y3.mul(t0);
                    t1.mul(z3);
                    y3.add(t1);
                    t0.mul(t3);
                    z3.mul(t4);
                    z3.add(t0);

                    this.x.copy(x3);
                    this.x.norm();
                    this.y.copy(y3);
                    this.y.norm();
                    this.z.copy(z3);
                    this.z.norm();
                } else {
                    t0 = new ctx.FP(0);
                    t0.copy(this.x); 
                    t1 = new ctx.FP(0);
                    t1.copy(this.y); 
                    t2 = new ctx.FP(0);
                    t2.copy(this.z); 
                    t3 = new ctx.FP(0);
                    t3.copy(this.x); 
                    t4 = new ctx.FP(0);
                    t4.copy(Q.x); 
                    z3 = new ctx.FP(0); 
                    y3 = new ctx.FP(0);
                    y3.copy(Q.x); 
                    x3 = new ctx.FP(0);
                    x3.copy(Q.y); 
                    b = new ctx.FP(0); 

                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        b.rcopy(ctx.ROM_CURVE.CURVE_B);
                    }
                    t0.mul(Q.x); //1
                    t1.mul(Q.y); //2
                    t2.mul(Q.z); //3

                    t3.add(this.y);
                    t3.norm(); //4
                    t4.add(Q.y);
                    t4.norm(); //5
                    t3.mul(t4); //6
                    t4.copy(t0);
                    t4.add(t1);  //7
                    t3.sub(t4);
                    t3.norm(); //8
                    t4.copy(this.y);
                    t4.add(this.z);
                    t4.norm(); //9
                    x3.add(Q.z);
                    x3.norm(); //10
                    t4.mul(x3); //11
                    x3.copy(t1);
                    x3.add(t2); //12

                    t4.sub(x3);
                    t4.norm(); //13
                    x3.copy(this.x);
                    x3.add(this.z);
                    x3.norm(); //14
                    y3.add(Q.z);
                    y3.norm(); //15

                    x3.mul(y3); //16
                    y3.copy(t0);
                    y3.add(t2); //17

                    y3.rsub(x3);
                    y3.norm(); //18
                    z3.copy(t2);

                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        z3.mul(b); //18
                    } else {
                        z3.imul(ctx.ROM_CURVE.CURVE_B_I);
                    }

                    x3.copy(y3);
                    x3.sub(z3);
                    x3.norm(); //20
                    z3.copy(x3);
                    z3.add(x3);  //21

                    x3.add(z3);  //22
                    z3.copy(t1);
                    z3.sub(x3);
                    z3.norm(); //23
                    x3.add(t1);
                    x3.norm(); //24

                    if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                        y3.mul(b); //18
                    } else {
                        y3.imul(ctx.ROM_CURVE.CURVE_B_I);
                    }

                    t1.copy(t2);
                    t1.add(t2); //26
                    t2.add(t1); //27

                    y3.sub(t2);  //28

                    y3.sub(t0);
                    y3.norm(); //29
                    t1.copy(y3);
                    t1.add(y3); //30
                    y3.add(t1);
                    y3.norm(); //31

                    t1.copy(t0);
                    t1.add(t0); //32
                    t0.add(t1); //33
                    t0.sub(t2);
                    t0.norm(); //34
                    t1.copy(t4);
                    t1.mul(y3); //35
                    t2.copy(t0);
                    t2.mul(y3); //36
                    y3.copy(x3);
                    y3.mul(z3); //37
                    y3.add(t2); //38
                    x3.mul(t3); //39
                    x3.sub(t1); //40
                    z3.mul(t4); //41
                    t1.copy(t3);
                    t1.mul(t0); //42
                    z3.add(t1); 
           
                    this.x.copy(x3);
                    this.x.norm();
                    this.y.copy(y3);
                    this.y.norm();
                    this.z.copy(z3);
                    this.z.norm();
                }
            }

            if (ECP.CURVETYPE == ECP.EDWARDS) {
                A = new ctx.FP(0);
                A.copy(this.z); 
                B = new ctx.FP(0); 
                C = new ctx.FP(0);
                C.copy(this.x); 
                D = new ctx.FP(0);
                D.copy(this.y); 
                E = new ctx.FP(0); 
                F = new ctx.FP(0); 
                G = new ctx.FP(0); 

                A.mul(Q.z); //A=2
                B.copy(A);
                B.sqr(); //B=2
                C.mul(Q.x); //C=2
                D.mul(Q.y); //D=2

                E.copy(C);
                E.mul(D); //E=2

                if (ctx.ROM_CURVE.CURVE_B_I == 0) {
                    b = new ctx.FP(0);
                    b.rcopy(ctx.ROM_CURVE.CURVE_B);
                    E.mul(b);
                } else {
                    E.imul(ctx.ROM_CURVE.CURVE_B_I); //E=22222
                }

                F.copy(B);
                F.sub(E); //F=22224
                G.copy(B);
                G.add(E); //G=22224

                if (ctx.ROM_CURVE.CURVE_A == 1) {
                    E.copy(D);
                    E.sub(C); //E=4
                }
                C.add(D); //C=4

                B.copy(this.x);
                B.add(this.y); //B=4
                D.copy(Q.x);
                D.add(Q.y);
                B.norm();
                D.norm(); //D=4
                B.mul(D); //B=2
                B.sub(C);
                B.norm();
                F.norm(); // B=6
                B.mul(F); //B=2
                this.x.copy(A);
                this.x.mul(B);
                G.norm(); // x=2

                if (ctx.ROM_CURVE.CURVE_A == 1) {
                    E.norm();
                    C.copy(E);
                    C.mul(G); //C=2
                }

                if (ctx.ROM_CURVE.CURVE_A == -1) {
                    C.norm();
                    C.mul(G);
                }

                this.y.copy(A);
                this.y.mul(C); //y=2
                this.z.copy(F);
                this.z.mul(G);
            }

            return;
        },

        /* Differential Add for Montgomery curves. this+=Q where W is this-Q and is affine. */
        dadd: function(Q, W) {
            var A, B, C, D, DA, CB;

            A = new ctx.FP(0);
            A.copy(this.x);
            B = new ctx.FP(0);
            B.copy(this.x);
            C = new ctx.FP(0);
            C.copy(Q.x);
            D = new ctx.FP(0);
            D.copy(Q.x);
            DA = new ctx.FP(0);
            CB = new ctx.FP(0);

            A.add(this.z);
            B.sub(this.z);

            C.add(Q.z);
            D.sub(Q.z);

            D.norm();
            A.norm();
            DA.copy(D);
            DA.mul(A);
            C.norm();
            B.norm();
            CB.copy(C);
            CB.mul(B);

            A.copy(DA);
            A.add(CB);
            A.norm();
            A.sqr();
            B.copy(DA);
            B.sub(CB);
            B.norm();
            B.sqr();

            this.x.copy(A);
            this.z.copy(W.x);
            this.z.mul(B);

            //  this.x.norm();
        },

	/**
         * Subtracts ECP instance Q  from this
         *
         * @this {ECP}
         * @param Q ECP instance
         */	
        sub: function(Q) {
			var NQ = new ECP(); NQ.copy(Q);
            NQ.neg();
            this.add(NQ);
        },

	/**
         * constant time multiply by small integer of length bts - use ladder 
         *
         * @this {ECP}
         * @param e small integer
         * @param bts e bit length
         */	
        pinmul: function(e, bts) {
            var i, b, P, R0, R1;

            if (ECP.CURVETYPE == ECP.MONTGOMERY) {
                return this.mul(new ctx.BIG(e));
            } else {
                P = new ECP();
                R0 = new ECP();
                R1 = new ECP();
                R1.copy(this);

                for (i = bts - 1; i >= 0; i--) {
                    b = (e >> i) & 1;
                    P.copy(R1);
                    P.add(R0);
                    R0.cswap(R1, b);
                    R1.copy(P);
                    R0.dbl();
                    R0.cswap(R1, b);
                }

                P.copy(R0);
                P.affine();

                return P;
            }
        },

	/**
         * multiply this by the curves cofactor
         *
         * @this {ECP}
         */	
        cfp: function() {
            var cf=ctx.ROM_CURVE.CURVE_Cof_I,
                c = new ctx.BIG(0);
            if (cf==1) {
                return;
            }
            if (cf==4) {
                this.dbl(); this.dbl();
                return;
            }
            if (cf==8) {
                this.dbl(); this.dbl(); this.dbl();
                return;
            }
            c.rcopy(ctx.ROM_CURVE.CURVE_Cof);
            this.copy(this.mul(c));
        },


	/**
         * Multiplies an ECP instance P by a BIG, side-channel resistant
         *
         * @this {ECP}
         * @param e BIG number multiplier
         */		
        mul: function(e) {
            var P, D, R0, R1, mt, t, Q, C, W, w,
                i, b, nb, s, ns;

            if (e.iszilch() || this.is_infinity()) {
                return new ECP();
            }

            P = new ECP();

            if (ECP.CURVETYPE == ECP.MONTGOMERY) { /* use ladder */
                D = new ECP();
                R0 = new ECP();
                R0.copy(this);
                R1 = new ECP();
                R1.copy(this);
                R1.dbl();
                D.copy(this);
                D.affine();
                nb = e.nbits();
                for (i = nb - 2; i >= 0; i--) {
                    b = e.bit(i);
                    P.copy(R1);
                    P.dadd(R0, D);

                    R0.cswap(R1, b);
                    R1.copy(P);
                    R0.dbl();
                    R0.cswap(R1, b);
                }
                P.copy(R0);
            } else {
                // fixed size windows
                mt = new ctx.BIG();
                t = new ctx.BIG();
                Q = new ECP();
                C = new ECP();
                W = [];
                w = [];

                // precompute table
                Q.copy(this);
                Q.dbl();
                W[0] = new ECP();
                W[0].copy(this);

                for (i = 1; i < 8; i++) {
                    W[i] = new ECP();
                    W[i].copy(W[i - 1]);
                    W[i].add(Q);
                }

                // make exponent odd - add 2P if even, P if odd
                t.copy(e);
                s = t.parity();
                t.inc(1);
                t.norm();
                ns = t.parity();
                mt.copy(t);
                mt.inc(1);
                mt.norm();
                t.cmove(mt, s);
                Q.cmove(this, ns);
                C.copy(Q);

                nb = 1 + Math.floor((t.nbits() + 3) / 4);

                // convert exponent to signed 4-bit window
                for (i = 0; i < nb; i++) {
                    w[i] = (t.lastbits(5) - 16);
                    t.dec(w[i]);
                    t.norm();
                    t.fshr(4);
                }
                w[nb] = t.lastbits(5);

                P.copy(W[Math.floor((w[nb] - 1) / 2)]);
                for (i = nb - 1; i >= 0; i--) {
                    Q.select(W, w[i]);
                    P.dbl();
                    P.dbl();
                    P.dbl();
                    P.dbl();
                    P.add(Q);
                }
                P.sub(C);
            }

            P.affine();

            return P;
        },

	/**
         * Return e.this+f.Q 
         *
         * @this {ECP}
         * @param e BIG number multiplier
         * @param Q ECP instance
         * @param f BIG number multiplier
         */		
        mul2: function(e, Q, f) {
            var te = new ctx.BIG(),
                tf = new ctx.BIG(),
                mt = new ctx.BIG(),
                S = new ECP(),
                T = new ECP(),
                C = new ECP(),
                W = [],
                w = [],
                i, s, ns, nb,
                a, b;

            te.copy(e);
            tf.copy(f);

            // precompute table
            W[1] = new ECP();
            W[1].copy(this);
            W[1].sub(Q);
            W[2] = new ECP();
            W[2].copy(this);
            W[2].add(Q);
            S.copy(Q);
            S.dbl();
            W[0] = new ECP();
            W[0].copy(W[1]);
            W[0].sub(S);
            W[3] = new ECP();
            W[3].copy(W[2]);
            W[3].add(S);
            T.copy(this);
            T.dbl();
            W[5] = new ECP();
            W[5].copy(W[1]);
            W[5].add(T);
            W[6] = new ECP();
            W[6].copy(W[2]);
            W[6].add(T);
            W[4] = new ECP();
            W[4].copy(W[5]);
            W[4].sub(S);
            W[7] = new ECP();
            W[7].copy(W[6]);
            W[7].add(S);

            // if multiplier is odd, add 2, else add 1 to multiplier, and add 2P or P to correction

            s = te.parity();
            te.inc(1);
            te.norm();
            ns = te.parity();
            mt.copy(te);
            mt.inc(1);
            mt.norm();
            te.cmove(mt, s);
            T.cmove(this, ns);
            C.copy(T);

            s = tf.parity();
            tf.inc(1);
            tf.norm();
            ns = tf.parity();
            mt.copy(tf);
            mt.inc(1);
            mt.norm();
            tf.cmove(mt, s);
            S.cmove(Q, ns);
            C.add(S);

            mt.copy(te);
            mt.add(tf);
            mt.norm();
            nb = 1 + Math.floor((mt.nbits() + 1) / 2);

            // convert exponent to signed 2-bit window
            for (i = 0; i < nb; i++) {
                a = (te.lastbits(3) - 4);
                te.dec(a);
                te.norm();
                te.fshr(2);
                b = (tf.lastbits(3) - 4);
                tf.dec(b);
                tf.norm();
                tf.fshr(2);
                w[i] = (4 * a + b);
            }
            w[nb] = (4 * te.lastbits(3) + tf.lastbits(3));
            S.copy(W[Math.floor((w[nb] - 1) / 2)]);

            for (i = nb - 1; i >= 0; i--) {
                T.select(W, w[i]);
                S.dbl();
                S.dbl();
                S.add(T);
            }
            S.sub(C); /* apply correction */
            S.affine();

            return S;
        }
    };

    /**
      * Set group generator
      *
      * @this {ECP}
      */	
    ECP.generator = function() {
        var G=new ECP(),
            gx = new ctx.BIG(0),
            gy = new ctx.BIG(0);

        gx.rcopy(ctx.ROM_CURVE.CURVE_Gx);

        if (ctx.ECP.CURVETYPE != ctx.ECP.MONTGOMERY) {
            gy.rcopy(ctx.ROM_CURVE.CURVE_Gy);
            G.setxy(gx, gy);
        } else {
            G.setx(gx);
        }
        return G;
    };

    /* return 1 if b==c, no branching */
    ECP.teq = function(b, c) {
        var x = b ^ c;
        x -= 1; // if x=0, x now -1
        return ((x >> 31) & 1);
    };

    /**
      * convert from byte array to point 
      *
      * @this {ECP}
      * @param b input byte array
      */	    
    ECP.fromBytes = function(b) {
        var t = [],
            P = new ECP(),
            p = new ctx.BIG(0),
            px, py, i;

        p.rcopy(ctx.ROM_FIELD.Modulus);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 1];
        }

        px = ctx.BIG.fromBytes(t);
        if (ctx.BIG.comp(px, p) >= 0) {
            return P;
        }

        if (ECP.CURVETYPE == ECP.MONTGOMERY) {
            P.setx(px);
            return P;
        }

        if (b[0] == 0x04) {
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                t[i] = b[i + ctx.BIG.MODBYTES + 1];
            }

            py = ctx.BIG.fromBytes(t);

            if (ctx.BIG.comp(py, p) >= 0) {
                return P;
            }

            P.setxy(px, py);

            return P;
        }

        if (b[0]==0x02 || b[0]==0x03) {
            P.setxi(px,b[0]&1);
            return P;
        }

        return P;
    };

    /**
      * Calculate RHS of the curve equation 
      *
      * @this {ECP}
      * @param x x-value
      */	    
    ECP.RHS = function(x) {
        var r = new ctx.FP(0),
            b, cx, one, x3;

        //x.norm();
        r.copy(x);
        r.sqr();

        if (ECP.CURVETYPE == ECP.WEIERSTRASS) { // x^3+Ax+B
            b = new ctx.FP(0);
            b.rcopy(ctx.ROM_CURVE.CURVE_B);
            r.mul(x);
            if (ctx.ROM_CURVE.CURVE_A == -3) {
                cx = new ctx.FP(0);
                cx.copy(x);
                cx.imul(3);
                cx.neg();
                cx.norm();
                r.add(cx);
            }
            r.add(b);
        } else if (ECP.CURVETYPE == ECP.EDWARDS) { // (Ax^2-1)/(Bx^2-1)
            b = new ctx.FP(0);
            b.rcopy(ctx.ROM_CURVE.CURVE_B);

            one = new ctx.FP(1);
            b.mul(r);
            b.sub(one);
            b.norm();
            if (ctx.ROM_CURVE.CURVE_A == -1) {
                r.neg();
            }
            r.sub(one);
            r.norm();
            b.inverse();

            r.mul(b);
        } else if (ECP.CURVETYPE == ECP.MONTGOMERY) { // x^3+Ax^2+x
            x3 = new ctx.FP(0);
            x3.copy(r);
            x3.mul(x);
            r.imul(ctx.ROM_CURVE.CURVE_A);
            r.add(x3);
            r.add(x);
        }

        r.reduce();

        return r;
    };

    ECP.mapit = function(h) {
        var q = new ctx.BIG(0),
            x = ctx.BIG.fromBytes(h),
            P = new ECP();

        q.rcopy(ctx.ROM_FIELD.Modulus);
        x.mod(q);

        for (;;) {
            for (;;) {
                if (ECP.CURVETYPE != ECP.MONTGOMERY) {
                    P.setxi(x,0);
                } else {
                    P.setx(x);
                }
                x.inc(1); x.norm();
                if (!P.is_infinity()){
                    break;
                }

            }
            P.cfp();
            if (!P.is_infinity()) {
                break;
            }
        }
        return P;
    };

    return ECP;
};