// 
// Decompiled by Procyon v0.6.0
// 

package org.bouncycastle.math.ec.rfc8032;

import org.bouncycastle.math.ec.rfc7748.X448;
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.SHAKEDigest;
import org.bouncycastle.crypto.Xof;
import org.bouncycastle.math.ec.rfc7748.X448Field;
import org.bouncycastle.math.raw.Nat;

public abstract class Ed448
{
    private static final int COORD_INTS = 14;
    private static final int POINT_BYTES = 57;
    private static final int SCALAR_INTS = 14;
    private static final int SCALAR_BYTES = 57;
    public static final int PREHASH_SIZE = 64;
    public static final int PUBLIC_KEY_SIZE = 57;
    public static final int SECRET_KEY_SIZE = 57;
    public static final int SIGNATURE_SIZE = 114;
    private static final byte[] DOM4_PREFIX;
    private static final int[] P;
    private static final int[] B_x;
    private static final int[] B_y;
    private static final int[] B225_x;
    private static final int[] B225_y;
    private static final int C_d = 39081;
    private static final int WNAF_WIDTH_225 = 5;
    private static final int WNAF_WIDTH_BASE = 7;
    private static final int PRECOMP_BLOCKS = 5;
    private static final int PRECOMP_TEETH = 5;
    private static final int PRECOMP_SPACING = 18;
    private static final int PRECOMP_RANGE = 450;
    private static final int PRECOMP_POINTS = 16;
    private static final int PRECOMP_MASK = 15;
    private static final Object PRECOMP_LOCK;
    private static PointAffine[] PRECOMP_BASE_WNAF;
    private static PointAffine[] PRECOMP_BASE225_WNAF;
    private static int[] PRECOMP_BASE_COMB;
    
    private static byte[] calculateS(final byte[] array, final byte[] array2, final byte[] array3) {
        final int[] array4 = new int[28];
        Scalar448.decode(array, array4);
        final int[] array5 = new int[14];
        Scalar448.decode(array2, array5);
        final int[] array6 = new int[14];
        Scalar448.decode(array3, array6);
        Nat.mulAddTo(14, array5, array6, array4);
        final byte[] array7 = new byte[114];
        Codec.encode32(array4, 0, array4.length, array7, 0);
        return Scalar448.reduce912(array7);
    }
    
    private static boolean checkContextVar(final byte[] array) {
        return array != null && array.length < 256;
    }
    
    private static int checkPoint(final PointAffine pointAffine) {
        final int[] create = X448Field.create();
        final int[] create2 = X448Field.create();
        final int[] create3 = X448Field.create();
        X448Field.sqr(pointAffine.x, create2);
        X448Field.sqr(pointAffine.y, create3);
        X448Field.mul(create2, create3, create);
        X448Field.add(create2, create3, create2);
        X448Field.mul(create, 39081, create);
        X448Field.subOne(create);
        X448Field.add(create, create2, create);
        X448Field.normalize(create);
        X448Field.normalize(create3);
        return X448Field.isZero(create) & ~X448Field.isZero(create3);
    }
    
    private static int checkPoint(final PointProjective pointProjective) {
        final int[] create = X448Field.create();
        final int[] create2 = X448Field.create();
        final int[] create3 = X448Field.create();
        final int[] create4 = X448Field.create();
        X448Field.sqr(pointProjective.x, create2);
        X448Field.sqr(pointProjective.y, create3);
        X448Field.sqr(pointProjective.z, create4);
        X448Field.mul(create2, create3, create);
        X448Field.add(create2, create3, create2);
        X448Field.mul(create2, create4, create2);
        X448Field.sqr(create4, create4);
        X448Field.mul(create, 39081, create);
        X448Field.sub(create, create4, create);
        X448Field.add(create, create2, create);
        X448Field.normalize(create);
        X448Field.normalize(create3);
        X448Field.normalize(create4);
        return X448Field.isZero(create) & ~X448Field.isZero(create3) & ~X448Field.isZero(create4);
    }
    
    private static boolean checkPointFullVar(final byte[] array) {
        if ((array[56] & 0x7F) != 0x0) {
            return false;
        }
        int decode32;
        int n = (decode32 = Codec.decode32(array, 52)) ^ Ed448.P[13];
        for (int i = 12; i > 0; --i) {
            final int decode33 = Codec.decode32(array, i * 4);
            if (n == 0 && decode33 + Integer.MIN_VALUE > Ed448.P[i] + Integer.MIN_VALUE) {
                return false;
            }
            decode32 |= decode33;
            n |= (decode33 ^ Ed448.P[i]);
        }
        final int decode34 = Codec.decode32(array, 0);
        return (decode32 != 0 || decode34 + Integer.MIN_VALUE > -2147483647) && (n != 0 || decode34 + Integer.MIN_VALUE < Ed448.P[0] - 1 + Integer.MIN_VALUE);
    }
    
    private static boolean checkPointOrderVar(final PointAffine pointAffine) {
        final PointProjective pointProjective = new PointProjective();
        scalarMultOrderVar(pointAffine, pointProjective);
        return normalizeToNeutralElementVar(pointProjective);
    }
    
    private static boolean checkPointVar(final byte[] array) {
        if ((array[56] & 0x7F) != 0x0) {
            return false;
        }
        if (Codec.decode32(array, 52) != Ed448.P[13]) {
            return true;
        }
        final int[] array2 = new int[14];
        Codec.decode32(array, 0, array2, 0, 14);
        return !Nat.gte(14, array2, Ed448.P);
    }
    
    private static byte[] copy(final byte[] array, final int n, final int n2) {
        final byte[] array2 = new byte[n2];
        System.arraycopy(array, n, array2, 0, n2);
        return array2;
    }
    
    public static Xof createPrehash() {
        return createXof();
    }
    
    private static Xof createXof() {
        return new SHAKEDigest(256);
    }
    
