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

package org.bouncycastle.math.ec.rfc8032;

import org.bouncycastle.math.ec.rfc7748.X25519;
import org.bouncycastle.math.raw.Interleave;
import java.security.SecureRandom;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.math.ec.rfc7748.X25519Field;
import org.bouncycastle.math.raw.Nat256;

public abstract class Ed25519
{
    private static final int COORD_INTS = 8;
    private static final int POINT_BYTES = 32;
    private static final int SCALAR_INTS = 8;
    private static final int SCALAR_BYTES = 32;
    public static final int PREHASH_SIZE = 64;
    public static final int PUBLIC_KEY_SIZE = 32;
    public static final int SECRET_KEY_SIZE = 32;
    public static final int SIGNATURE_SIZE = 64;
    private static final byte[] DOM2_PREFIX;
    private static final int[] P;
    private static final int[] ORDER8_y1;
    private static final int[] ORDER8_y2;
    private static final int[] B_x;
    private static final int[] B_y;
    private static final int[] B128_x;
    private static final int[] B128_y;
    private static final int[] C_d;
    private static final int[] C_d2;
    private static final int[] C_d4;
    private static final int WNAF_WIDTH_128 = 4;
    private static final int WNAF_WIDTH_BASE = 6;
    private static final int PRECOMP_BLOCKS = 8;
    private static final int PRECOMP_TEETH = 4;
    private static final int PRECOMP_SPACING = 8;
    private static final int PRECOMP_RANGE = 256;
    private static final int PRECOMP_POINTS = 8;
    private static final int PRECOMP_MASK = 7;
    private static final Object PRECOMP_LOCK;
    private static PointPrecomp[] PRECOMP_BASE_WNAF;
    private static PointPrecomp[] PRECOMP_BASE128_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[16];
        Scalar25519.decode(array, array4);
        final int[] array5 = new int[8];
        Scalar25519.decode(array2, array5);
        final int[] array6 = new int[8];
        Scalar25519.decode(array3, array6);
        Nat256.mulAddTo(array5, array6, array4);
        final byte[] array7 = new byte[64];
        Codec.encode32(array4, 0, array4.length, array7, 0);
        return Scalar25519.reduce512(array7);
    }
    
    private static boolean checkContextVar(final byte[] array, final byte b) {
        return (array == null && b == 0) || (array != null && array.length < 256);
    }
    
    private static int checkPoint(final PointAffine pointAffine) {
        final int[] create = X25519Field.create();
        final int[] create2 = X25519Field.create();
        final int[] create3 = X25519Field.create();
        X25519Field.sqr(pointAffine.x, create2);
        X25519Field.sqr(pointAffine.y, create3);
        X25519Field.mul(create2, create3, create);
        X25519Field.sub(create2, create3, create2);
        X25519Field.mul(create, Ed25519.C_d, create);
        X25519Field.addOne(create);
        X25519Field.add(create, create2, create);
        X25519Field.normalize(create);
        X25519Field.normalize(create3);
        return X25519Field.isZero(create) & ~X25519Field.isZero(create3);
    }
    
    private static int checkPoint(final PointAccum pointAccum) {
        final int[] create = X25519Field.create();
        final int[] create2 = X25519Field.create();
        final int[] create3 = X25519Field.create();
        final int[] create4 = X25519Field.create();
        X25519Field.sqr(pointAccum.x, create2);
        X25519Field.sqr(pointAccum.y, create3);
        X25519Field.sqr(pointAccum.z, create4);
        X25519Field.mul(create2, create3, create);
        X25519Field.sub(create2, create3, create2);
        X25519Field.mul(create2, create4, create2);
        X25519Field.sqr(create4, create4);
        X25519Field.mul(create, Ed25519.C_d, create);
        X25519Field.add(create, create4, create);
        X25519Field.add(create, create2, create);
        X25519Field.normalize(create);
        X25519Field.normalize(create3);
        X25519Field.normalize(create4);
        return X25519Field.isZero(create) & ~X25519Field.isZero(create3) & ~X25519Field.isZero(create4);
    }
    
    private static boolean checkPointFullVar(final byte[] array) {
        int n2;
        final int n = n2 = (Codec.decode32(array, 28) & Integer.MAX_VALUE);
        int n3 = n ^ Ed25519.P[7];
        int n4 = n ^ Ed25519.ORDER8_y1[7];
        int n5 = n ^ Ed25519.ORDER8_y2[7];
        for (int i = 6; i > 0; --i) {
            final int decode32 = Codec.decode32(array, i * 4);
            n2 |= decode32;
            n3 |= (decode32 ^ Ed25519.P[i]);
            n4 |= (decode32 ^ Ed25519.ORDER8_y1[i]);
            n5 |= (decode32 ^ Ed25519.ORDER8_y2[i]);
        }
        final int decode33 = Codec.decode32(array, 0);
        return (n2 != 0 || decode33 + Integer.MIN_VALUE > -2147483647) && (n3 != 0 || decode33 + Integer.MIN_VALUE < Ed25519.P[0] - 1 + Integer.MIN_VALUE) && ((n4 | (decode33 ^ Ed25519.ORDER8_y1[0])) != 0x0 & (n5 | (decode33 ^ Ed25519.ORDER8_y2[0])) != 0x0);
    }
    
    private static boolean checkPointOrderVar(final PointAffine pointAffine) {
        final PointAccum pointAccum = new PointAccum();
        scalarMultOrderVar(pointAffine, pointAccum);
        return normalizeToNeutralElementVar(pointAccum);
    }
    
    private static boolean checkPointVar(final byte[] array) {
        if ((Codec.decode32(array, 28) & Integer.MAX_VALUE) < Ed25519.P[7]) {
            return true;
        }
        final int[] array2 = new int[8];
        Codec.decode32(array, 0, array2, 0, 8);
        final int[] array3 = array2;
        final int n = 7;
        array3[n] &= Integer.MAX_VALUE;
        return !Nat256.gte(array2, Ed25519.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;
    }
    
    private static Digest createDigest() {
        final SHA512Digest sha512Digest = new SHA512Digest();
        if (sha512Digest.getDigestSize() != 64) {
            throw new IllegalStateException();
        }
        return sha512Digest;
    }
    
    public static Digest createPrehash() {
        return createDigest();
    }
    
    private static boolean decodePointVar(final byte[] array, final boolean b, final PointAffine pointAffine) {
        final int n = (array[31] & 0x80) >>> 7;
        X25519Field.decode(array, pointAffine.y);
        final int[] create = X25519Field.create();
        final int[] create2 = X25519Field.create();
        X25519Field.sqr(pointAffine.y, create);
        X25519Field.mul(Ed25519.C_d, create, create2);
        X25519Field.subOne(create);
        X25519Field.addOne(create2);
        if (!X25519Field.sqrtRatioVar(create, create2, pointAffine.x)) {
            return false;
        }
        X25519Field.normalize(pointAffine.x);
        if (n == 1 && X25519Field.isZeroVar(pointAffine.x)) {
            return false;
        }
        if (b ^ n != (pointAffine.x[0] & 0x1)) {
            X25519Field.negate(pointAffine.x, pointAffine.x);
            X25519Field.normalize(pointAffine.x);
        }
        return true;
    }
    
    private static void dom2(final Digest digest, final byte b, final byte[] array) {
        final int length = Ed25519.DOM2_PREFIX.length;
        final byte[] array2 = new byte[length + 2 + array.length];
        System.arraycopy(Ed25519.DOM2_PREFIX, 0, array2, 0, length);
        array2[length] = b;
        array2[length + 1] = (byte)array.length;
        System.arraycopy(array, 0, array2, length + 2, array.length);
        digest.update(array2, 0, array2.length);
    }
    
    private static void encodePoint(final PointAffine pointAffine, final byte[] array, final int n) {
        X25519Field.encode(pointAffine.y, array, n);
        final int n2 = n + 32 - 1;
        array[n2] |= (byte)((pointAffine.x[0] & 0x1) << 7);
    }
    
    public static void encodePublicPoint(final PublicPoint publicPoint, final byte[] array, final int n) {
        X25519Field.encode(publicPoint.data, 10, array, n);
        final int n2 = n + 32 - 1;
        array[n2] |= (byte)((publicPoint.data[0] & 0x1) << 7);
    }
    
    private static int encodeResult(final PointAccum pointAccum, final byte[] array, final int n) {
        final PointAffine pointAffine = new PointAffine();
        normalizeToAffine(pointAccum, pointAffine);
        final int checkPoint = checkPoint(pointAffine);
        encodePoint(pointAffine, array, n);
        return checkPoint;
    }
    
    private static PublicPoint exportPoint(final PointAffine pointAffine) {
        final int[] array = new int[20];
        X25519Field.copy(pointAffine.x, 0, array, 0);
        X25519Field.copy(pointAffine.y, 0, array, 10);
        return new PublicPoint(array);
    }
    
    public static void generatePrivateKey(final SecureRandom secureRandom, final byte[] bytes) {
        if (bytes.length != 32) {
            throw new IllegalArgumentException("k");
        }
        secureRandom.nextBytes(bytes);
    }
    
    public static void generatePublicKey(final byte[] array, final int n, final byte[] array2, final int n2) {
        final Digest digest = createDigest();
        final byte[] array3 = new byte[64];
        digest.update(array, n, 32);
        digest.doFinal(array3, 0);
        final byte[] array4 = new byte[32];
        pruneScalar(array3, 0, array4);
        scalarMultBaseEncoded(array4, array2, n2);
    }
    
    public static PublicPoint generatePublicKey(final byte[] array, final int n) {
        final Digest digest = createDigest();
        final byte[] array2 = new byte[64];
        digest.update(array, n, 32);
        digest.doFinal(array2, 0);
        final byte[] array3 = new byte[32];
        pruneScalar(array2, 0, array3);
        final PointAccum pointAccum = new PointAccum();
        scalarMultBase(array3, pointAccum);
        final PointAffine pointAffine = new PointAffine();
        normalizeToAffine(pointAccum, 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 groupCombBits(final int[] array) {
        for (int i = 0; i < array.length; ++i) {
            array[i] = Interleave.shuffle2(array[i]);
        }
    }
    
    private static void implSign(final Digest digest, 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) {
        if (array4 != null) {
            dom2(digest, b, array4);
        }
        digest.update(array, 32, 32);
        digest.update(array5, n2, n3);
        digest.doFinal(array, 0);
        final byte[] reduce512 = Scalar25519.reduce512(array);
        final byte[] array7 = new byte[32];
        scalarMultBaseEncoded(reduce512, array7, 0);
        if (array4 != null) {
            dom2(digest, b, array4);
        }
        digest.update(array7, 0, 32);
        digest.update(array3, n, 32);
        digest.update(array5, n2, n3);
        digest.doFinal(array, 0);
        final byte[] calculateS = calculateS(reduce512, Scalar25519.reduce512(array), array2);
        System.arraycopy(array7, 0, array6, n4, 32);
        System.arraycopy(calculateS, 0, array6, n4 + 32, 32);
    }
    
    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, b)) {
            throw new IllegalArgumentException("ctx");
        }
        final Digest digest = createDigest();
        final byte[] array5 = new byte[64];
        digest.update(array, n, 32);
        digest.doFinal(array5, 0);
        final byte[] array6 = new byte[32];
        pruneScalar(array5, 0, array6);
        final byte[] array7 = new byte[32];
        scalarMultBaseEncoded(array6, array7, 0);
        implSign(digest, 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, b)) {
            throw new IllegalArgumentException("ctx");
        }
        final Digest digest = createDigest();
        final byte[] array6 = new byte[64];
        digest.update(array, n, 32);
        digest.doFinal(array6, 0);
        final byte[] array7 = new byte[32];
        pruneScalar(array6, 0, array7);
        implSign(digest, 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, b)) {
            throw new IllegalArgumentException("ctx");
        }
        final byte[] copy = copy(array, n, 32);
        final byte[] copy2 = copy(array, n + 32, 32);
        final byte[] copy3 = copy(array2, n2, 32);
        if (!checkPointVar(copy)) {
            return false;
        }
        final int[] array5 = new int[8];
        if (!Scalar25519.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 Digest digest = createDigest();
        final byte[] array6 = new byte[64];
        if (array3 != null) {
            dom2(digest, b, array3);
        }
        digest.update(copy, 0, 32);
        digest.update(copy3, 0, 32);
        digest.update(array4, n3, n4);
        digest.doFinal(array6, 0);
        final byte[] reduce512 = Scalar25519.reduce512(array6);
        final int[] array7 = new int[8];
        Scalar25519.decode(reduce512, array7);
        final int[] array8 = new int[4];
        final int[] array9 = new int[4];
        if (!Scalar25519.reduceBasisVar(array7, array8, array9)) {
            throw new IllegalStateException();
        }
        Scalar25519.multiply128Var(array5, array9, array5);
        final PointAccum pointAccum = new PointAccum();
        scalarMultStraus128Var(array5, array8, pointAffine2, array9, pointAffine, pointAccum);
        return normalizeToNeutralElementVar(pointAccum);
    }
    
    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, b)) {
            throw new IllegalArgumentException("ctx");
        }
        final byte[] copy = copy(array, n, 32);
        final byte[] copy2 = copy(array, n + 32, 32);
        if (!checkPointVar(copy)) {
            return false;
        }
        final int[] array4 = new int[8];
        if (!Scalar25519.checkVar(copy2, array4)) {
            return false;
        }
        final PointAffine pointAffine = new PointAffine();
        if (!decodePointVar(copy, true, pointAffine)) {
            return false;
        }
        final PointAffine pointAffine2 = new PointAffine();
        X25519Field.negate(publicPoint.data, pointAffine2.x);
        X25519Field.copy(publicPoint.data, 10, pointAffine2.y, 0);
        final byte[] array5 = new byte[32];
        encodePublicPoint(publicPoint, array5, 0);
        final Digest digest = createDigest();
        final byte[] array6 = new byte[64];
        if (array2 != null) {
            dom2(digest, b, array2);
        }
        digest.update(copy, 0, 32);
        digest.update(array5, 0, 32);
        digest.update(array3, n2, n3);
        digest.doFinal(array6, 0);
        final byte[] reduce512 = Scalar25519.reduce512(array6);
        final int[] array7 = new int[8];
        Scalar25519.decode(reduce512, array7);
        final int[] array8 = new int[4];
        final int[] array9 = new int[4];
        if (!Scalar25519.reduceBasisVar(array7, array8, array9)) {
            throw new IllegalStateException();
        }
        Scalar25519.multiply128Var(array4, array9, array4);
        final PointAccum pointAccum = new PointAccum();
        scalarMultStraus128Var(array4, array8, pointAffine2, array9, pointAffine, pointAccum);
        return normalizeToNeutralElementVar(pointAccum);
    }
    
    private static void invertDoubleZs(final PointExtended[] array) {
        final int length = array.length;
        final int[] table = X25519Field.createTable(length);
        final int[] create = X25519Field.create();
        X25519Field.copy(array[0].z, 0, create, 0);
        X25519Field.copy(create, 0, table, 0);
        int i = 0;
        while (++i < length) {
            X25519Field.mul(create, array[i].z, create);
            X25519Field.copy(create, 0, table, i * 10);
        }
        X25519Field.add(create, create, create);
        X25519Field.invVar(create, create);
        --i;
        final int[] create2 = X25519Field.create();
        while (i > 0) {
            final int n = i--;
            X25519Field.copy(table, i * 10, create2, 0);
            X25519Field.mul(create2, create, create2);
            X25519Field.mul(create, array[n].z, create);
            X25519Field.copy(create2, 0, array[n].z, 0);
        }
        X25519Field.copy(create, 0, array[0].z, 0);
    }
    
    private static void normalizeToAffine(final PointAccum pointAccum, final PointAffine pointAffine) {
        X25519Field.inv(pointAccum.z, pointAffine.y);
        X25519Field.mul(pointAffine.y, pointAccum.x, pointAffine.x);
        X25519Field.mul(pointAffine.y, pointAccum.y, pointAffine.y);
        X25519Field.normalize(pointAffine.x);
        X25519Field.normalize(pointAffine.y);
    }
    
    private static boolean normalizeToNeutralElementVar(final PointAccum pointAccum) {
        X25519Field.normalize(pointAccum.x);
        X25519Field.normalize(pointAccum.y);
        X25519Field.normalize(pointAccum.z);
        return X25519Field.isZeroVar(pointAccum.x) && !X25519Field.isZeroVar(pointAccum.y) && X25519Field.areEqualVar(pointAccum.y, pointAccum.z);
    }
    
    private static void pointAdd(final PointExtended pointExtended, final PointExtended pointExtended2, final PointExtended pointExtended3, final PointTemp pointTemp) {
        final int[] x = pointExtended3.x;
        final int[] y = pointExtended3.y;
        final int[] r0 = pointTemp.r0;
        final int[] r2 = pointTemp.r1;
        final int[] array = x;
        final int[] array2 = r0;
        final int[] array3 = r2;
        final int[] array4 = y;
        X25519Field.apm(pointExtended.y, pointExtended.x, y, x);
        X25519Field.apm(pointExtended2.y, pointExtended2.x, r2, r0);
        X25519Field.mul(x, r0, x);
        X25519Field.mul(y, r2, y);
        X25519Field.mul(pointExtended.t, pointExtended2.t, r0);
        X25519Field.mul(r0, Ed25519.C_d2, r0);
        X25519Field.add(pointExtended.z, pointExtended.z, r2);
        X25519Field.mul(r2, pointExtended2.z, r2);
        X25519Field.apm(y, x, array4, array);
        X25519Field.apm(r2, r0, array3, array2);
        X25519Field.mul(array, array4, pointExtended3.t);
        X25519Field.mul(array2, array3, pointExtended3.z);
        X25519Field.mul(array, array2, pointExtended3.x);
        X25519Field.mul(array4, array3, pointExtended3.y);
    }
    
    private static void pointAdd(final PointPrecomp pointPrecomp, final PointAccum pointAccum, final PointTemp pointTemp) {
        final int[] x = pointAccum.x;
        final int[] y = pointAccum.y;
        final int[] r0 = pointTemp.r0;
        final int[] u = pointAccum.u;
        final int[] array = x;
        final int[] array2 = y;
        final int[] v = pointAccum.v;
        X25519Field.apm(pointAccum.y, pointAccum.x, y, x);
        X25519Field.mul(x, pointPrecomp.ymx_h, x);
        X25519Field.mul(y, pointPrecomp.ypx_h, y);
        X25519Field.mul(pointAccum.u, pointAccum.v, r0);
        X25519Field.mul(r0, pointPrecomp.xyd, r0);
        X25519Field.apm(y, x, v, u);
        X25519Field.apm(pointAccum.z, r0, array2, array);
        X25519Field.mul(array, array2, pointAccum.z);
        X25519Field.mul(array, u, pointAccum.x);
        X25519Field.mul(array2, v, pointAccum.y);
    }
    
    private static void pointAdd(final PointPrecompZ pointPrecompZ, final PointAccum pointAccum, final PointTemp pointTemp) {
        final int[] x = pointAccum.x;
        final int[] y = pointAccum.y;
        final int[] r0 = pointTemp.r0;
        final int[] z = pointAccum.z;
        final int[] u = pointAccum.u;
        final int[] array = x;
        final int[] array2 = y;
        final int[] v = pointAccum.v;
        X25519Field.apm(pointAccum.y, pointAccum.x, y, x);
        X25519Field.mul(x, pointPrecompZ.ymx_h, x);
        X25519Field.mul(y, pointPrecompZ.ypx_h, y);
        X25519Field.mul(pointAccum.u, pointAccum.v, r0);
        X25519Field.mul(r0, pointPrecompZ.xyd, r0);
        X25519Field.mul(pointAccum.z, pointPrecompZ.z, z);
        X25519Field.apm(y, x, v, u);
        X25519Field.apm(z, r0, array2, array);
        X25519Field.mul(array, array2, pointAccum.z);
        X25519Field.mul(array, u, pointAccum.x);
        X25519Field.mul(array2, v, pointAccum.y);
    }
    
    private static void pointAddVar(final boolean b, final PointPrecomp pointPrecomp, final PointAccum pointAccum, final PointTemp pointTemp) {
        final int[] x = pointAccum.x;
        final int[] y = pointAccum.y;
        final int[] r0 = pointTemp.r0;
        final int[] u = pointAccum.u;
        final int[] array = x;
        final int[] array2 = y;
        final int[] v = pointAccum.v;
        int[] array3;
        int[] array4;
        if (b) {
            array3 = y;
            array4 = x;
        }
        else {
            array3 = x;
            array4 = y;
        }
        final int[] array5 = array3;
        final int[] array6 = array4;
        X25519Field.apm(pointAccum.y, pointAccum.x, y, x);
        X25519Field.mul(array3, pointPrecomp.ymx_h, array3);
        X25519Field.mul(array4, pointPrecomp.ypx_h, array4);
        X25519Field.mul(pointAccum.u, pointAccum.v, r0);
        X25519Field.mul(r0, pointPrecomp.xyd, r0);
        X25519Field.apm(y, x, v, u);
        X25519Field.apm(pointAccum.z, r0, array6, array5);
        X25519Field.mul(array, array2, pointAccum.z);
        X25519Field.mul(array, u, pointAccum.x);
        X25519Field.mul(array2, v, pointAccum.y);
    }
    
    private static void pointAddVar(final boolean b, final PointPrecompZ pointPrecompZ, final PointAccum pointAccum, final PointTemp pointTemp) {
        final int[] x = pointAccum.x;
        final int[] y = pointAccum.y;
        final int[] r0 = pointTemp.r0;
        final int[] z = pointAccum.z;
        final int[] u = pointAccum.u;
        final int[] array = x;
        final int[] array2 = y;
        final int[] v = pointAccum.v;
        int[] array3;
        int[] array4;
        if (b) {
            array3 = y;
            array4 = x;
        }
        else {
            array3 = x;
            array4 = y;
        }
        final int[] array5 = array3;
        final int[] array6 = array4;
        X25519Field.apm(pointAccum.y, pointAccum.x, y, x);
        X25519Field.mul(array3, pointPrecompZ.ymx_h, array3);
        X25519Field.mul(array4, pointPrecompZ.ypx_h, array4);
        X25519Field.mul(pointAccum.u, pointAccum.v, r0);
        X25519Field.mul(r0, pointPrecompZ.xyd, r0);
        X25519Field.mul(pointAccum.z, pointPrecompZ.z, z);
        X25519Field.apm(y, x, v, u);
        X25519Field.apm(z, r0, array6, array5);
        X25519Field.mul(array, array2, pointAccum.z);
        X25519Field.mul(array, u, pointAccum.x);
        X25519Field.mul(array2, v, pointAccum.y);
    }
    
    private static void pointCopy(final PointAccum pointAccum, final PointExtended pointExtended) {
        X25519Field.copy(pointAccum.x, 0, pointExtended.x, 0);
        X25519Field.copy(pointAccum.y, 0, pointExtended.y, 0);
        X25519Field.copy(pointAccum.z, 0, pointExtended.z, 0);
        X25519Field.mul(pointAccum.u, pointAccum.v, pointExtended.t);
    }
    
    private static void pointCopy(final PointAffine pointAffine, final PointExtended pointExtended) {
        X25519Field.copy(pointAffine.x, 0, pointExtended.x, 0);
        X25519Field.copy(pointAffine.y, 0, pointExtended.y, 0);
        X25519Field.one(pointExtended.z);
        X25519Field.mul(pointAffine.x, pointAffine.y, pointExtended.t);
    }
    
    private static void pointCopy(final PointExtended pointExtended, final PointPrecompZ pointPrecompZ) {
        X25519Field.apm(pointExtended.y, pointExtended.x, pointPrecompZ.ypx_h, pointPrecompZ.ymx_h);
        X25519Field.mul(pointExtended.t, Ed25519.C_d2, pointPrecompZ.xyd);
        X25519Field.add(pointExtended.z, pointExtended.z, pointPrecompZ.z);
    }
    
    private static void pointDouble(final PointAccum pointAccum) {
        final int[] x = pointAccum.x;
        final int[] y = pointAccum.y;
        final int[] z = pointAccum.z;
        final int[] u = pointAccum.u;
        final int[] array = x;
        final int[] array2 = y;
        final int[] v = pointAccum.v;
        X25519Field.add(pointAccum.x, pointAccum.y, u);
        X25519Field.sqr(pointAccum.x, x);
        X25519Field.sqr(pointAccum.y, y);
        X25519Field.sqr(pointAccum.z, z);
        X25519Field.add(z, z, z);
        X25519Field.apm(x, y, v, array2);
        X25519Field.sqr(u, u);
        X25519Field.sub(v, u, u);
        X25519Field.add(z, array2, array);
        X25519Field.carry(array);
        X25519Field.mul(array, array2, pointAccum.z);
        X25519Field.mul(array, u, pointAccum.x);
        X25519Field.mul(array2, v, pointAccum.y);
    }
    
    private static void pointLookup(final int n, final int n2, final PointPrecomp pointPrecomp) {
        int n3 = n * 8 * 3 * 10;
        for (int i = 0; i < 8; ++i) {
            final int n4 = (i ^ n2) - 1 >> 31;
            X25519Field.cmov(n4, Ed25519.PRECOMP_BASE_COMB, n3, pointPrecomp.ymx_h, 0);
            n3 += 10;
            X25519Field.cmov(n4, Ed25519.PRECOMP_BASE_COMB, n3, pointPrecomp.ypx_h, 0);
            n3 += 10;
            X25519Field.cmov(n4, Ed25519.PRECOMP_BASE_COMB, n3, pointPrecomp.xyd, 0);
            n3 += 10;
        }
    }
    
    private static void pointLookupZ(final int[] array, final int n, final int[] array2, final PointPrecompZ pointPrecompZ) {
        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;
            X25519Field.cmov(n5, array2, n4, pointPrecompZ.ymx_h, 0);
            n4 += 10;
            X25519Field.cmov(n5, array2, n4, pointPrecompZ.ypx_h, 0);
            n4 += 10;
            X25519Field.cmov(n5, array2, n4, pointPrecompZ.xyd, 0);
            n4 += 10;
            X25519Field.cmov(n5, array2, n4, pointPrecompZ.z, 0);
            n4 += 10;
            ++i;
        }
        X25519Field.cswap(n2, pointPrecompZ.ymx_h, pointPrecompZ.ypx_h);
        X25519Field.cnegate(n2, pointPrecompZ.xyd);
    }
    
    private static void pointPrecompute(final PointAffine pointAffine, final PointExtended[] array, final int n, final int n2, final PointTemp pointTemp) {
        pointCopy(pointAffine, array[n] = new PointExtended());
        final PointExtended pointExtended = new PointExtended();
        pointAdd(array[n], array[n], pointExtended, pointTemp);
        for (int i = 1; i < n2; ++i) {
            pointAdd(array[n + i - 1], pointExtended, array[n + i] = new PointExtended(), pointTemp);
        }
    }
    
    private static int[] pointPrecomputeZ(final PointAffine pointAffine, final int n, final PointTemp pointTemp) {
        final PointExtended pointExtended = new PointExtended();
        pointCopy(pointAffine, pointExtended);
        final PointExtended pointExtended2 = new PointExtended();
        pointAdd(pointExtended, pointExtended, pointExtended2, pointTemp);
        final PointPrecompZ pointPrecompZ = new PointPrecompZ();
        final int[] table = X25519Field.createTable(n * 4);
        int n2 = 0;
        int n3 = 0;
        while (true) {
            pointCopy(pointExtended, pointPrecompZ);
            X25519Field.copy(pointPrecompZ.ymx_h, 0, table, n2);
            n2 += 10;
            X25519Field.copy(pointPrecompZ.ypx_h, 0, table, n2);
            n2 += 10;
            X25519Field.copy(pointPrecompZ.xyd, 0, table, n2);
            n2 += 10;
            X25519Field.copy(pointPrecompZ.z, 0, table, n2);
            n2 += 10;
            if (++n3 == n) {
                break;
            }
            pointAdd(pointExtended, pointExtended2, pointExtended, pointTemp);
        }
        return table;
    }
    
    private static void pointPrecomputeZ(final PointAffine pointAffine, final PointPrecompZ[] array, final int n, final PointTemp pointTemp) {
        final PointExtended pointExtended = new PointExtended();
        pointCopy(pointAffine, pointExtended);
        final PointExtended pointExtended2 = new PointExtended();
        pointAdd(pointExtended, pointExtended, pointExtended2, pointTemp);
        int n2 = 0;
        while (true) {
            pointCopy(pointExtended, array[n2] = new PointPrecompZ());
            if (++n2 == n) {
                break;
            }
            pointAdd(pointExtended, pointExtended2, pointExtended, pointTemp);
        }
    }
    
    private static void pointSetNeutral(final PointAccum pointAccum) {
        X25519Field.zero(pointAccum.x);
        X25519Field.one(pointAccum.y);
        X25519Field.one(pointAccum.z);
        X25519Field.zero(pointAccum.u);
        X25519Field.one(pointAccum.v);
    }
    
    public static void precompute() {
        synchronized (Ed25519.PRECOMP_LOCK) {
            if (Ed25519.PRECOMP_BASE_COMB != null) {
                return;
            }
            final int n = 16;
            final int n2 = 64;
            final int n3 = n * 2 + n2;
            final PointExtended[] array = new PointExtended[n3];
            final PointTemp pointTemp = new PointTemp();
            final PointAffine pointAffine = new PointAffine();
            X25519Field.copy(Ed25519.B_x, 0, pointAffine.x, 0);
            X25519Field.copy(Ed25519.B_y, 0, pointAffine.y, 0);
            pointPrecompute(pointAffine, array, 0, n, pointTemp);
            final PointAffine pointAffine2 = new PointAffine();
            X25519Field.copy(Ed25519.B128_x, 0, pointAffine2.x, 0);
            X25519Field.copy(Ed25519.B128_y, 0, pointAffine2.y, 0);
            pointPrecompute(pointAffine2, array, n, n, pointTemp);
            final PointAccum pointAccum = new PointAccum();
            X25519Field.copy(Ed25519.B_x, 0, pointAccum.x, 0);
            X25519Field.copy(Ed25519.B_y, 0, pointAccum.y, 0);
            X25519Field.one(pointAccum.z);
            X25519Field.copy(pointAccum.x, 0, pointAccum.u, 0);
            X25519Field.copy(pointAccum.y, 0, pointAccum.v, 0);
            int n4 = n * 2;
            final PointExtended[] array2 = new PointExtended[4];
            for (int i = 0; i < 4; ++i) {
                array2[i] = new PointExtended();
            }
            final PointExtended pointExtended = new PointExtended();
            for (int j = 0; j < 8; ++j) {
                final PointExtended[] array3 = array;
                final int n5 = n4++;
                final PointExtended pointExtended2 = new PointExtended();
                array3[n5] = pointExtended2;
                final PointExtended pointExtended3 = pointExtended2;
                for (int k = 0; k < 4; ++k) {
                    if (k == 0) {
                        pointCopy(pointAccum, pointExtended3);
                    }
                    else {
                        pointCopy(pointAccum, pointExtended);
                        pointAdd(pointExtended3, pointExtended, pointExtended3, pointTemp);
                    }
                    pointDouble(pointAccum);
                    pointCopy(pointAccum, array2[k]);
                    if (j + k != 10) {
                        for (int l = 1; l < 8; ++l) {
                            pointDouble(pointAccum);
                        }
                    }
                }
                X25519Field.negate(pointExtended3.x, pointExtended3.x);
                X25519Field.negate(pointExtended3.t, pointExtended3.t);
                for (int n6 = 0; n6 < 3; ++n6) {
                    for (int n7 = 1 << n6, n8 = 0; n8 < n7; ++n8, ++n4) {
                        array[n4] = new PointExtended();
                        pointAdd(array[n4 - n7], array2[n6], array[n4], pointTemp);
                    }
                }
            }
            invertDoubleZs(array);
            Ed25519.PRECOMP_BASE_WNAF = new PointPrecomp[n];
            for (int n9 = 0; n9 < n; ++n9) {
                final PointExtended pointExtended4 = array[n9];
                final PointPrecomp[] precomp_BASE_WNAF = Ed25519.PRECOMP_BASE_WNAF;
                final int n10 = n9;
                final PointPrecomp pointPrecomp = new PointPrecomp();
                precomp_BASE_WNAF[n10] = pointPrecomp;
                final PointPrecomp pointPrecomp2 = pointPrecomp;
                X25519Field.mul(pointExtended4.x, pointExtended4.z, pointExtended4.x);
                X25519Field.mul(pointExtended4.y, pointExtended4.z, pointExtended4.y);
                X25519Field.apm(pointExtended4.y, pointExtended4.x, pointPrecomp2.ypx_h, pointPrecomp2.ymx_h);
                X25519Field.mul(pointExtended4.x, pointExtended4.y, pointPrecomp2.xyd);
                X25519Field.mul(pointPrecomp2.xyd, Ed25519.C_d4, pointPrecomp2.xyd);
                X25519Field.normalize(pointPrecomp2.ymx_h);
                X25519Field.normalize(pointPrecomp2.ypx_h);
                X25519Field.normalize(pointPrecomp2.xyd);
            }
            Ed25519.PRECOMP_BASE128_WNAF = new PointPrecomp[n];
            for (int n11 = 0; n11 < n; ++n11) {
                final PointExtended pointExtended5 = array[n + n11];
                final PointPrecomp[] precomp_BASE128_WNAF = Ed25519.PRECOMP_BASE128_WNAF;
                final int n12 = n11;
                final PointPrecomp pointPrecomp3 = new PointPrecomp();
                precomp_BASE128_WNAF[n12] = pointPrecomp3;
                final PointPrecomp pointPrecomp4 = pointPrecomp3;
                X25519Field.mul(pointExtended5.x, pointExtended5.z, pointExtended5.x);
                X25519Field.mul(pointExtended5.y, pointExtended5.z, pointExtended5.y);
                X25519Field.apm(pointExtended5.y, pointExtended5.x, pointPrecomp4.ypx_h, pointPrecomp4.ymx_h);
                X25519Field.mul(pointExtended5.x, pointExtended5.y, pointPrecomp4.xyd);
                X25519Field.mul(pointPrecomp4.xyd, Ed25519.C_d4, pointPrecomp4.xyd);
                X25519Field.normalize(pointPrecomp4.ymx_h);
                X25519Field.normalize(pointPrecomp4.ypx_h);
                X25519Field.normalize(pointPrecomp4.xyd);
            }
            Ed25519.PRECOMP_BASE_COMB = X25519Field.createTable(n2 * 3);
            final PointPrecomp pointPrecomp5 = new PointPrecomp();
            int n13 = 0;
            for (int n14 = n * 2; n14 < n3; ++n14) {
                final PointExtended pointExtended6 = array[n14];
                X25519Field.mul(pointExtended6.x, pointExtended6.z, pointExtended6.x);
                X25519Field.mul(pointExtended6.y, pointExtended6.z, pointExtended6.y);
                X25519Field.apm(pointExtended6.y, pointExtended6.x, pointPrecomp5.ypx_h, pointPrecomp5.ymx_h);
                X25519Field.mul(pointExtended6.x, pointExtended6.y, pointPrecomp5.xyd);
                X25519Field.mul(pointPrecomp5.xyd, Ed25519.C_d4, pointPrecomp5.xyd);
                X25519Field.normalize(pointPrecomp5.ymx_h);
                X25519Field.normalize(pointPrecomp5.ypx_h);
                X25519Field.normalize(pointPrecomp5.xyd);
                X25519Field.copy(pointPrecomp5.ymx_h, 0, Ed25519.PRECOMP_BASE_COMB, n13);
                n13 += 10;
                X25519Field.copy(pointPrecomp5.ypx_h, 0, Ed25519.PRECOMP_BASE_COMB, n13);
                n13 += 10;
                X25519Field.copy(pointPrecomp5.xyd, 0, Ed25519.PRECOMP_BASE_COMB, n13);
                n13 += 10;
            }
        }
    }
    
    private static void pruneScalar(final byte[] array, final int n, final byte[] array2) {
        System.arraycopy(array, n, array2, 0, 32);
        final int n2 = 0;
        array2[n2] &= (byte)248;
        final int n3 = 31;
        array2[n3] &= 0x7F;
        final int n4 = 31;
        array2[n4] |= 0x40;
    }
    
    private static void scalarMult(final byte[] array, final PointAffine pointAffine, final PointAccum pointAccum) {
        final int[] array2 = new int[8];
        Scalar25519.decode(array, array2);
        Scalar25519.toSignedDigits(256, array2);
        final PointPrecompZ pointPrecompZ = new PointPrecompZ();
        final PointTemp pointTemp = new PointTemp();
        final int[] pointPrecomputeZ = pointPrecomputeZ(pointAffine, 8, pointTemp);
        pointSetNeutral(pointAccum);
        int n = 63;
        while (true) {
            pointLookupZ(array2, n, pointPrecomputeZ, pointPrecompZ);
            pointAdd(pointPrecompZ, pointAccum, pointTemp);
            if (--n < 0) {
                break;
            }
            for (int i = 0; i < 4; ++i) {
                pointDouble(pointAccum);
            }
        }
    }
    
    private static void scalarMultBase(final byte[] array, final PointAccum pointAccum) {
        precompute();
        final int[] array2 = new int[8];
        Scalar25519.decode(array, array2);
        Scalar25519.toSignedDigits(256, array2);
        groupCombBits(array2);
        final PointPrecomp pointPrecomp = new PointPrecomp();
        final PointTemp pointTemp = new PointTemp();
        pointSetNeutral(pointAccum);
        int n = 0;
        int n2 = 28;
        while (true) {
            for (int i = 0; i < 8; ++i) {
                final int n3 = array2[i] >>> n2;
                final int n4 = n3 >>> 3 & 0x1;
                pointLookup(i, (n3 ^ -n4) & 0x7, pointPrecomp);
                X25519Field.cnegate(n ^ n4, pointAccum.x);
                X25519Field.cnegate(n ^ n4, pointAccum.u);
                n = n4;
                pointAdd(pointPrecomp, pointAccum, pointTemp);
            }
            n2 -= 4;
            if (n2 < 0) {
                break;
            }
            pointDouble(pointAccum);
        }
        X25519Field.cnegate(n, pointAccum.x);
        X25519Field.cnegate(n, pointAccum.u);
    }
    
    private static void scalarMultBaseEncoded(final byte[] array, final byte[] array2, final int n) {
        final PointAccum pointAccum = new PointAccum();
        scalarMultBase(array, pointAccum);
        if (0 == encodeResult(pointAccum, array2, n)) {
            throw new IllegalStateException();
        }
    }
    
    public static void scalarMultBaseYZ(final X25519.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 X25519");
        }
        final byte[] array4 = new byte[32];
        pruneScalar(array, n, array4);
        final PointAccum pointAccum = new PointAccum();
        scalarMultBase(array4, pointAccum);
        if (0 == checkPoint(pointAccum)) {
            throw new IllegalStateException();
        }
        X25519Field.copy(pointAccum.y, 0, array2, 0);
        X25519Field.copy(pointAccum.z, 0, array3, 0);
    }
    
    private static void scalarMultOrderVar(final PointAffine pointAffine, final PointAccum pointAccum) {
        final byte[] array = new byte[253];
        Scalar25519.getOrderWnafVar(4, array);
        final int n = 4;
        final PointPrecompZ[] array2 = new PointPrecompZ[n];
        final PointTemp pointTemp = new PointTemp();
        pointPrecomputeZ(pointAffine, array2, n, pointTemp);
        pointSetNeutral(pointAccum);
        int n2 = 252;
        while (true) {
            final byte b = array[n2];
            if (b != 0) {
                pointAddVar(b < 0, array2[b >> 1 ^ b >> 31], pointAccum, pointTemp);
            }
            if (--n2 < 0) {
                break;
            }
            pointDouble(pointAccum);
        }
    }
    
    private static void scalarMultStraus128Var(final int[] array, final int[] array2, final PointAffine pointAffine, final int[] array3, final PointAffine pointAffine2, final PointAccum pointAccum) {
        precompute();
        final byte[] array4 = new byte[256];
        final byte[] array5 = new byte[128];
        final byte[] array6 = new byte[128];
        Wnaf.getSignedVar(array, 6, array4);
        Wnaf.getSignedVar(array2, 4, array5);
        Wnaf.getSignedVar(array3, 4, array6);
        final int n = 4;
        final PointPrecompZ[] array7 = new PointPrecompZ[n];
        final PointPrecompZ[] array8 = new PointPrecompZ[n];
        final PointTemp pointTemp = new PointTemp();
        pointPrecomputeZ(pointAffine, array7, n, pointTemp);
        pointPrecomputeZ(pointAffine2, array8, n, pointTemp);
        pointSetNeutral(pointAccum);
        int i = 128;
        while (--i >= 0 && (array4[i] | array4[128 + i] | array5[i] | array6[i]) == 0x0) {}
        while (i >= 0) {
            final byte b = array4[i];
            if (b != 0) {
                pointAddVar(b < 0, Ed25519.PRECOMP_BASE_WNAF[b >> 1 ^ b >> 31], pointAccum, pointTemp);
            }
            final byte b2 = array4[128 + i];
            if (b2 != 0) {
                pointAddVar(b2 < 0, Ed25519.PRECOMP_BASE128_WNAF[b2 >> 1 ^ b2 >> 31], pointAccum, pointTemp);
            }
            final byte b3 = array5[i];
            if (b3 != 0) {
                pointAddVar(b3 < 0, array7[b3 >> 1 ^ b3 >> 31], pointAccum, pointTemp);
            }
            final byte b4 = array6[i];
            if (b4 != 0) {
                pointAddVar(b4 < 0, array8[b4 >> 1 ^ b4 >> 31], pointAccum, pointTemp);
            }
            pointDouble(pointAccum);
            --i;
        }
        pointDouble(pointAccum);
        pointDouble(pointAccum);
    }
    
    public static void sign(final byte[] array, final int n, final byte[] array2, final int n2, final int n3, final byte[] array3, final int n4) {
        implSign(array, n, null, (byte)0, array2, n2, n3, array3, n4);
    }
    
    public static void sign(final byte[] array, final int n, final byte[] array2, final int n2, final byte[] array3, final int n3, final int n4, final byte[] array4, final int n5) {
        implSign(array, n, array2, n2, null, (byte)0, array3, n3, n4, array4, n5);
    }
    
    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 Digest digest, final byte[] array3, final int n2) {
        final byte[] array4 = new byte[64];
        if (64 != digest.doFinal(array4, 0)) {
            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 Digest digest, final byte[] array4, final int n3) {
        final byte[] array5 = new byte[64];
        if (64 != digest.doFinal(array5, 0)) {
            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, 32);
        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, 32);
        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, 32);
        return checkPointFullVar(copy) && decodePointVar(copy, false, new PointAffine());
    }
    
    public static PublicPoint validatePublicKeyPartialExport(final byte[] array, final int n) {
        final byte[] copy = copy(array, n, 32);
        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 int n3, final int n4) {
        return implVerify(array, n, array2, n2, null, (byte)0, array3, n3, n4);
    }
    
    public static boolean verify(final byte[] array, final int n, final PublicPoint publicPoint, final byte[] array2, final int n2, final int n3) {
        return implVerify(array, n, publicPoint, null, (byte)0, array2, n2, n3);
    }
    
    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 Digest digest) {
        final byte[] array4 = new byte[64];
        if (64 != digest.doFinal(array4, 0)) {
            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 Digest digest) {
        final byte[] array3 = new byte[64];
        if (64 != digest.doFinal(array3, 0)) {
            throw new IllegalArgumentException("ph");
        }
        return implVerify(array, n, publicPoint, array2, (byte)1, array3, 0, array3.length);
    }
    
    static {
        DOM2_PREFIX = new byte[] { 83, 105, 103, 69, 100, 50, 53, 53, 49, 57, 32, 110, 111, 32, 69, 100, 50, 53, 53, 49, 57, 32, 99, 111, 108, 108, 105, 115, 105, 111, 110, 115 };
        P = new int[] { -19, -1, -1, -1, -1, -1, -1, Integer.MAX_VALUE };
        ORDER8_y1 = new int[] { 1886001095, 1339575613, 1980447930, 258412557, -95215574, -959694548, 2013120334, 2047061138 };
        ORDER8_y2 = new int[] { -1886001114, -1339575614, -1980447931, -258412558, 95215573, 959694547, -2013120335, 100422509 };
        B_x = new int[] { 52811034, 25909283, 8072341, 50637101, 13785486, 30858332, 20483199, 20966410, 43936626, 4379245 };
        B_y = new int[] { 40265304, 26843545, 6710886, 53687091, 13421772, 40265318, 26843545, 6710886, 53687091, 13421772 };
        B128_x = new int[] { 12052516, 1174424, 4087752, 38672185, 20040971, 21899680, 55468344, 20105554, 66708015, 9981791 };
        B128_y = new int[] { 66430571, 45040722, 4842939, 15895846, 18981244, 46308410, 4697481, 8903007, 53646190, 12474675 };
        C_d = new int[] { 56195235, 47411844, 25868126, 40503822, 57364, 58321048, 30416477, 31930572, 57760639, 10749657 };
        C_d2 = new int[] { 45281625, 27714825, 18181821, 13898781, 114729, 49533232, 60832955, 30306712, 48412415, 4722099 };
        C_d4 = new int[] { 23454386, 55429651, 2809210, 27797563, 229458, 31957600, 54557047, 27058993, 29715967, 9444199 };
        PRECOMP_LOCK = new Object();
        Ed25519.PRECOMP_BASE_WNAF = null;
        Ed25519.PRECOMP_BASE128_WNAF = null;
        Ed25519.PRECOMP_BASE_COMB = null;
    }
    
    public static final class Algorithm
    {
        public static final int Ed25519 = 0;
        public static final int Ed25519ctx = 1;
        public static final int Ed25519ph = 2;
    }
    
    private static class F extends X25519Field
    {
    }
    
    private static class PointAccum
    {
        int[] x;
        int[] y;
        int[] z;
        int[] u;
        int[] v;
        
        private PointAccum() {
            this.x = X25519Field.create();
            this.y = X25519Field.create();
            this.z = X25519Field.create();
            this.u = X25519Field.create();
            this.v = X25519Field.create();
        }
    }
    
    private static class PointAffine
    {
        int[] x;
        int[] y;
        
        private PointAffine() {
            this.x = X25519Field.create();
            this.y = X25519Field.create();
        }
    }
    
    private static class PointExtended
    {
        int[] x;
        int[] y;
        int[] z;
        int[] t;
        
        private PointExtended() {
            this.x = X25519Field.create();
            this.y = X25519Field.create();
            this.z = X25519Field.create();
            this.t = X25519Field.create();
        }
    }
    
    private static class PointPrecomp
    {
        int[] ymx_h;
        int[] ypx_h;
        int[] xyd;
        
        private PointPrecomp() {
            this.ymx_h = X25519Field.create();
            this.ypx_h = X25519Field.create();
            this.xyd = X25519Field.create();
        }
    }
    
    private static class PointPrecompZ
    {
        int[] ymx_h;
        int[] ypx_h;
        int[] xyd;
        int[] z;
        
        private PointPrecompZ() {
            this.ymx_h = X25519Field.create();
            this.ypx_h = X25519Field.create();
            this.xyd = X25519Field.create();
            this.z = X25519Field.create();
        }
    }
    
    private static class PointTemp
    {
        int[] r0;
        int[] r1;
        
        private PointTemp() {
            this.r0 = X25519Field.create();
            this.r1 = X25519Field.create();
        }
    }
    
    public static final class PublicPoint
    {
        final int[] data;
        
        PublicPoint(final int[] data) {
            this.data = data;
        }
    }
}
