var ECP8 = function()

in src/ecp8.js [22:1178]


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

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

    ECP8.prototype = {

	/**
         * Tests for ECP8 point equal to infinity
         *
         * @this {ECP8}
         * @param 1 if infinity, else returns 0
         */	
        is_infinity: function() {

            this.x.reduce();
            this.y.reduce();
            this.z.reduce();
            return (this.x.iszilch() && this.z.iszilch());
        },

	/**
         * Copy ECP8 point to another ECP8 point
         *
         * @this {ECP8}
         * @param P ECP8 instance
         */	
        copy: function(P) {
            this.x.copy(P.x);
            this.y.copy(P.y);
            this.z.copy(P.z);
        },

	/**
         * Set ECP8 to point-at-infinity
         *
         * @this {ECP8}
         */	
        inf: function() {
            this.x.zero();
            this.y.one();
            this.z.zero();
        },

	/**
         * conditional move of Q to P dependant on d 
         *
         * @this {ECP8}
         */	
        cmove: function(Q, d) {
            this.x.cmove(Q.x, d);
            this.y.cmove(Q.y, d);
            this.z.cmove(Q.z, d);
        },

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

            babs = (babs - 1) / 2;

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

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

	/**
         * Test P == Q 
         *
         * @this {ECP8}
         * @param Q ECP8 instance 
         */	
        equals: function(Q) {
            var a, b;

            a = new ctx.FP8(this.x);
            b = new ctx.FP8(Q.x);

            a.mul(Q.z);
            b.mul(this.z);
            if (!a.equals(b)) {
                return false;
            }

            a.copy(this.y);
            a.mul(Q.z);
            b.copy(Q.y);
            b.mul(this.z);
            if (!a.equals(b)) {
                return false;
            }

            return true;
        },

	/**
         * set this=-this 
         *
         * @this {ECP8}
         */	
        neg: function() {
            this.y.norm();
            this.y.neg();
            this.y.norm();
            return;
        },

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

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

            one = new ctx.FP8(1);

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

            this.z.inverse();
            this.x.mul(this.z);
            this.x.reduce();
            this.y.mul(this.z);
            this.y.reduce();
            this.z.copy(one);
        },

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

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

	/**
         * extract projective x
         *
         * @this {ECP8}
         */	
        getx: function() {
            return this.x;
        },

	/**
         * extract projective y
         *
         * @this {ECP8}
         */	
        gety: function() {
            return this.y;
        },

	/**
         * extract projective z
         *
         * @this {ECP8}
         */	
        getz: function() {
            return this.z;
        },

	/**
         * convert this to byte arrayextract projective x
         *
         * @this {ECP8}
         * @param b byte array output
         */	
        toBytes: function(b) {
            var t = [],
                i;
			var W=new ECP8(); W.copy(this);
            W.affine();
            W.x.geta().geta().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i] = t[i];
            }
            W.x.geta().geta().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + ctx.BIG.MODBYTES] = t[i];
            }
            W.x.geta().getb().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 2*ctx.BIG.MODBYTES] = t[i];
            }
            W.x.geta().getb().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 3*ctx.BIG.MODBYTES] = t[i];
            }

            W.x.getb().geta().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 4*ctx.BIG.MODBYTES] = t[i];
            }
            W.x.getb().geta().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 5*ctx.BIG.MODBYTES] = t[i];
            }
            W.x.getb().getb().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 6*ctx.BIG.MODBYTES] = t[i];
            }
            W.x.getb().getb().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 7*ctx.BIG.MODBYTES] = t[i];
            }

            W.y.geta().geta().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 8 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.geta().geta().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 9 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.geta().getb().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 10 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.geta().getb().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 11 * ctx.BIG.MODBYTES] = t[i];
            }

            W.y.getb().geta().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 12 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.getb().geta().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 13 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.getb().getb().getA().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 14 * ctx.BIG.MODBYTES] = t[i];
            }
            W.y.getb().getb().getB().toBytes(t);
            for (i = 0; i < ctx.BIG.MODBYTES; i++) {
                b[i + 15 * ctx.BIG.MODBYTES] = t[i];
            }
        },

	/**
         * convert this to hex string 
         *
         * @this {ECP8}
         * @return hex string
         */	
        toString: function() {
			var W=new ECP8(); W.copy(this);
            if (W.is_infinity()) {
                return "infinity";
            }
            W.affine();
            return "(" + W.x.toString() + "," + W.y.toString() + ")";
        },

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

            this.x.copy(ix);
            this.y.copy(iy);
            this.z.one();
			this.x.norm();

            rhs = ECP8.RHS(this.x);

            y2 = new ctx.FP8(this.y); //y2.copy(this.y);
            y2.sqr();

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

	/**
         * set this=(x,.) 
         *
         * @this {ECP8}
         * @param ix x-value
         */	
        setx: function(ix) {
            var rhs;

            this.x.copy(ix);
            this.z.one();
			this.x.norm();
            rhs = ECP8.RHS(this.x);

            if (rhs.sqrt()) {
                this.y.copy(rhs);
            } else {
                this.inf();
            }
        },

	/**
         * set this*=q, where q is Modulus, using Frobenius 
         *
         * @this {ECP8}
         */	
        frob: function(F,n) {
            for (var i=0;i<n;i++) {
                this.x.frob(F[2]);
                this.x.qmul(F[0]);
                if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
                    this.x.div_i2();
                }
                if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                    this.x.times_i2();
                }

                this.y.frob(F[2]);
                this.y.qmul(F[1]);

                if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
                    this.y.div_i();
                }
                if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                    this.y.times_i2(); this.y.times_i2(); this.y.times_i();
                }
                this.z.frob(F[2]);
            }
        },

	/**
         * this+=this 
         *
         * @this {ECP8}
         */	
        dbl: function() {
            var iy, t0, t1, t2, x3, y3;

            iy = new ctx.FP8(this.y);
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                iy.times_i();
                iy.norm();
            }

            t0 = new ctx.FP8(this.y);
            t0.sqr();
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                t0.times_i();
            }
            t1 = new ctx.FP8(iy);
            t1.mul(this.z);
            t2 = new ctx.FP8(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);
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
                t2.times_i();
            }

            x3 = new ctx.FP8(t2);
            x3.mul(this.z);

            y3 = new ctx.FP8(t0);

            y3.add(t2);
            y3.norm();
            this.z.mul(t1);
            t1.copy(t2);
            t1.add(t2);
            t2.add(t1);
            t2.norm();
            t0.sub(t2);
            t0.norm(); //y^2-9bz^2
            y3.mul(t0);
            y3.add(x3); //(y^2+3z*2)(y^2-9z^2)+3b.z^2.8y^2
            t1.copy(this.x);
            t1.mul(iy); //
            this.x.copy(t0);
            this.x.norm();
            this.x.mul(t1);
            this.x.add(this.x); //(y^2-9bz^2)xy2

            this.x.norm();
            this.y.copy(y3);
            this.y.norm();

            return 1;
        },

	/**
         * Adds ECP8 instances
         *
         * param Q ECP8 instance
         * @this {ECP8}
         */		
        add: function(Q) {
            var b, t0, t1, t2, t3, t4, x3, y3, z3;

            b = 3 * ctx.ROM_CURVE.CURVE_B_I;
            t0 = new ctx.FP8(this.x);
            t0.mul(Q.x); // x.Q.x
            t1 = new ctx.FP8(this.y);
            t1.mul(Q.y); // y.Q.y

            t2 = new ctx.FP8(this.z);
            t2.mul(Q.z);
            t3 = new ctx.FP8(this.x);
            t3.add(this.y);
            t3.norm(); //t3=X1+Y1
            t4 = new ctx.FP8(Q.x);
            t4.add(Q.y);
            t4.norm(); //t4=X2+Y2
            t3.mul(t4); //t3=(X1+Y1)(X2+Y2)
            t4.copy(t0);
            t4.add(t1); //t4=X1.X2+Y1.Y2

            t3.sub(t4);
            t3.norm();
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                t3.times_i();  //t3=(X1+Y1)(X2+Y2)-(X1.X2+Y1.Y2) = X1.Y2+X2.Y1
            }

            t4.copy(this.y);
            t4.add(this.z);
            t4.norm(); //t4=Y1+Z1
            x3 = new ctx.FP8(Q.y);
            x3.add(Q.z);
            x3.norm(); //x3=Y2+Z2

            t4.mul(x3); //t4=(Y1+Z1)(Y2+Z2)
            x3.copy(t1); //
            x3.add(t2); //X3=Y1.Y2+Z1.Z2

            t4.sub(x3);
            t4.norm();
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                t4.times_i();  //t4=(Y1+Z1)(Y2+Z2) - (Y1.Y2+Z1.Z2) = Y1.Z2+Y2.Z1
            }

            x3.copy(this.x);
            x3.add(this.z);
            x3.norm(); // x3=X1+Z1
            y3 = new ctx.FP8(Q.x);
            y3.add(Q.z);
            y3.norm(); // y3=X2+Z2
            x3.mul(y3); // x3=(X1+Z1)(X2+Z2)
            y3.copy(t0);
            y3.add(t2); // y3=X1.X2+Z1+Z2
            y3.rsub(x3);
            y3.norm(); // y3=(X1+Z1)(X2+Z2) - (X1.X2+Z1.Z2) = X1.Z2+X2.Z1

            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
                t0.times_i();
                t1.times_i();
            }

            x3.copy(t0);
            x3.add(t0);
            t0.add(x3);
            t0.norm();
            t2.imul(b);
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
                t2.times_i();
            }

            z3 = new ctx.FP8(t1);
            z3.add(t2);
            z3.norm();
            t1.sub(t2);
            t1.norm();
            y3.imul(b);
            if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
                y3.times_i();
            }

            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();

            return 0;
        },

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

	/**
         * Multiplies an ECP8 instance P by a BIG, side-channel resistant
         *
         * @this {ECP8}
         * @param e BIG number multiplier
         */		
        mul: function(e) {
            /* fixed size windows */
            var mt = new ctx.BIG(),
                t = new ctx.BIG(),
                C = new ECP8(),
                P = new ECP8(),
                Q = new ECP8(),
                W = [],
                w = [],
                i, nb, s, ns;

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

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

            for (i = 1; i < 8; i++) {
                W[i] = new ECP8();
                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;
        }
    };

    /**
      * Set group generator
      *
      * @this {ECP8}
      */	
    ECP8.generator = function() {
        var G=new ECP8(),
            A = new ctx.BIG(0),
            B = new ctx.BIG(0),
            XAA, XAB, XA, XBA, XBB, XB, X,
            YAA, YAB, YA, YBA, YBB, YB, Y;

        A.rcopy(ctx.ROM_CURVE.CURVE_Pxaaa);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pxaab);
        XAA= new ctx.FP2(A,B);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pxaba);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pxabb);

        XAB= new ctx.FP2(A,B);
        XA=new ctx.FP4(XAA,XAB);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pxbaa);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pxbab);
        XBA= new ctx.FP2(A,B);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pxbba);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pxbbb);

        XBB= new ctx.FP2(A,B);
        XB=new ctx.FP4(XBA,XBB);

        X=new ctx.FP8(XA,XB);


        A.rcopy(ctx.ROM_CURVE.CURVE_Pyaaa);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pyaab);
        YAA= new ctx.FP2(A,B);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pyaba);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pyabb);

        YAB= new ctx.FP2(A,B);
        YA=new ctx.FP4(YAA,YAB);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pybaa);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pybab);
        YBA= new ctx.FP2(A,B);

        A.rcopy(ctx.ROM_CURVE.CURVE_Pybba);
        B.rcopy(ctx.ROM_CURVE.CURVE_Pybbb);

        YBB= new ctx.FP2(A,B);
        YB=new ctx.FP4(YBA,YBB);

        Y=new ctx.FP8(YA,YB);

        G.setxy(X,Y);

        return G;
    };

    /**
      * convert from byte array to point 
      *
      * @this {ECP8}
      * @param b input byte array
      */	    
    ECP8.fromBytes = function(b) {
        var t = [],
            ra, rb, ra4, rb4, ra8, rb8, i, rx, ry, P;

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        ra4=new ctx.FP2(ra,rb);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i  + 2*ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 3*ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        rb4=new ctx.FP2(ra,rb);

        ra8=new ctx.FP4(ra4,rb4);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 4*ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 5*ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        ra4=new ctx.FP2(ra,rb);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i  + 6*ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 7*ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        rb4=new ctx.FP2(ra,rb);

        rb8=new ctx.FP4(ra4,rb4);

        rx = new ctx.FP8(ra8, rb8); //rx.bset(ra,rb);


        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 8 * ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 9 * ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        ra4=new ctx.FP2(ra,rb);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 10 * ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 11 * ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        rb4=new ctx.FP2(ra,rb);

        ra8=new ctx.FP4(ra4,rb4);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 12 * ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 13 * ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        ra4=new ctx.FP2(ra,rb);

        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 14 * ctx.BIG.MODBYTES];
        }
        ra = ctx.BIG.fromBytes(t);
        for (i = 0; i < ctx.BIG.MODBYTES; i++) {
            t[i] = b[i + 15 * ctx.BIG.MODBYTES];
        }
        rb = ctx.BIG.fromBytes(t);
        rb4=new ctx.FP2(ra,rb);

        rb8=new ctx.FP4(ra4,rb4);

        ry = new ctx.FP8(ra8, rb8); //ry.bset(ra,rb);

        P = new ECP8();
        P.setxy(rx, ry);

        return P;
    };

    /**
      * Calculate RHS of curve equation x^3+B
      *
      * @this {ECP8}
      * @param x x-value
      */	    
    ECP8.RHS = function(x) {
        var r, c, b;

        //x.norm();
        r = new ctx.FP8(x); //r.copy(x);
        r.sqr();

        c = new ctx.BIG(0);
        c.rcopy(ctx.ROM_CURVE.CURVE_B);
        b = new ctx.FP8(c); //b.bseta(c);

        if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.D_TYPE) {
            b.div_i();
        }
        if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
            b.times_i();
        }

        r.mul(x);
        r.add(b);

        r.reduce();
        return r;
    };

    /* P=u0.Q0+u1*Q1+u2*Q2+u3*Q3... */
    // Bos & Costello https://eprint.iacr.org/2013/458.pdf
    // Faz-Hernandez & Longa & Sanchez  https://eprint.iacr.org/2013/158.pdf
    // Side channel attack secure

    /**
      * Calculate P=u0.Q0+u1*Q1+u2*Q2+u3*Q3... 
      *
      * @this {ECP8}
      */     
    ECP8.mul16 = function(Q, u) {
        var W = new ECP8(),
            P = new ECP8(),
            T1 = [],
            T2 = [],
            T3 = [],
            T4 = [],
            mt = new ctx.BIG(),
            t = [],
            w1 = [],
            s1 = [],
            w2 = [],
            s2 = [],
            w3 = [],
            s3 = [],
            w4 = [],
            s4 = [],
            F=ECP8.frob_constants(),
            i, j, k, nb, bt, pb1, pb2, pb3, pb4;

        for (i = 0; i < 16; i++) {
            t[i] = new ctx.BIG(u[i]); t[i].norm();
            //Q[i].affine();
        }

        T1[0] = new ECP8(); T1[0].copy(Q[0]); // Q[0]
        T1[1] = new ECP8(); T1[1].copy(T1[0]); T1[1].add(Q[1]); // Q[0]+Q[1]
        T1[2] = new ECP8(); T1[2].copy(T1[0]); T1[2].add(Q[2]); // Q[0]+Q[2]
        T1[3] = new ECP8(); T1[3].copy(T1[1]); T1[3].add(Q[2]); // Q[0]+Q[1]+Q[2]
        T1[4] = new ECP8(); T1[4].copy(T1[0]); T1[4].add(Q[3]); // Q[0]+Q[3]
        T1[5] = new ECP8(); T1[5].copy(T1[1]); T1[5].add(Q[3]); // Q[0]+Q[1]+Q[3]
        T1[6] = new ECP8(); T1[6].copy(T1[2]); T1[6].add(Q[3]); // Q[0]+Q[2]+Q[3]
        T1[7] = new ECP8(); T1[7].copy(T1[3]); T1[7].add(Q[3]); // Q[0]+Q[1]+Q[2]+Q[3]

        //  Use Frobenius
        for (i=0;i<8;i++) {
            T2[i] = new ECP8(); T2[i].copy(T1[i]);
            T2[i].frob(F,4);
            T3[i] = new ECP8(); T3[i].copy(T2[i]);
            T3[i].frob(F,4);
            T4[i] = new ECP8(); T4[i].copy(T3[i]);
            T4[i].frob(F,4);
        }

        // Make it odd
        pb1=1-t[0].parity();
        t[0].inc(pb1);
        t[0].norm();

        pb2=1-t[4].parity();
        t[4].inc(pb2);
        t[4].norm();

        pb3=1-t[8].parity();
        t[8].inc(pb3);
        t[8].norm();

        pb4=1-t[12].parity();
        t[12].inc(pb4);
        t[12].norm();

        // Number of bits
        mt.zero();
        for (i=0;i<16;i++) {
            mt.or(t[i]);
        }

        nb=1+mt.nbits();

        // Sign pivot
        s1[nb-1]=1;
        s2[nb-1]=1;
        s3[nb-1]=1;
        s4[nb-1]=1;
        for (i=0;i<nb-1;i++) {
            t[0].fshr(1);
            s1[i]=2*t[0].parity()-1;
            t[4].fshr(1);
            s2[i]=2*t[4].parity()-1;

            t[8].fshr(1);
            s3[i]=2*t[8].parity()-1;
            t[12].fshr(1);
            s4[i]=2*t[12].parity()-1;
        }

        // Recoded exponent
        for (i=0; i<nb; i++) {
            w1[i]=0;
            k=1;
            for (j=1; j<4; j++) {
                bt=s1[i]*t[j].parity();
                t[j].fshr(1);
                t[j].dec(bt>>1);
                t[j].norm();
                w1[i]+=bt*k;
                k*=2;
            }
            w2[i]=0;
            k=1;
            for (j=5; j<8; j++) {
                bt=s2[i]*t[j].parity();
                t[j].fshr(1);
                t[j].dec(bt>>1);
                t[j].norm();
                w2[i]+=bt*k;
                k*=2;
            }

            w3[i]=0;
            k=1;
            for (j=9; j<12; j++) {
                bt=s3[i]*t[j].parity();
                t[j].fshr(1);
                t[j].dec(bt>>1);
                t[j].norm();
                w3[i]+=bt*k;
                k*=2;
            }
            w4[i]=0;
            k=1;
            for (j=13; j<16; j++) {
                bt=s4[i]*t[j].parity();
                t[j].fshr(1);
                t[j].dec(bt>>1);
                t[j].norm();
                w4[i]+=bt*k;
                k*=2;
            }
        }

        // Main loop
        P.select(T1,2*w1[nb-1]+1);
        W.select(T2,2*w2[nb-1]+1);
        P.add(W);
        W.select(T3,2*w3[nb-1]+1);
        P.add(W);
        W.select(T4,2*w4[nb-1]+1);
        P.add(W);
        for (i=nb-2;i>=0;i--) {
            P.dbl();
            W.select(T1,2*w1[i]+s1[i]);
            P.add(W);
            W.select(T2,2*w2[i]+s2[i]);
            P.add(W);
            W.select(T3,2*w3[i]+s3[i]);
            P.add(W);
            W.select(T4,2*w4[i]+s4[i]);
            P.add(W);
        }

        // apply correction
        W.copy(P);
        W.sub(Q[0]);
        P.cmove(W,pb1);

        W.copy(P);
        W.sub(Q[4]);
        P.cmove(W,pb2);

        W.copy(P);
        W.sub(Q[8]);
        P.cmove(W,pb3);

        W.copy(P);
        W.sub(Q[12]);
        P.cmove(W,pb4);

        P.affine();
        return P;
    };

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

    /* needed for SOK */
    ECP8.mapit = function(h) {
        var F=ECP8.frob_constants(),
            q, x, one, Q, X, X2, X4,
            xQ, x2Q, x3Q, x4Q, x5Q, x6Q, x7Q, x8Q;

        q = new ctx.BIG(0);
        q.rcopy(ctx.ROM_FIELD.Modulus);
        x = ctx.BIG.fromBytes(h);
        one = new ctx.BIG(1);
        x.mod(q);

        for (;;) {
            X2 = new ctx.FP2(one, x);
            X4 = new ctx.FP4(X2);
            X = new ctx.FP8(X4);
            Q = new ECP8();
            Q.setx(X);
            if (!Q.is_infinity()) {
                break;
            }
            x.inc(1);
            x.norm();
        }

        /* Fast Hashing to G2 - Fuentes-Castaneda, Knapp and Rodriguez-Henriquez */
        x = new ctx.BIG(0);
        x.rcopy(ctx.ROM_CURVE.CURVE_Bnx);


        xQ = Q.mul(x);
        x2Q = xQ.mul(x);
        x3Q = x2Q.mul(x);
        x4Q = x3Q.mul(x);
        x5Q = x4Q.mul(x);
        x6Q = x5Q.mul(x);
        x7Q = x6Q.mul(x);
        x8Q = x7Q.mul(x);

        if (ctx.ECP.SIGN_OF_X == ctx.ECP.NEGATIVEX) {
            xQ.neg();
            x3Q.neg();
            x5Q.neg();
            x7Q.neg();
        }

        x8Q.sub(x7Q);
        x8Q.sub(Q);

        x7Q.sub(x6Q);
        x7Q.frob(F,1);

        x6Q.sub(x5Q);
        x6Q.frob(F,2);

        x5Q.sub(x4Q);
        x5Q.frob(F,3);

        x4Q.sub(x3Q);
        x4Q.frob(F,4);

        x3Q.sub(x2Q);
        x3Q.frob(F,5);

        x2Q.sub(xQ);
        x2Q.frob(F,6);

        xQ.sub(Q);
        xQ.frob(F,7);

        Q.dbl();
        Q.frob(F,8);

        Q.add(x8Q);
        Q.add(x7Q);
        Q.add(x6Q);
        Q.add(x5Q);

        Q.add(x4Q);
        Q.add(x3Q);
        Q.add(x2Q);
        Q.add(xQ);

        Q.affine();
        return Q;
    };

    ECP8.frob_constants = function() {
        var fa = new ctx.BIG(0),
            fb = new ctx.BIG(0),
            F=[],
            X, F0, F1, F2;

        fa.rcopy(ctx.ROM_FIELD.Fra);
        fb.rcopy(ctx.ROM_FIELD.Frb);
        X = new ctx.FP2(fa, fb);

        F0=new ctx.FP2(X); F0.sqr();
        F2=new ctx.FP2(F0);
        F2.mul_ip(); F2.norm();
        F1=new ctx.FP2(F2); F1.sqr();
        F2.mul(F1);

        F2.mul_ip(); F2.norm();

        F1.copy(X);
        if (ctx.ECP.SEXTIC_TWIST == ctx.ECP.M_TYPE) {
            F1.mul_ip();
            F1.inverse();
            F0.copy(F1); F0.sqr();
        }
        F0.mul_ip(); F0.norm();
        F1.mul(F0);

        F[0]=new ctx.FP2(F0); F[1]=new ctx.FP2(F1); F[2]=new ctx.FP2(F2);
        return F;
    };

    return ECP8;
};