    private static boolean decodePointVar(final byte[] array, final boolean b, final PointAffine pointAffine) {
        final int n = (array[56] & 0x80) >>> 7;
        X448Field.decode(array, pointAffine.y);
        final int[] create = X448Field.create();
        final int[] create2 = X448Field.create();
        X448Field.sqr(pointAffine.y, create);
        X448Field.mul(create, 39081, create2);
        X448Field.negate(create, create);
        X448Field.addOne(create);
        X448Field.addOne(create2);
        if (!X448Field.sqrtRatioVar(create, create2, pointAffine.x)) {
            return false;
        }
        X448Field.normalize(pointAffine.x);
        if (n == 1 && X448Field.isZeroVar(pointAffine.x)) {
            return false;
        }
        if (b ^ n != (pointAffine.x[0] & 0x1)) {
            X448Field.negate(pointAffine.x, pointAffine.x);
            X448Field.normalize(pointAffine.x);
        }
        return true;
    }
    
    private static void dom4(final Xof xof, final byte b, final byte[] array) {
        final int length = Ed448.DOM4_PREFIX.length;
        final byte[] array2 = new byte[length + 2 + array.length];
        System.arraycopy(Ed448.DOM4_PREFIX, 0, array2, 0, length);
        array2[length] = b;
        array2[length + 1] = (byte)array.length;
        System.arraycopy(array, 0, array2, length + 2, array.length);
        xof.update(array2, 0, array2.length);
    }
    
    private static void encodePoint(final PointAffine pointAffine, final byte[] array, final int n) {
        X448Field.encode(pointAffine.y, array, n);
        array[n + 57 - 1] = (byte)((pointAffine.x[0] & 0x1) << 7);
    }
    
    public static void encodePublicPoint(final PublicPoint publicPoint, final byte[] array, final int n) {
        X448Field.encode(publicPoint.data, 16, array, n);
        array[n + 57 - 1] = (byte)((publicPoint.data[0] & 0x1) << 7);
    }
    
    private static int encodeResult(final PointProjective pointProjective, final byte[] array, final int n) {
        final PointAffine pointAffine = new PointAffine();
        normalizeToAffine(pointProjective, pointAffine);
        final int checkPoint = checkPoint(pointAffine);
        encodePoint(pointAffine, array, n);
        return checkPoint;
    }
    
    private static PublicPoint exportPoint(final PointAffine pointAffine) {
        final int[] array = new int[32];
        X448Field.copy(pointAffine.x, 0, array, 0);
        X448Field.copy(pointAffine.y, 0, array, 16);
        return new PublicPoint(array);
    }
    
    public static void generatePrivateKey(final SecureRandom secureRandom, final byte[] bytes) {
        if (bytes.length != 57) {
            throw new IllegalArgumentException("k");
        }
        secureRandom.nextBytes(bytes);
    }
    
    public static void generatePublicKey(final byte[] array, final int n, final byte[] array2, final int n2) {
        final Xof xof = createXof();
        final byte[] array3 = new byte[114];
        xof.update(array, n, 57);
        xof.doFinal(array3, 0, array3.length);
        final byte[] array4 = new byte[57];
        pruneScalar(array3, 0, array4);
        scalarMultBaseEncoded(array4, array2, n2);
    }
    
    public static PublicPoint generatePublicKey(final byte[] array, final int n) {
        final Xof xof = createXof();
        final byte[] array2 = new byte[114];
        xof.update(array, n, 57);
        xof.doFinal(array2, 0, array2.length);
        final byte[] array3 = new byte[57];
        pruneScalar(array2, 0, array3);
        final PointProjective pointProjective = new PointProjective();
        scalarMultBase(array3, pointProjective);
        final PointAffine pointAffine = new PointAffine();
        normalizeToAffine(pointProjective, pointAffine);
        if (0 == checkPoint(pointAffine)) {
            throw new IllegalStateException();
        }
        return exportPoint(pointAffine);
    }
    
    private static int getWindow4(final int[] array, final int n) {
        return array[n >>> 3] >>> ((n & 0x7) << 2) & 0xF;
    }
    
    private static void implSign(final Xof xof, final byte[] array, final byte[] array2, final byte[] array3, final int n, final byte[] array4, final byte b, final byte[] array5, final int n2, final int n3, final byte[] array6, final int n4) {
        dom4(xof, b, array4);
        xof.update(array, 57, 57);
        xof.update(array5, n2, n3);
        xof.doFinal(array, 0, array.length);
        final byte[] reduce912 = Scalar448.reduce912(array);
        final byte[] array7 = new byte[57];
        scalarMultBaseEncoded(reduce912, array7, 0);
        dom4(xof, b, array4);
        xof.update(array7, 0, 57);
        xof.update(array3, n, 57);
        xof.update(array5, n2, n3);
        xof.doFinal(array, 0, array.length);
        final byte[] calculateS = calculateS(reduce912, Scalar448.reduce912(array), array2);
        System.arraycopy(array7, 0, array6, n4, 57);
        System.arraycopy(calculateS, 0, array6, n4 + 57, 57);
    }
    
    private static void implSign(final byte[] array, final int n, final byte[] array2, final byte b, final byte[] array3, final int n2, final int n3, final byte[] array4, final int n4) {
        if (!checkContextVar(array2)) {
            throw new IllegalArgumentException("ctx");
        }
        final Xof xof = createXof();
        final byte[] array5 = new byte[114];
        xof.update(array, n, 57);
        xof.doFinal(array5, 0, array5.length);
        final byte[] array6 = new byte[57];
        pruneScalar(array5, 0, array6);
        final byte[] array7 = new byte[57];
        scalarMultBaseEncoded(array6, array7, 0);
        implSign(xof, array5, array6, array7, 0, array2, b, array3, n2, n3, array4, n4);
    }
    
    private static void implSign(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte b, final byte[] array4, final int n3, final int n4, final byte[] array5, final int n5) {
        if (!checkContextVar(array3)) {
            throw new IllegalArgumentException("ctx");
        }
        final Xof xof = createXof();
        final byte[] array6 = new byte[114];
        xof.update(array, n, 57);
        xof.doFinal(array6, 0, array6.length);
        final byte[] array7 = new byte[57];
        pruneScalar(array6, 0, array7);
        implSign(xof, array6, array7, array2, n2, array3, b, array4, n3, n4, array5, n5);
    }
    
    private static boolean implVerify(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte b, final byte[] array4, final int n3, final int n4) {
        if (!checkContextVar(array3)) {
            throw new IllegalArgumentException("ctx");
        }
        final byte[] copy = copy(array, n, 57);
        final byte[] copy2 = copy(array, n + 57, 57);
        final byte[] copy3 = copy(array2, n2, 57);
        if (!checkPointVar(copy)) {
            return false;
        }
        final int[] array5 = new int[14];
        if (!Scalar448.checkVar(copy2, array5)) {
            return false;
        }
        if (!checkPointFullVar(copy3)) {
            return false;
        }
        final PointAffine pointAffine = new PointAffine();
        if (!decodePointVar(copy, true, pointAffine)) {
            return false;
        }
        final PointAffine pointAffine2 = new PointAffine();
        if (!decodePointVar(copy3, true, pointAffine2)) {
            return false;
        }
        final Xof xof = createXof();
        final byte[] array6 = new byte[114];
        dom4(xof, b, array3);
        xof.update(copy, 0, 57);
        xof.update(copy3, 0, 57);
        xof.update(array4, n3, n4);
        xof.doFinal(array6, 0, array6.length);
        final byte[] reduce912 = Scalar448.reduce912(array6);
        final int[] array7 = new int[14];
        Scalar448.decode(reduce912, array7);
        final int[] array8 = new int[8];
        final int[] array9 = new int[8];
        if (!Scalar448.reduceBasisVar(array7, array8, array9)) {
            throw new IllegalStateException();
        }
        Scalar448.multiply225Var(array5, array9, array5);
        final PointProjective pointProjective = new PointProjective();
        scalarMultStraus225Var(array5, array8, pointAffine2, array9, pointAffine, pointProjective);
        return normalizeToNeutralElementVar(pointProjective);
    }
    
    private static boolean implVerify(final byte[] array, final int n, final PublicPoint publicPoint, final byte[] array2, final byte b, final byte[] array3, final int n2, final int n3) {
        if (!checkContextVar(array2)) {
            throw new IllegalArgumentException("ctx");
        }
        final byte[] copy = copy(array, n, 57);
        final byte[] copy2 = copy(array, n + 57, 57);
        if (!checkPointVar(copy)) {
            return false;
        }
        final int[] array4 = new int[14];
        if (!Scalar448.checkVar(copy2, array4)) {
            return false;
        }
        final PointAffine pointAffine = new PointAffine();
        if (!decodePointVar(copy, true, pointAffine)) {
            return false;
        }
        final PointAffine pointAffine2 = new PointAffine();
        X448Field.negate(publicPoint.data, pointAffine2.x);
        X448Field.copy(publicPoint.data, 16, pointAffine2.y, 0);
        final byte[] array5 = new byte[57];
        encodePublicPoint(publicPoint, array5, 0);
        final Xof xof = createXof();
        final byte[] array6 = new byte[114];
        dom4(xof, b, array2);
        xof.update(copy, 0, 57);
        xof.update(array5, 0, 57);
        xof.update(array3, n2, n3);
        xof.doFinal(array6, 0, array6.length);
        final byte[] reduce912 = Scalar448.reduce912(array6);
        final int[] array7 = new int[14];
        Scalar448.decode(reduce912, array7);
        final int[] array8 = new int[8];
        final int[] array9 = new int[8];
        if (!Scalar448.reduceBasisVar(array7, array8, array9)) {
            throw new IllegalStateException();
        }
        Scalar448.multiply225Var(array4, array9, array4);
        final PointProjective pointProjective = new PointProjective();
        scalarMultStraus225Var(array4, array8, pointAffine2, array9, pointAffine, pointProjective);
        return normalizeToNeutralElementVar(pointProjective);
    }
    
    private static void invertZs(final PointProjective[] array) {
        final int length = array.length;
        final int[] table = X448Field.createTable(length);
        final int[] create = X448Field.create();
        X448Field.copy(array[0].z, 0, create, 0);
        X448Field.copy(create, 0, table, 0);
        int i = 0;
        while (++i < length) {
            X448Field.mul(create, array[i].z, create);
            X448Field.copy(create, 0, table, i * 16);
        }
        X448Field.invVar(create, create);
        --i;
        final int[] create2 = X448Field.create();
        while (i > 0) {
            final int n = i--;
            X448Field.copy(table, i * 16, create2, 0);
            X448Field.mul(create2, create, create2);
            X448Field.mul(create, array[n].z, create);
            X448Field.copy(create2, 0, array[n].z, 0);
        }
        X448Field.copy(create, 0, array[0].z, 0);
    }
    
    private static void normalizeToAffine(final PointProjective pointProjective, final PointAffine pointAffine) {
        X448Field.inv(pointProjective.z, pointAffine.y);
        X448Field.mul(pointAffine.y, pointProjective.x, pointAffine.x);
        X448Field.mul(pointAffine.y, pointProjective.y, pointAffine.y);
        X448Field.normalize(pointAffine.x);
        X448Field.normalize(pointAffine.y);
    }
    
    private static boolean normalizeToNeutralElementVar(final PointProjective pointProjective) {
        X448Field.normalize(pointProjective.x);
        X448Field.normalize(pointProjective.y);
        X448Field.normalize(pointProjective.z);
        return X448Field.isZeroVar(pointProjective.x) && !X448Field.isZeroVar(pointProjective.y) && X448Field.areEqualVar(pointProjective.y, pointProjective.z);
    }
    
    private static void pointAdd(final PointAffine pointAffine, final PointProjective pointProjective, final PointTemp pointTemp) {
        final int[] r1 = pointTemp.r1;
        final int[] r2 = pointTemp.r2;
        final int[] r3 = pointTemp.r3;
        final int[] r4 = pointTemp.r4;
        final int[] r5 = pointTemp.r5;
        final int[] r6 = pointTemp.r6;
        final int[] r7 = pointTemp.r7;
        X448Field.sqr(pointProjective.z, r1);
        X448Field.mul(pointAffine.x, pointProjective.x, r2);
        X448Field.mul(pointAffine.y, pointProjective.y, r3);
        X448Field.mul(r2, r3, r4);
        X448Field.mul(r4, 39081, r4);
        X448Field.add(r1, r4, r5);
        X448Field.sub(r1, r4, r6);
        X448Field.add(pointAffine.y, pointAffine.x, r7);
        X448Field.add(pointProjective.y, pointProjective.x, r4);
        X448Field.mul(r7, r4, r7);
        X448Field.add(r3, r2, r1);
        X448Field.sub(r3, r2, r4);
        X448Field.carry(r1);
        X448Field.sub(r7, r1, r7);
        X448Field.mul(r7, pointProjective.z, r7);
        X448Field.mul(r4, pointProjective.z, r4);
        X448Field.mul(r5, r7, pointProjective.x);
        X448Field.mul(r4, r6, pointProjective.y);
        X448Field.mul(r5, r6, pointProjective.z);
    }
    
    private static void pointAdd(final PointProjective pointProjective, final PointProjective pointProjective2, final PointTemp pointTemp) {
        final int[] r0 = pointTemp.r0;
        final int[] r2 = pointTemp.r1;
        final int[] r3 = pointTemp.r2;
        final int[] r4 = pointTemp.r3;
        final int[] r5 = pointTemp.r4;
        final int[] r6 = pointTemp.r5;
        final int[] r7 = pointTemp.r6;
        final int[] r8 = pointTemp.r7;
        X448Field.mul(pointProjective.z, pointProjective2.z, r0);
        X448Field.sqr(r0, r2);
        X448Field.mul(pointProjective.x, pointProjective2.x, r3);
        X448Field.mul(pointProjective.y, pointProjective2.y, r4);
        X448Field.mul(r3, r4, r5);
        X448Field.mul(r5, 39081, r5);
        X448Field.add(r2, r5, r6);
        X448Field.sub(r2, r5, r7);
        X448Field.add(pointProjective.y, pointProjective.x, r8);
        X448Field.add(pointProjective2.y, pointProjective2.x, r5);
        X448Field.mul(r8, r5, r8);
        X448Field.add(r4, r3, r2);
        X448Field.sub(r4, r3, r5);
        X448Field.carry(r2);
        X448Field.sub(r8, r2, r8);
        X448Field.mul(r8, r0, r8);
        X448Field.mul(r5, r0, r5);
        X448Field.mul(r6, r8, pointProjective2.x);
        X448Field.mul(r5, r7, pointProjective2.y);
        X448Field.mul(r6, r7, pointProjective2.z);
    }
    
    private static void pointAddVar(final boolean b, final PointAffine pointAffine, final PointProjective pointProjective, final PointTemp pointTemp) {
        final int[] r1 = pointTemp.r1;
        final int[] r2 = pointTemp.r2;
        final int[] r3 = pointTemp.r3;
        final int[] r4 = pointTemp.r4;
        final int[] r5 = pointTemp.r5;
        final int[] r6 = pointTemp.r6;
        final int[] r7 = pointTemp.r7;
        int[] array;
        int[] array2;
        int[] array3;
        int[] array4;
        if (b) {
            array = r4;
            array2 = r1;
            array3 = r6;
            array4 = r5;
            X448Field.sub(pointAffine.y, pointAffine.x, r7);
        }
        else {
            array = r1;
            array2 = r4;
            array3 = r5;
            array4 = r6;
            X448Field.add(pointAffine.y, pointAffine.x, r7);
        }
        X448Field.sqr(pointProjective.z, r1);
        X448Field.mul(pointAffine.x, pointProjective.x, r2);
        X448Field.mul(pointAffine.y, pointProjective.y, r3);
        X448Field.mul(r2, r3, r4);
        X448Field.mul(r4, 39081, r4);
        X448Field.add(r1, r4, array3);
        X448Field.sub(r1, r4, array4);
        X448Field.add(pointProjective.y, pointProjective.x, r4);
        X448Field.mul(r7, r4, r7);
        X448Field.add(r3, r2, array);
        X448Field.sub(r3, r2, array2);
        X448Field.carry(array);
        X448Field.sub(r7, r1, r7);
        X448Field.mul(r7, pointProjective.z, r7);
        X448Field.mul(r4, pointProjective.z, r4);
        X448Field.mul(r5, r7, pointProjective.x);
        X448Field.mul(r4, r6, pointProjective.y);
        X448Field.mul(r5, r6, pointProjective.z);
    }
    
    private static void pointAddVar(final boolean b, final PointProjective pointProjective, final PointProjective pointProjective2, final PointTemp pointTemp) {
        final int[] r0 = pointTemp.r0;
        final int[] r2 = pointTemp.r1;
        final int[] r3 = pointTemp.r2;
        final int[] r4 = pointTemp.r3;
        final int[] r5 = pointTemp.r4;
        final int[] r6 = pointTemp.r5;
        final int[] r7 = pointTemp.r6;
        final int[] r8 = pointTemp.r7;
        int[] array;
        int[] array2;
        int[] array3;
        int[] array4;
        if (b) {
            array = r5;
            array2 = r2;
            array3 = r7;
            array4 = r6;
            X448Field.sub(pointProjective.y, pointProjective.x, r8);
        }
        else {
            array = r2;
            array2 = r5;
            array3 = r6;
            array4 = r7;
            X448Field.add(pointProjective.y, pointProjective.x, r8);
        }
        X448Field.mul(pointProjective.z, pointProjective2.z, r0);
        X448Field.sqr(r0, r2);
        X448Field.mul(pointProjective.x, pointProjective2.x, r3);
        X448Field.mul(pointProjective.y, pointProjective2.y, r4);
        X448Field.mul(r3, r4, r5);
        X448Field.mul(r5, 39081, r5);
        X448Field.add(r2, r5, array3);
        X448Field.sub(r2, r5, array4);
        X448Field.add(pointProjective2.y, pointProjective2.x, r5);
        X448Field.mul(r8, r5, r8);
        X448Field.add(r4, r3, array);
        X448Field.sub(r4, r3, array2);
        X448Field.carry(array);
        X448Field.sub(r8, r2, r8);
        X448Field.mul(r8, r0, r8);
        X448Field.mul(r5, r0, r5);
        X448Field.mul(r6, r8, pointProjective2.x);
        X448Field.mul(r5, r7, pointProjective2.y);
        X448Field.mul(r6, r7, pointProjective2.z);
    }
    
    private static void pointCopy(final PointAffine pointAffine, final PointProjective pointProjective) {
        X448Field.copy(pointAffine.x, 0, pointProjective.x, 0);
        X448Field.copy(pointAffine.y, 0, pointProjective.y, 0);
        X448Field.one(pointProjective.z);
    }
    
    private static void pointCopy(final PointProjective pointProjective, final PointProjective pointProjective2) {
        X448Field.copy(pointProjective.x, 0, pointProjective2.x, 0);
        X448Field.copy(pointProjective.y, 0, pointProjective2.y, 0);
        X448Field.copy(pointProjective.z, 0, pointProjective2.z, 0);
    }
    
    private static void pointDouble(final PointProjective pointProjective, final PointTemp pointTemp) {
        final int[] r1 = pointTemp.r1;
        final int[] r2 = pointTemp.r2;
        final int[] r3 = pointTemp.r3;
        final int[] r4 = pointTemp.r4;
        final int[] r5 = pointTemp.r7;
        final int[] r6 = pointTemp.r0;
        X448Field.add(pointProjective.x, pointProjective.y, r1);
        X448Field.sqr(r1, r1);
        X448Field.sqr(pointProjective.x, r2);
        X448Field.sqr(pointProjective.y, r3);
        X448Field.add(r2, r3, r4);
        X448Field.carry(r4);
        X448Field.sqr(pointProjective.z, r5);
        X448Field.add(r5, r5, r5);
        X448Field.carry(r5);
        X448Field.sub(r4, r5, r6);
        X448Field.sub(r1, r4, r1);
        X448Field.sub(r2, r3, r2);
        X448Field.mul(r1, r6, pointProjective.x);
        X448Field.mul(r4, r2, pointProjective.y);
        X448Field.mul(r4, r6, pointProjective.z);
    }
    
    private static void pointLookup(final int n, final int n2, final PointAffine pointAffine) {
        int n3 = n * 16 * 2 * 16;
        for (int i = 0; i < 16; ++i) {
            final int n4 = (i ^ n2) - 1 >> 31;
            X448Field.cmov(n4, Ed448.PRECOMP_BASE_COMB, n3, pointAffine.x, 0);
            n3 += 16;
            X448Field.cmov(n4, Ed448.PRECOMP_BASE_COMB, n3, pointAffine.y, 0);
            n3 += 16;
        }
    }
    
    private static void pointLookup(final int[] array, final int n, final int[] array2, final PointProjective pointProjective) {
        final int window4 = getWindow4(array, n);
        final int n2 = window4 >>> 3 ^ 0x1;
        final int n3 = (window4 ^ -n2) & 0x7;
        int i = 0;
        int n4 = 0;
        while (i < 8) {
            final int n5 = (i ^ n3) - 1 >> 31;
            X448Field.cmov(n5, array2, n4, pointProjective.x, 0);
            n4 += 16;
            X448Field.cmov(n5, array2, n4, pointProjective.y, 0);
            n4 += 16;
            X448Field.cmov(n5, array2, n4, pointProjective.z, 0);
            n4 += 16;
            ++i;
        }
        X448Field.cnegate(n2, pointProjective.x);
    }
    
    private static void pointLookup15(final int[] array, final PointProjective pointProjective) {
        int n = 336;
        X448Field.copy(array, n, pointProjective.x, 0);
        n += 16;
        X448Field.copy(array, n, pointProjective.y, 0);
        n += 16;
        X448Field.copy(array, n, pointProjective.z, 0);
    }
    
    private static int[] pointPrecompute(final PointProjective pointProjective, final int n, final PointTemp pointTemp) {
        final PointProjective pointProjective2 = new PointProjective();
        pointCopy(pointProjective, pointProjective2);
        final PointProjective pointProjective3 = new PointProjective();
        pointCopy(pointProjective, pointProjective3);
        pointDouble(pointProjective3, pointTemp);
        final int[] table = X448Field.createTable(n * 3);
        int n2 = 0;
        int n3 = 0;
        while (true) {
            X448Field.copy(pointProjective2.x, 0, table, n2);
            n2 += 16;
            X448Field.copy(pointProjective2.y, 0, table, n2);
            n2 += 16;
            X448Field.copy(pointProjective2.z, 0, table, n2);
            n2 += 16;
            if (++n3 == n) {
                break;
            }
            pointAdd(pointProjective3, pointProjective2, pointTemp);
        }
        return table;
    }
    
    private static void pointPrecompute(final PointAffine pointAffine, final PointProjective[] array, final int n, final int n2, final PointTemp pointTemp) {
        final PointProjective pointProjective = new PointProjective();
        pointCopy(pointAffine, pointProjective);
        pointDouble(pointProjective, pointTemp);
        pointCopy(pointAffine, array[n] = new PointProjective());
        for (int i = 1; i < n2; ++i) {
            array[n + i] = new PointProjective();
            pointCopy(array[n + i - 1], array[n + i]);
            pointAdd(pointProjective, array[n + i], pointTemp);
        }
    }
    
    private static void pointSetNeutral(final PointProjective pointProjective) {
        X448Field.zero(pointProjective.x);
        X448Field.one(pointProjective.y);
        X448Field.one(pointProjective.z);
    }
    
    public static void precompute() {
        synchronized (Ed448.PRECOMP_LOCK) {
            if (Ed448.PRECOMP_BASE_COMB != null) {
                return;
            }
            final int n = 32;
            final int n2 = 80;
            final int n3 = n * 2 + n2;
            final PointProjective[] array = new PointProjective[n3];
            final PointTemp pointTemp = new PointTemp();
            final PointAffine pointAffine = new PointAffine();
            X448Field.copy(Ed448.B_x, 0, pointAffine.x, 0);
            X448Field.copy(Ed448.B_y, 0, pointAffine.y, 0);
            pointPrecompute(pointAffine, array, 0, n, pointTemp);
            final PointAffine pointAffine2 = new PointAffine();
            X448Field.copy(Ed448.B225_x, 0, pointAffine2.x, 0);
            X448Field.copy(Ed448.B225_y, 0, pointAffine2.y, 0);
            pointPrecompute(pointAffine2, array, n, n, pointTemp);
            final PointProjective pointProjective = new PointProjective();
            pointCopy(pointAffine, pointProjective);
            int n4 = n * 2;
            final PointProjective[] array2 = new PointProjective[5];
            for (int i = 0; i < 5; ++i) {
                array2[i] = new PointProjective();
            }
            for (int j = 0; j < 5; ++j) {
                final PointProjective[] array3 = array;
                final int n5 = n4++;
                final PointProjective pointProjective2 = new PointProjective();
                array3[n5] = pointProjective2;
                final PointProjective pointProjective3 = pointProjective2;
                for (int k = 0; k < 5; ++k) {
                    if (k == 0) {
                        pointCopy(pointProjective, pointProjective3);
                    }
                    else {
                        pointAdd(pointProjective, pointProjective3, pointTemp);
                    }
                    pointDouble(pointProjective, pointTemp);
                    pointCopy(pointProjective, array2[k]);
                    if (j + k != 8) {
                        for (int l = 1; l < 18; ++l) {
                            pointDouble(pointProjective, pointTemp);
                        }
                    }
                }
                X448Field.negate(pointProjective3.x, pointProjective3.x);
                for (int n6 = 0; n6 < 4; ++n6) {
                    for (int n7 = 1 << n6, n8 = 0; n8 < n7; ++n8, ++n4) {
                        array[n4] = new PointProjective();
                        pointCopy(array[n4 - n7], array[n4]);
                        pointAdd(array2[n6], array[n4], pointTemp);
                    }
                }
            }
            invertZs(array);
            Ed448.PRECOMP_BASE_WNAF = new PointAffine[n];
            for (int n9 = 0; n9 < n; ++n9) {
                final PointProjective pointProjective4 = array[n9];
                final PointAffine[] precomp_BASE_WNAF = Ed448.PRECOMP_BASE_WNAF;
                final int n10 = n9;
                final PointAffine pointAffine3 = new PointAffine();
                precomp_BASE_WNAF[n10] = pointAffine3;
                final PointAffine pointAffine4 = pointAffine3;
                X448Field.mul(pointProjective4.x, pointProjective4.z, pointAffine4.x);
                X448Field.normalize(pointAffine4.x);
                X448Field.mul(pointProjective4.y, pointProjective4.z, pointAffine4.y);
                X448Field.normalize(pointAffine4.y);
            }
            Ed448.PRECOMP_BASE225_WNAF = new PointAffine[n];
            for (int n11 = 0; n11 < n; ++n11) {
                final PointProjective pointProjective5 = array[n + n11];
                final PointAffine[] precomp_BASE225_WNAF = Ed448.PRECOMP_BASE225_WNAF;
                final int n12 = n11;
                final PointAffine pointAffine5 = new PointAffine();
                precomp_BASE225_WNAF[n12] = pointAffine5;
                final PointAffine pointAffine6 = pointAffine5;
                X448Field.mul(pointProjective5.x, pointProjective5.z, pointAffine6.x);
                X448Field.normalize(pointAffine6.x);
                X448Field.mul(pointProjective5.y, pointProjective5.z, pointAffine6.y);
                X448Field.normalize(pointAffine6.y);
            }
            Ed448.PRECOMP_BASE_COMB = X448Field.createTable(n2 * 2);
            int n13 = 0;
            for (int n14 = n * 2; n14 < n3; ++n14) {
                final PointProjective pointProjective6 = array[n14];
                X448Field.mul(pointProjective6.x, pointProjective6.z, pointProjective6.x);
                X448Field.normalize(pointProjective6.x);
                X448Field.mul(pointProjective6.y, pointProjective6.z, pointProjective6.y);
                X448Field.normalize(pointProjective6.y);
                X448Field.copy(pointProjective6.x, 0, Ed448.PRECOMP_BASE_COMB, n13);
                n13 += 16;
                X448Field.copy(pointProjective6.y, 0, Ed448.PRECOMP_BASE_COMB, n13);
                n13 += 16;
            }
        }
    }
    
    private static void pruneScalar(final byte[] array, final int n, final byte[] array2) {
        System.arraycopy(array, n, array2, 0, 56);
        final int n2 = 0;
        array2[n2] &= (byte)252;
        final int n3 = 55;
        array2[n3] |= (byte)128;
        array2[56] = 0;
    }
    
    private static void scalarMult(final byte[] array, final PointProjective pointProjective, final PointProjective pointProjective2) {
        final int[] array2 = new int[15];
        Scalar448.decode(array, array2);
        Scalar448.toSignedDigits(449, array2, array2);
        final PointProjective pointProjective3 = new PointProjective();
        final PointTemp pointTemp = new PointTemp();
        final int[] pointPrecompute = pointPrecompute(pointProjective, 8, pointTemp);
        pointLookup15(pointPrecompute, pointProjective2);
        pointAdd(pointProjective, pointProjective2, pointTemp);
        int n = 111;
        while (true) {
            pointLookup(array2, n, pointPrecompute, pointProjective3);
            pointAdd(pointProjective3, pointProjective2, pointTemp);
            if (--n < 0) {
                break;
            }
            for (int i = 0; i < 4; ++i) {
                pointDouble(pointProjective2, pointTemp);
            }
        }
    }
    
    private static void scalarMultBase(final byte[] array, final PointProjective pointProjective) {
        precompute();
        final int[] array2 = new int[15];
        Scalar448.decode(array, array2);
        Scalar448.toSignedDigits(450, array2, array2);
        final PointAffine pointAffine = new PointAffine();
        final PointTemp pointTemp = new PointTemp();
        pointSetNeutral(pointProjective);
        int n = 17;
        while (true) {
            int n2 = n;
            for (int i = 0; i < 5; ++i) {
                int n3 = 0;
                for (int j = 0; j < 5; ++j) {
                    n3 = ((n3 & ~(1 << j)) ^ array2[n2 >>> 5] >>> (n2 & 0x1F) << j);
                    n2 += 18;
                }
                final int n4 = n3 >>> 4 & 0x1;
                pointLookup(i, (n3 ^ -n4) & 0xF, pointAffine);
                X448Field.cnegate(n4, pointAffine.x);
                pointAdd(pointAffine, pointProjective, pointTemp);
            }
            if (--n < 0) {
                break;
            }
            pointDouble(pointProjective, pointTemp);
        }
    }
    
    private static void scalarMultBaseEncoded(final byte[] array, final byte[] array2, final int n) {
        final PointProjective pointProjective = new PointProjective();
        scalarMultBase(array, pointProjective);
        if (0 == encodeResult(pointProjective, array2, n)) {
            throw new IllegalStateException();
        }
    }
    
    public static void scalarMultBaseXY(final X448.Friend friend, final byte[] array, final int n, final int[] array2, final int[] array3) {
        if (null == friend) {
            throw new NullPointerException("This method is only for use by X448");
        }
        final byte[] array4 = new byte[57];
        pruneScalar(array, n, array4);
        final PointProjective pointProjective = new PointProjective();
        scalarMultBase(array4, pointProjective);
        if (0 == checkPoint(pointProjective)) {
            throw new IllegalStateException();
        }
        X448Field.copy(pointProjective.x, 0, array2, 0);
        X448Field.copy(pointProjective.y, 0, array3, 0);
    }
    
    private static void scalarMultOrderVar(final PointAffine pointAffine, final PointProjective pointProjective) {
        final byte[] array = new byte[447];
        Scalar448.getOrderWnafVar(5, array);
        final int n = 8;
        final PointProjective[] array2 = new PointProjective[n];
        final PointTemp pointTemp = new PointTemp();
        pointPrecompute(pointAffine, array2, 0, n, pointTemp);
        pointSetNeutral(pointProjective);
        int n2 = 446;
        while (true) {
            final byte b = array[n2];
            if (b != 0) {
                pointAddVar(b < 0, array2[b >> 1 ^ b >> 31], pointProjective, pointTemp);
            }
            if (--n2 < 0) {
                break;
            }
            pointDouble(pointProjective, pointTemp);
        }
    }
    
    private static void scalarMultStraus225Var(final int[] array, final int[] array2, final PointAffine pointAffine, final int[] array3, final PointAffine pointAffine2, final PointProjective pointProjective) {
        precompute();
        final byte[] array4 = new byte[450];
        final byte[] array5 = new byte[225];
        final byte[] array6 = new byte[225];
        Wnaf.getSignedVar(array, 7, array4);
        Wnaf.getSignedVar(array2, 5, array5);
        Wnaf.getSignedVar(array3, 5, array6);
        final int n = 8;
        final PointProjective[] array7 = new PointProjective[n];
        final PointProjective[] array8 = new PointProjective[n];
        final PointTemp pointTemp = new PointTemp();
        pointPrecompute(pointAffine, array7, 0, n, pointTemp);
        pointPrecompute(pointAffine2, array8, 0, n, pointTemp);
        pointSetNeutral(pointProjective);
        int i = 225;
        while (--i >= 0 && (array4[i] | array4[225 + i] | array5[i] | array6[i]) == 0x0) {}
        while (i >= 0) {
            final byte b = array4[i];
            if (b != 0) {
                pointAddVar(b < 0, Ed448.PRECOMP_BASE_WNAF[b >> 1 ^ b >> 31], pointProjective, pointTemp);
            }
            final byte b2 = array4[225 + i];
            if (b2 != 0) {
                pointAddVar(b2 < 0, Ed448.PRECOMP_BASE225_WNAF[b2 >> 1 ^ b2 >> 31], pointProjective, pointTemp);
            }
            final byte b3 = array5[i];
            if (b3 != 0) {
                pointAddVar(b3 < 0, array7[b3 >> 1 ^ b3 >> 31], pointProjective, pointTemp);
            }
            final byte b4 = array6[i];
            if (b4 != 0) {
                pointAddVar(b4 < 0, array8[b4 >> 1 ^ b4 >> 31], pointProjective, pointTemp);
            }
            pointDouble(pointProjective, pointTemp);
            --i;
        }
        pointDouble(pointProjective, pointTemp);
    }
    
    public static void sign(final byte[] array, final int n, final byte[] array2, final byte[] array3, final int n2, final int n3, final byte[] array4, final int n4) {
        implSign(array, n, array2, (byte)0, array3, n2, n3, array4, n4);
    }
    
    public static void sign(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte[] array4, final int n3, final int n4, final byte[] array5, final int n5) {
        implSign(array, n, array2, n2, array3, (byte)0, array4, n3, n4, array5, n5);
    }
    
    public static void signPrehash(final byte[] array, final int n, final byte[] array2, final byte[] array3, final int n2, final byte[] array4, final int n3) {
        implSign(array, n, array2, (byte)1, array3, n2, 64, array4, n3);
    }
    
    public static void signPrehash(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte[] array4, final int n3, final byte[] array5, final int n4) {
        implSign(array, n, array2, n2, array3, (byte)1, array4, n3, 64, array5, n4);
    }
    
    public static void signPrehash(final byte[] array, final int n, final byte[] array2, final Xof xof, final byte[] array3, final int n2) {
        final byte[] array4 = new byte[64];
        if (64 != xof.doFinal(array4, 0, 64)) {
            throw new IllegalArgumentException("ph");
        }
        implSign(array, n, array2, (byte)1, array4, 0, array4.length, array3, n2);
    }
    
    public static void signPrehash(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final Xof xof, final byte[] array4, final int n3) {
        final byte[] array5 = new byte[64];
        if (64 != xof.doFinal(array5, 0, 64)) {
            throw new IllegalArgumentException("ph");
        }
        implSign(array, n, array2, n2, array3, (byte)1, array5, 0, array5.length, array4, n3);
    }
    
    public static boolean validatePublicKeyFull(final byte[] array, final int n) {
        final byte[] copy = copy(array, n, 57);
        if (!checkPointFullVar(copy)) {
            return false;
        }
        final PointAffine pointAffine = new PointAffine();
        return decodePointVar(copy, false, pointAffine) && checkPointOrderVar(pointAffine);
    }
    
    public static PublicPoint validatePublicKeyFullExport(final byte[] array, final int n) {
        final byte[] copy = copy(array, n, 57);
        if (!checkPointFullVar(copy)) {
            return null;
        }
        final PointAffine pointAffine = new PointAffine();
        if (!decodePointVar(copy, false, pointAffine)) {
            return null;
        }
        if (!checkPointOrderVar(pointAffine)) {
            return null;
        }
        return exportPoint(pointAffine);
    }
    
    public static boolean validatePublicKeyPartial(final byte[] array, final int n) {
        final byte[] copy = copy(array, n, 57);
        return checkPointFullVar(copy) && decodePointVar(copy, false, new PointAffine());
    }
    
    public static PublicPoint validatePublicKeyPartialExport(final byte[] array, final int n) {
        final byte[] copy = copy(array, n, 57);
        if (!checkPointFullVar(copy)) {
            return null;
        }
        final PointAffine pointAffine = new PointAffine();
        if (!decodePointVar(copy, false, pointAffine)) {
            return null;
        }
        return exportPoint(pointAffine);
    }
    
    public static boolean verify(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte[] array4, final int n3, final int n4) {
        return implVerify(array, n, array2, n2, array3, (byte)0, array4, n3, n4);
    }
    
    public static boolean verify(final byte[] array, final int n, final PublicPoint publicPoint, final byte[] array2, final byte[] array3, final int n2, final int n3) {
        return implVerify(array, n, publicPoint, array2, (byte)0, array3, n2, n3);
    }
    
    public static boolean verifyPrehash(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final byte[] array4, final int n3) {
        return implVerify(array, n, array2, n2, array3, (byte)1, array4, n3, 64);
    }
    
    public static boolean verifyPrehash(final byte[] array, final int n, final PublicPoint publicPoint, final byte[] array2, final byte[] array3, final int n2) {
        return implVerify(array, n, publicPoint, array2, (byte)1, array3, n2, 64);
    }
    
    public static boolean verifyPrehash(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final Xof xof) {
        final byte[] array4 = new byte[64];
        if (64 != xof.doFinal(array4, 0, 64)) {
            throw new IllegalArgumentException("ph");
        }
        return implVerify(array, n, array2, n2, array3, (byte)1, array4, 0, array4.length);
    }
    
    public static boolean verifyPrehash(final byte[] array, final int n, final PublicPoint publicPoint, final byte[] array2, final Xof xof) {
        final byte[] array3 = new byte[64];
        if (64 != xof.doFinal(array3, 0, 64)) {
            throw new IllegalArgumentException("ph");
        }
        return implVerify(array, n, publicPoint, array2, (byte)1, array3, 0, array3.length);
    }
    
    static {
        DOM4_PREFIX = new byte[] { 83, 105, 103, 69, 100, 52, 52, 56 };
        P = new int[] { -1, -1, -1, -1, -1, -1, -1, -2, -1, -1, -1, -1, -1, -1 };
        B_x = new int[] { 118276190, 40534716, 9670182, 135141552, 85017403, 259173222, 68333082, 171784774, 174973732, 15824510, 73756743, 57518561, 94773951, 248652241, 107736333, 82941708 };
        B_y = new int[] { 36764180, 8885695, 130592152, 20104429, 163904957, 30304195, 121295871, 5901357, 125344798, 171541512, 175338348, 209069246, 3626697, 38307682, 24032956, 110359655 };
        B225_x = new int[] { 110141154, 30892124, 160820362, 264558960, 217232225, 47722141, 19029845, 8326902, 183409749, 170134547, 90340180, 222600478, 61097333, 7431335, 198491505, 102372861 };
        B225_y = new int[] { 221945828, 50763449, 132637478, 109250759, 216053960, 61612587, 50649998, 138339097, 98949899, 248139835, 186410297, 126520782, 47339196, 78164062, 198835543, 169622712 };
        PRECOMP_LOCK = new Object();
        Ed448.PRECOMP_BASE_WNAF = null;
        Ed448.PRECOMP_BASE225_WNAF = null;
        Ed448.PRECOMP_BASE_COMB = null;
    }
    
    public static final class Algorithm
    {
        public static final int Ed448 = 0;
        public static final int Ed448ph = 1;
    }
    
    private static class F extends X448Field
    {
    }
    
    private static class PointAffine
    {
        int[] x;
        int[] y;
        
        private PointAffine() {
            this.x = X448Field.create();
            this.y = X448Field.create();
        }
    }
    
    private static class PointProjective
    {
        int[] x;
        int[] y;
        int[] z;
        
        private PointProjective() {
            this.x = X448Field.create();
            this.y = X448Field.create();
            this.z = X448Field.create();
        }
    }
    
    private static class PointTemp
    {
        int[] r0;
        int[] r1;
        int[] r2;
        int[] r3;
        int[] r4;
        int[] r5;
        int[] r6;
        int[] r7;
        
        private PointTemp() {
            this.r0 = X448Field.create();
            this.r1 = X448Field.create();
            this.r2 = X448Field.create();
            this.r3 = X448Field.create();
            this.r4 = X448Field.create();
            this.r5 = X448Field.create();
            this.r6 = X448Field.create();
            this.r7 = X448Field.create();
        }
    }
    
    public static final class PublicPoint
    {
        final int[] data;
        
        PublicPoint(final int[] data) {
            this.data = data;
        }
    }
}
