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

package com.google.crypto.tink.internal;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.crypto.tink.subtle.Bytes;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.subtle.EngineFactory;
import java.security.MessageDigest;
import java.util.Arrays;

public final class Ed25519
{
    public static final int SECRET_KEY_LEN = 32;
    public static final int PUBLIC_KEY_LEN = 32;
    public static final int SIGNATURE_LEN = 64;
    private static final CachedXYT CACHED_NEUTRAL;
    private static final PartialXYZT NEUTRAL;
    static final byte[] GROUP_ORDER;
    
    private static void add(final PartialXYZT partialXYZT, final XYZT extended, final CachedXYT cached) {
        final long[] t = new long[10];
        Field25519.sum(partialXYZT.xyz.x, extended.xyz.y, extended.xyz.x);
        Field25519.sub(partialXYZT.xyz.y, extended.xyz.y, extended.xyz.x);
        Field25519.mult(partialXYZT.xyz.y, partialXYZT.xyz.y, cached.yMinusX);
        Field25519.mult(partialXYZT.xyz.z, partialXYZT.xyz.x, cached.yPlusX);
        Field25519.mult(partialXYZT.t, extended.t, cached.t2d);
        cached.multByZ(partialXYZT.xyz.x, extended.xyz.z);
        Field25519.sum(t, partialXYZT.xyz.x, partialXYZT.xyz.x);
        Field25519.sub(partialXYZT.xyz.x, partialXYZT.xyz.z, partialXYZT.xyz.y);
        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.y);
        Field25519.sum(partialXYZT.xyz.z, t, partialXYZT.t);
        Field25519.sub(partialXYZT.t, t, partialXYZT.t);
    }
    
    private static void sub(final PartialXYZT partialXYZT, final XYZT extended, final CachedXYT cached) {
        final long[] t = new long[10];
        Field25519.sum(partialXYZT.xyz.x, extended.xyz.y, extended.xyz.x);
        Field25519.sub(partialXYZT.xyz.y, extended.xyz.y, extended.xyz.x);
        Field25519.mult(partialXYZT.xyz.y, partialXYZT.xyz.y, cached.yPlusX);
        Field25519.mult(partialXYZT.xyz.z, partialXYZT.xyz.x, cached.yMinusX);
        Field25519.mult(partialXYZT.t, extended.t, cached.t2d);
        cached.multByZ(partialXYZT.xyz.x, extended.xyz.z);
        Field25519.sum(t, partialXYZT.xyz.x, partialXYZT.xyz.x);
        Field25519.sub(partialXYZT.xyz.x, partialXYZT.xyz.z, partialXYZT.xyz.y);
        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.y);
        Field25519.sub(partialXYZT.xyz.z, t, partialXYZT.t);
        Field25519.sum(partialXYZT.t, t, partialXYZT.t);
    }
    
    private static void doubleXYZ(final PartialXYZT partialXYZT, final XYZ p) {
        final long[] t0 = new long[10];
        Field25519.square(partialXYZT.xyz.x, p.x);
        Field25519.square(partialXYZT.xyz.z, p.y);
        Field25519.square(partialXYZT.t, p.z);
        Field25519.sum(partialXYZT.t, partialXYZT.t, partialXYZT.t);
        Field25519.sum(partialXYZT.xyz.y, p.x, p.y);
        Field25519.square(t0, partialXYZT.xyz.y);
        Field25519.sum(partialXYZT.xyz.y, partialXYZT.xyz.z, partialXYZT.xyz.x);
        Field25519.sub(partialXYZT.xyz.z, partialXYZT.xyz.z, partialXYZT.xyz.x);
        Field25519.sub(partialXYZT.xyz.x, t0, partialXYZT.xyz.y);
        Field25519.sub(partialXYZT.t, partialXYZT.t, partialXYZT.xyz.z);
    }
    
    private static void doubleXYZT(final PartialXYZT partialXYZT, final XYZT p) {
        doubleXYZ(partialXYZT, p.xyz);
    }
    
    private static int eq(final int a, final int b) {
        int r = ~(a ^ b) & 0xFF;
        r &= r << 4;
        r &= r << 2;
        r &= r << 1;
        return r >> 7 & 0x1;
    }
    
    private static void select(final CachedXYT t, final int pos, final byte b) {
        final int bnegative = (b & 0xFF) >> 7;
        final int babs = b - ((-bnegative & b) << 1);
        t.copyConditional(Ed25519Constants.B_TABLE[pos][0], eq(babs, 1));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][1], eq(babs, 2));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][2], eq(babs, 3));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][3], eq(babs, 4));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][4], eq(babs, 5));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][5], eq(babs, 6));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][6], eq(babs, 7));
        t.copyConditional(Ed25519Constants.B_TABLE[pos][7], eq(babs, 8));
        final long[] yPlusX = Arrays.copyOf(t.yMinusX, 10);
        final long[] yMinusX = Arrays.copyOf(t.yPlusX, 10);
        final long[] t2d = Arrays.copyOf(t.t2d, 10);
        neg(t2d, t2d);
        final CachedXYT minust = new CachedXYT(yPlusX, yMinusX, t2d);
        t.copyConditional(minust, bnegative);
    }
    
    private static XYZ scalarMultWithBase(final byte[] a) {
        final byte[] e = new byte[64];
        for (int i = 0; i < 32; ++i) {
            e[2 * i + 0] = (byte)((a[i] & 0xFF) >> 0 & 0xF);
            e[2 * i + 1] = (byte)((a[i] & 0xFF) >> 4 & 0xF);
        }
        int carry = 0;
        for (int j = 0; j < e.length - 1; ++j) {
            final byte[] array = e;
            final int n = j;
            array[n] += (byte)carry;
            carry = e[j] + 8;
            carry >>= 4;
            final byte[] array2 = e;
            final int n2 = j;
            array2[n2] -= (byte)(carry << 4);
        }
        final byte[] array3 = e;
        final int n3 = e.length - 1;
        array3[n3] += (byte)carry;
        final PartialXYZT ret = new PartialXYZT(Ed25519.NEUTRAL);
        final XYZT xyzt = new XYZT();
        for (int k = 1; k < e.length; k += 2) {
            final CachedXYT t = new CachedXYT(Ed25519.CACHED_NEUTRAL);
            select(t, k / 2, e[k]);
            add(ret, fromPartialXYZT(xyzt, ret), t);
        }
        final XYZ xyz = new XYZ();
        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));
        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));
        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));
        doubleXYZ(ret, XYZ.fromPartialXYZT(xyz, ret));
        for (int l = 0; l < e.length; l += 2) {
            final CachedXYT t2 = new CachedXYT(Ed25519.CACHED_NEUTRAL);
            select(t2, l / 2, e[l]);
            add(ret, fromPartialXYZT(xyzt, ret), t2);
        }
        final XYZ result = new XYZ(ret);
        if (!result.isOnCurve()) {
            throw new IllegalStateException("arithmetic error in scalar multiplication");
        }
        return result;
    }
    
    public static byte[] scalarMultWithBaseToBytes(final byte[] a) {
        return scalarMultWithBase(a).toBytes();
    }
    
    private static byte[] slide(final byte[] a) {
        final byte[] r = new byte[256];
        for (int i = 0; i < 256; ++i) {
            r[i] = (byte)(0x1 & (a[i >> 3] & 0xFF) >> (i & 0x7));
        }
        for (int i = 0; i < 256; ++i) {
            if (r[i] != 0) {
                for (int b = 1; b <= 6 && i + b < 256; ++b) {
                    if (r[i + b] != 0) {
                        if (r[i] + (r[i + b] << b) <= 15) {
                            final byte[] array = r;
                            final int n = i;
                            array[n] += (byte)(r[i + b] << b);
                            r[i + b] = 0;
                        }
                        else {
                            if (r[i] - (r[i + b] << b) < -15) {
                                break;
                            }
                            final byte[] array2 = r;
                            final int n2 = i;
                            array2[n2] -= (byte)(r[i + b] << b);
                            for (int k = i + b; k < 256; ++k) {
                                if (r[k] == 0) {
                                    r[k] = 1;
                                    break;
                                }
                                r[k] = 0;
                            }
                        }
                    }
                }
            }
        }
        return r;
    }
    
    private static XYZ doubleScalarMultVarTime(final byte[] a, final XYZT pointA, final byte[] b) {
        final CachedXYZT[] pointAArray = new CachedXYZT[8];
        pointAArray[0] = new CachedXYZT(pointA);
        PartialXYZT t = new PartialXYZT();
        doubleXYZT(t, pointA);
        final XYZT doubleA = new XYZT(t);
        for (int i = 1; i < pointAArray.length; ++i) {
            add(t, doubleA, pointAArray[i - 1]);
            pointAArray[i] = new CachedXYZT(new XYZT(t));
        }
        final byte[] aSlide = slide(a);
        final byte[] bSlide = slide(b);
        t = new PartialXYZT(Ed25519.NEUTRAL);
        final XYZT u = new XYZT();
        int j;
        for (j = 255; j >= 0 && aSlide[j] == 0; --j) {
            if (bSlide[j] != 0) {
                break;
            }
        }
        while (j >= 0) {
            doubleXYZ(t, new XYZ(t));
            if (aSlide[j] > 0) {
                add(t, fromPartialXYZT(u, t), pointAArray[aSlide[j] / 2]);
            }
            else if (aSlide[j] < 0) {
                sub(t, fromPartialXYZT(u, t), pointAArray[-aSlide[j] / 2]);
            }
            if (bSlide[j] > 0) {
                add(t, fromPartialXYZT(u, t), Ed25519Constants.B2[bSlide[j] / 2]);
            }
            else if (bSlide[j] < 0) {
                sub(t, fromPartialXYZT(u, t), Ed25519Constants.B2[-bSlide[j] / 2]);
            }
            --j;
        }
        return new XYZ(t);
    }
    
    private static boolean isNonZeroVarTime(final long[] in) {
        final long[] inCopy = new long[in.length + 1];
        System.arraycopy(in, 0, inCopy, 0, in.length);
        Field25519.reduceCoefficients(inCopy);
        final byte[] contract;
        final byte[] bytes = contract = Field25519.contract(inCopy);
        for (final byte b : contract) {
            if (b != 0) {
                return true;
            }
        }
        return false;
    }
    
    private static int getLsb(final long[] in) {
        return Field25519.contract(in)[0] & 0x1;
    }
    
    private static void neg(final long[] out, final long[] in) {
        for (int i = 0; i < in.length; ++i) {
            out[i] = -in[i];
        }
    }
    
    private static void pow2252m3(final long[] out, final long[] in) {
        final long[] t0 = new long[10];
        final long[] t2 = new long[10];
        final long[] t3 = new long[10];
        Field25519.square(t0, in);
        Field25519.square(t2, t0);
        for (int i = 1; i < 2; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t2, in, t2);
        Field25519.mult(t0, t0, t2);
        Field25519.square(t0, t0);
        Field25519.mult(t0, t2, t0);
        Field25519.square(t2, t0);
        for (int i = 1; i < 5; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t0, t2, t0);
        Field25519.square(t2, t0);
        for (int i = 1; i < 10; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t2, t2, t0);
        Field25519.square(t3, t2);
        for (int i = 1; i < 20; ++i) {
            Field25519.square(t3, t3);
        }
        Field25519.mult(t2, t3, t2);
        Field25519.square(t2, t2);
        for (int i = 1; i < 10; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t0, t2, t0);
        Field25519.square(t2, t0);
        for (int i = 1; i < 50; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t2, t2, t0);
        Field25519.square(t3, t2);
        for (int i = 1; i < 100; ++i) {
            Field25519.square(t3, t3);
        }
        Field25519.mult(t2, t3, t2);
        Field25519.square(t2, t2);
        for (int i = 1; i < 50; ++i) {
            Field25519.square(t2, t2);
        }
        Field25519.mult(t0, t2, t0);
        Field25519.square(t0, t0);
        for (int i = 1; i < 2; ++i) {
            Field25519.square(t0, t0);
        }
        Field25519.mult(out, t0, in);
    }
    
    private static long load3(final byte[] in, final int idx) {
        long result = (long)in[idx] & 0xFFL;
        result |= (long)(in[idx + 1] & 0xFF) << 8;
        result |= (long)(in[idx + 2] & 0xFF) << 16;
        return result;
    }
    
    private static long load4(final byte[] in, final int idx) {
        long result = load3(in, idx);
        result |= (long)(in[idx + 3] & 0xFF) << 24;
        return result;
    }
    
    private static void reduce(final byte[] s) {
        long s2 = 0x1FFFFFL & load3(s, 0);
        long s3 = 0x1FFFFFL & load4(s, 2) >> 5;
        long s4 = 0x1FFFFFL & load3(s, 5) >> 2;
        long s5 = 0x1FFFFFL & load4(s, 7) >> 7;
        long s6 = 0x1FFFFFL & load4(s, 10) >> 4;
        long s7 = 0x1FFFFFL & load3(s, 13) >> 1;
        long s8 = 0x1FFFFFL & load4(s, 15) >> 6;
        long s9 = 0x1FFFFFL & load3(s, 18) >> 3;
        long s10 = 0x1FFFFFL & load3(s, 21);
        long s11 = 0x1FFFFFL & load4(s, 23) >> 5;
        long s12 = 0x1FFFFFL & load3(s, 26) >> 2;
        long s13 = 0x1FFFFFL & load4(s, 28) >> 7;
        long s14 = 0x1FFFFFL & load4(s, 31) >> 4;
        long s15 = 0x1FFFFFL & load3(s, 34) >> 1;
        long s16 = 0x1FFFFFL & load4(s, 36) >> 6;
        long s17 = 0x1FFFFFL & load3(s, 39) >> 3;
        long s18 = 0x1FFFFFL & load3(s, 42);
        long s19 = 0x1FFFFFL & load4(s, 44) >> 5;
        final long s20 = 0x1FFFFFL & load3(s, 47) >> 2;
        final long s21 = 0x1FFFFFL & load4(s, 49) >> 7;
        final long s22 = 0x1FFFFFL & load4(s, 52) >> 4;
        final long s23 = 0x1FFFFFL & load3(s, 55) >> 1;
        final long s24 = 0x1FFFFFL & load4(s, 57) >> 6;
        final long s25 = load4(s, 60) >> 3;
        s13 += s25 * 666643L;
        s14 += s25 * 470296L;
        s15 += s25 * 654183L;
        s16 -= s25 * 997805L;
        s17 += s25 * 136657L;
        s18 -= s25 * 683901L;
        s12 += s24 * 666643L;
        s13 += s24 * 470296L;
        s14 += s24 * 654183L;
        s15 -= s24 * 997805L;
        s16 += s24 * 136657L;
        s17 -= s24 * 683901L;
        s11 += s23 * 666643L;
        s12 += s23 * 470296L;
        s13 += s23 * 654183L;
        s14 -= s23 * 997805L;
        s15 += s23 * 136657L;
        s16 -= s23 * 683901L;
        s10 += s22 * 666643L;
        s11 += s22 * 470296L;
        s12 += s22 * 654183L;
        s13 -= s22 * 997805L;
        s14 += s22 * 136657L;
        s15 -= s22 * 683901L;
        s9 += s21 * 666643L;
        s10 += s21 * 470296L;
        s11 += s21 * 654183L;
        s12 -= s21 * 997805L;
        s13 += s21 * 136657L;
        s14 -= s21 * 683901L;
        s8 += s20 * 666643L;
        s9 += s20 * 470296L;
        s10 += s20 * 654183L;
        s11 -= s20 * 997805L;
        s12 += s20 * 136657L;
        s13 -= s20 * 683901L;
        long carry6 = s8 + 1048576L >> 21;
        s9 += carry6;
        s8 -= carry6 << 21;
        long carry7 = s10 + 1048576L >> 21;
        s11 += carry7;
        s10 -= carry7 << 21;
        long carry8 = s12 + 1048576L >> 21;
        s13 += carry8;
        s12 -= carry8 << 21;
        final long carry9 = s14 + 1048576L >> 21;
        s15 += carry9;
        s14 -= carry9 << 21;
        final long carry10 = s16 + 1048576L >> 21;
        s17 += carry10;
        s16 -= carry10 << 21;
        final long carry11 = s18 + 1048576L >> 21;
        s19 += carry11;
        s18 -= carry11 << 21;
        long carry12 = s9 + 1048576L >> 21;
        s10 += carry12;
        s9 -= carry12 << 21;
        long carry13 = s11 + 1048576L >> 21;
        s12 += carry13;
        s11 -= carry13 << 21;
        long carry14 = s13 + 1048576L >> 21;
        s14 += carry14;
        s13 -= carry14 << 21;
        final long carry15 = s15 + 1048576L >> 21;
        s16 += carry15;
        s15 -= carry15 << 21;
        final long carry16 = s17 + 1048576L >> 21;
        s18 += carry16;
        s17 -= carry16 << 21;
        s7 += s19 * 666643L;
        s8 += s19 * 470296L;
        s9 += s19 * 654183L;
        s10 -= s19 * 997805L;
        s11 += s19 * 136657L;
        s12 -= s19 * 683901L;
        s6 += s18 * 666643L;
        s7 += s18 * 470296L;
        s8 += s18 * 654183L;
        s9 -= s18 * 997805L;
        s10 += s18 * 136657L;
        s11 -= s18 * 683901L;
        s5 += s17 * 666643L;
        s6 += s17 * 470296L;
        s7 += s17 * 654183L;
        s8 -= s17 * 997805L;
        s9 += s17 * 136657L;
        s10 -= s17 * 683901L;
        s4 += s16 * 666643L;
        s5 += s16 * 470296L;
        s6 += s16 * 654183L;
        s7 -= s16 * 997805L;
        s8 += s16 * 136657L;
        s9 -= s16 * 683901L;
        s3 += s15 * 666643L;
        s4 += s15 * 470296L;
        s5 += s15 * 654183L;
        s6 -= s15 * 997805L;
        s7 += s15 * 136657L;
        s8 -= s15 * 683901L;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        s14 = 0L;
        long carry17 = s2 + 1048576L >> 21;
        s3 += carry17;
        s2 -= carry17 << 21;
        long carry18 = s4 + 1048576L >> 21;
        s5 += carry18;
        s4 -= carry18 << 21;
        long carry19 = s6 + 1048576L >> 21;
        s7 += carry19;
        s6 -= carry19 << 21;
        carry6 = s8 + 1048576L >> 21;
        s9 += carry6;
        s8 -= carry6 << 21;
        carry7 = s10 + 1048576L >> 21;
        s11 += carry7;
        s10 -= carry7 << 21;
        carry8 = s12 + 1048576L >> 21;
        s13 += carry8;
        s12 -= carry8 << 21;
        long carry20 = s3 + 1048576L >> 21;
        s4 += carry20;
        s3 -= carry20 << 21;
        long carry21 = s5 + 1048576L >> 21;
        s6 += carry21;
        s5 -= carry21 << 21;
        long carry22 = s7 + 1048576L >> 21;
        s8 += carry22;
        s7 -= carry22 << 21;
        carry12 = s9 + 1048576L >> 21;
        s10 += carry12;
        s9 -= carry12 << 21;
        carry13 = s11 + 1048576L >> 21;
        s12 += carry13;
        s11 -= carry13 << 21;
        carry14 = s13 + 1048576L >> 21;
        s14 += carry14;
        s13 -= carry14 << 21;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        s14 = 0L;
        carry17 = s2 >> 21;
        s3 += carry17;
        s2 -= carry17 << 21;
        carry20 = s3 >> 21;
        s4 += carry20;
        s3 -= carry20 << 21;
        carry18 = s4 >> 21;
        s5 += carry18;
        s4 -= carry18 << 21;
        carry21 = s5 >> 21;
        s6 += carry21;
        s5 -= carry21 << 21;
        carry19 = s6 >> 21;
        s7 += carry19;
        s6 -= carry19 << 21;
        carry22 = s7 >> 21;
        s8 += carry22;
        s7 -= carry22 << 21;
        carry6 = s8 >> 21;
        s9 += carry6;
        s8 -= carry6 << 21;
        carry12 = s9 >> 21;
        s10 += carry12;
        s9 -= carry12 << 21;
        carry7 = s10 >> 21;
        s11 += carry7;
        s10 -= carry7 << 21;
        carry13 = s11 >> 21;
        s12 += carry13;
        s11 -= carry13 << 21;
        carry8 = s12 >> 21;
        s13 += carry8;
        s12 -= carry8 << 21;
        carry14 = s13 >> 21;
        s14 += carry14;
        s13 -= carry14 << 21;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        carry17 = s2 >> 21;
        s3 += carry17;
        s2 -= carry17 << 21;
        carry20 = s3 >> 21;
        s4 += carry20;
        s3 -= carry20 << 21;
        carry18 = s4 >> 21;
        s5 += carry18;
        s4 -= carry18 << 21;
        carry21 = s5 >> 21;
        s6 += carry21;
        s5 -= carry21 << 21;
        carry19 = s6 >> 21;
        s7 += carry19;
        s6 -= carry19 << 21;
        carry22 = s7 >> 21;
        s8 += carry22;
        s7 -= carry22 << 21;
        carry6 = s8 >> 21;
        s9 += carry6;
        s8 -= carry6 << 21;
        carry12 = s9 >> 21;
        s10 += carry12;
        s9 -= carry12 << 21;
        carry7 = s10 >> 21;
        s11 += carry7;
        s10 -= carry7 << 21;
        carry13 = s11 >> 21;
        s12 += carry13;
        s11 -= carry13 << 21;
        carry8 = s12 >> 21;
        s13 += carry8;
        s12 -= carry8 << 21;
        s[0] = (byte)s2;
        s[1] = (byte)(s2 >> 8);
        s[2] = (byte)(s2 >> 16 | s3 << 5);
        s[3] = (byte)(s3 >> 3);
        s[4] = (byte)(s3 >> 11);
        s[5] = (byte)(s3 >> 19 | s4 << 2);
        s[6] = (byte)(s4 >> 6);
        s[7] = (byte)(s4 >> 14 | s5 << 7);
        s[8] = (byte)(s5 >> 1);
        s[9] = (byte)(s5 >> 9);
        s[10] = (byte)(s5 >> 17 | s6 << 4);
        s[11] = (byte)(s6 >> 4);
        s[12] = (byte)(s6 >> 12);
        s[13] = (byte)(s6 >> 20 | s7 << 1);
        s[14] = (byte)(s7 >> 7);
        s[15] = (byte)(s7 >> 15 | s8 << 6);
        s[16] = (byte)(s8 >> 2);
        s[17] = (byte)(s8 >> 10);
        s[18] = (byte)(s8 >> 18 | s9 << 3);
        s[19] = (byte)(s9 >> 5);
        s[20] = (byte)(s9 >> 13);
        s[21] = (byte)s10;
        s[22] = (byte)(s10 >> 8);
        s[23] = (byte)(s10 >> 16 | s11 << 5);
        s[24] = (byte)(s11 >> 3);
        s[25] = (byte)(s11 >> 11);
        s[26] = (byte)(s11 >> 19 | s12 << 2);
        s[27] = (byte)(s12 >> 6);
        s[28] = (byte)(s12 >> 14 | s13 << 7);
        s[29] = (byte)(s13 >> 1);
        s[30] = (byte)(s13 >> 9);
        s[31] = (byte)(s13 >> 17);
    }
    
    private static void mulAdd(final byte[] s, final byte[] a, final byte[] b, final byte[] c) {
        final long a2 = 0x1FFFFFL & load3(a, 0);
        final long a3 = 0x1FFFFFL & load4(a, 2) >> 5;
        final long a4 = 0x1FFFFFL & load3(a, 5) >> 2;
        final long a5 = 0x1FFFFFL & load4(a, 7) >> 7;
        final long a6 = 0x1FFFFFL & load4(a, 10) >> 4;
        final long a7 = 0x1FFFFFL & load3(a, 13) >> 1;
        final long a8 = 0x1FFFFFL & load4(a, 15) >> 6;
        final long a9 = 0x1FFFFFL & load3(a, 18) >> 3;
        final long a10 = 0x1FFFFFL & load3(a, 21);
        final long a11 = 0x1FFFFFL & load4(a, 23) >> 5;
        final long a12 = 0x1FFFFFL & load3(a, 26) >> 2;
        final long a13 = load4(a, 28) >> 7;
        final long b2 = 0x1FFFFFL & load3(b, 0);
        final long b3 = 0x1FFFFFL & load4(b, 2) >> 5;
        final long b4 = 0x1FFFFFL & load3(b, 5) >> 2;
        final long b5 = 0x1FFFFFL & load4(b, 7) >> 7;
        final long b6 = 0x1FFFFFL & load4(b, 10) >> 4;
        final long b7 = 0x1FFFFFL & load3(b, 13) >> 1;
        final long b8 = 0x1FFFFFL & load4(b, 15) >> 6;
        final long b9 = 0x1FFFFFL & load3(b, 18) >> 3;
        final long b10 = 0x1FFFFFL & load3(b, 21);
        final long b11 = 0x1FFFFFL & load4(b, 23) >> 5;
        final long b12 = 0x1FFFFFL & load3(b, 26) >> 2;
        final long b13 = load4(b, 28) >> 7;
        final long c2 = 0x1FFFFFL & load3(c, 0);
        final long c3 = 0x1FFFFFL & load4(c, 2) >> 5;
        final long c4 = 0x1FFFFFL & load3(c, 5) >> 2;
        final long c5 = 0x1FFFFFL & load4(c, 7) >> 7;
        final long c6 = 0x1FFFFFL & load4(c, 10) >> 4;
        final long c7 = 0x1FFFFFL & load3(c, 13) >> 1;
        final long c8 = 0x1FFFFFL & load4(c, 15) >> 6;
        final long c9 = 0x1FFFFFL & load3(c, 18) >> 3;
        final long c10 = 0x1FFFFFL & load3(c, 21);
        final long c11 = 0x1FFFFFL & load4(c, 23) >> 5;
        final long c12 = 0x1FFFFFL & load3(c, 26) >> 2;
        final long c13 = load4(c, 28) >> 7;
        long s2 = c2 + a2 * b2;
        long s3 = c3 + a2 * b3 + a3 * b2;
        long s4 = c4 + a2 * b4 + a3 * b3 + a4 * b2;
        long s5 = c5 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2;
        long s6 = c6 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2;
        long s7 = c7 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2;
        long s8 = c8 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2;
        long s9 = c9 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2;
        long s10 = c10 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2;
        long s11 = c11 + a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2;
        long s12 = c12 + a2 * b12 + a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3 + a12 * b2;
        long s13 = c13 + a2 * b13 + a3 * b12 + a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4 + a12 * b3 + a13 * b2;
        long s14 = a3 * b13 + a4 * b12 + a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5 + a12 * b4 + a13 * b3;
        long s15 = a4 * b13 + a5 * b12 + a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6 + a12 * b5 + a13 * b4;
        long s16 = a5 * b13 + a6 * b12 + a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7 + a12 * b6 + a13 * b5;
        long s17 = a6 * b13 + a7 * b12 + a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8 + a12 * b7 + a13 * b6;
        long s18 = a7 * b13 + a8 * b12 + a9 * b11 + a10 * b10 + a11 * b9 + a12 * b8 + a13 * b7;
        long s19 = a8 * b13 + a9 * b12 + a10 * b11 + a11 * b10 + a12 * b9 + a13 * b8;
        long s20 = a9 * b13 + a10 * b12 + a11 * b11 + a12 * b10 + a13 * b9;
        long s21 = a10 * b13 + a11 * b12 + a12 * b11 + a13 * b10;
        long s22 = a11 * b13 + a12 * b12 + a13 * b11;
        long s23 = a12 * b13 + a13 * b12;
        long s24 = a13 * b13;
        long s25 = 0L;
        long carry0 = s2 + 1048576L >> 21;
        s3 += carry0;
        s2 -= carry0 << 21;
        long carry2 = s4 + 1048576L >> 21;
        s5 += carry2;
        s4 -= carry2 << 21;
        long carry3 = s6 + 1048576L >> 21;
        s7 += carry3;
        s6 -= carry3 << 21;
        long carry4 = s8 + 1048576L >> 21;
        s9 += carry4;
        s8 -= carry4 << 21;
        long carry5 = s10 + 1048576L >> 21;
        s11 += carry5;
        s10 -= carry5 << 21;
        long carry6 = s12 + 1048576L >> 21;
        s13 += carry6;
        s12 -= carry6 << 21;
        long carry7 = s14 + 1048576L >> 21;
        s15 += carry7;
        s14 -= carry7 << 21;
        long carry8 = s16 + 1048576L >> 21;
        s17 += carry8;
        s16 -= carry8 << 21;
        long carry9 = s18 + 1048576L >> 21;
        s19 += carry9;
        s18 -= carry9 << 21;
        final long carry10 = s20 + 1048576L >> 21;
        s21 += carry10;
        s20 -= carry10 << 21;
        final long carry11 = s22 + 1048576L >> 21;
        s23 += carry11;
        s22 -= carry11 << 21;
        final long carry12 = s24 + 1048576L >> 21;
        s25 += carry12;
        s24 -= carry12 << 21;
        long carry13 = s3 + 1048576L >> 21;
        s4 += carry13;
        s3 -= carry13 << 21;
        long carry14 = s5 + 1048576L >> 21;
        s6 += carry14;
        s5 -= carry14 << 21;
        long carry15 = s7 + 1048576L >> 21;
        s8 += carry15;
        s7 -= carry15 << 21;
        long carry16 = s9 + 1048576L >> 21;
        s10 += carry16;
        s9 -= carry16 << 21;
        long carry17 = s11 + 1048576L >> 21;
        s12 += carry17;
        s11 -= carry17 << 21;
        long carry18 = s13 + 1048576L >> 21;
        s14 += carry18;
        s13 -= carry18 << 21;
        long carry19 = s15 + 1048576L >> 21;
        s16 += carry19;
        s15 -= carry19 << 21;
        long carry20 = s17 + 1048576L >> 21;
        s18 += carry20;
        s17 -= carry20 << 21;
        final long carry21 = s19 + 1048576L >> 21;
        s20 += carry21;
        s19 -= carry21 << 21;
        final long carry22 = s21 + 1048576L >> 21;
        s22 += carry22;
        s21 -= carry22 << 21;
        final long carry23 = s23 + 1048576L >> 21;
        s24 += carry23;
        s23 -= carry23 << 21;
        s13 += s25 * 666643L;
        s14 += s25 * 470296L;
        s15 += s25 * 654183L;
        s16 -= s25 * 997805L;
        s17 += s25 * 136657L;
        s18 -= s25 * 683901L;
        s12 += s24 * 666643L;
        s13 += s24 * 470296L;
        s14 += s24 * 654183L;
        s15 -= s24 * 997805L;
        s16 += s24 * 136657L;
        s17 -= s24 * 683901L;
        s11 += s23 * 666643L;
        s12 += s23 * 470296L;
        s13 += s23 * 654183L;
        s14 -= s23 * 997805L;
        s15 += s23 * 136657L;
        s16 -= s23 * 683901L;
        s10 += s22 * 666643L;
        s11 += s22 * 470296L;
        s12 += s22 * 654183L;
        s13 -= s22 * 997805L;
        s14 += s22 * 136657L;
        s15 -= s22 * 683901L;
        s9 += s21 * 666643L;
        s10 += s21 * 470296L;
        s11 += s21 * 654183L;
        s12 -= s21 * 997805L;
        s13 += s21 * 136657L;
        s14 -= s21 * 683901L;
        s8 += s20 * 666643L;
        s9 += s20 * 470296L;
        s10 += s20 * 654183L;
        s11 -= s20 * 997805L;
        s12 += s20 * 136657L;
        s13 -= s20 * 683901L;
        carry4 = s8 + 1048576L >> 21;
        s9 += carry4;
        s8 -= carry4 << 21;
        carry5 = s10 + 1048576L >> 21;
        s11 += carry5;
        s10 -= carry5 << 21;
        carry6 = s12 + 1048576L >> 21;
        s13 += carry6;
        s12 -= carry6 << 21;
        carry7 = s14 + 1048576L >> 21;
        s15 += carry7;
        s14 -= carry7 << 21;
        carry8 = s16 + 1048576L >> 21;
        s17 += carry8;
        s16 -= carry8 << 21;
        carry9 = s18 + 1048576L >> 21;
        s19 += carry9;
        s18 -= carry9 << 21;
        carry16 = s9 + 1048576L >> 21;
        s10 += carry16;
        s9 -= carry16 << 21;
        carry17 = s11 + 1048576L >> 21;
        s12 += carry17;
        s11 -= carry17 << 21;
        carry18 = s13 + 1048576L >> 21;
        s14 += carry18;
        s13 -= carry18 << 21;
        carry19 = s15 + 1048576L >> 21;
        s16 += carry19;
        s15 -= carry19 << 21;
        carry20 = s17 + 1048576L >> 21;
        s18 += carry20;
        s17 -= carry20 << 21;
        s7 += s19 * 666643L;
        s8 += s19 * 470296L;
        s9 += s19 * 654183L;
        s10 -= s19 * 997805L;
        s11 += s19 * 136657L;
        s12 -= s19 * 683901L;
        s6 += s18 * 666643L;
        s7 += s18 * 470296L;
        s8 += s18 * 654183L;
        s9 -= s18 * 997805L;
        s10 += s18 * 136657L;
        s11 -= s18 * 683901L;
        s5 += s17 * 666643L;
        s6 += s17 * 470296L;
        s7 += s17 * 654183L;
        s8 -= s17 * 997805L;
        s9 += s17 * 136657L;
        s10 -= s17 * 683901L;
        s4 += s16 * 666643L;
        s5 += s16 * 470296L;
        s6 += s16 * 654183L;
        s7 -= s16 * 997805L;
        s8 += s16 * 136657L;
        s9 -= s16 * 683901L;
        s3 += s15 * 666643L;
        s4 += s15 * 470296L;
        s5 += s15 * 654183L;
        s6 -= s15 * 997805L;
        s7 += s15 * 136657L;
        s8 -= s15 * 683901L;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        s14 = 0L;
        carry0 = s2 + 1048576L >> 21;
        s3 += carry0;
        s2 -= carry0 << 21;
        carry2 = s4 + 1048576L >> 21;
        s5 += carry2;
        s4 -= carry2 << 21;
        carry3 = s6 + 1048576L >> 21;
        s7 += carry3;
        s6 -= carry3 << 21;
        carry4 = s8 + 1048576L >> 21;
        s9 += carry4;
        s8 -= carry4 << 21;
        carry5 = s10 + 1048576L >> 21;
        s11 += carry5;
        s10 -= carry5 << 21;
        carry6 = s12 + 1048576L >> 21;
        s13 += carry6;
        s12 -= carry6 << 21;
        carry13 = s3 + 1048576L >> 21;
        s4 += carry13;
        s3 -= carry13 << 21;
        carry14 = s5 + 1048576L >> 21;
        s6 += carry14;
        s5 -= carry14 << 21;
        carry15 = s7 + 1048576L >> 21;
        s8 += carry15;
        s7 -= carry15 << 21;
        carry16 = s9 + 1048576L >> 21;
        s10 += carry16;
        s9 -= carry16 << 21;
        carry17 = s11 + 1048576L >> 21;
        s12 += carry17;
        s11 -= carry17 << 21;
        carry18 = s13 + 1048576L >> 21;
        s14 += carry18;
        s13 -= carry18 << 21;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        s14 = 0L;
        carry0 = s2 >> 21;
        s3 += carry0;
        s2 -= carry0 << 21;
        carry13 = s3 >> 21;
        s4 += carry13;
        s3 -= carry13 << 21;
        carry2 = s4 >> 21;
        s5 += carry2;
        s4 -= carry2 << 21;
        carry14 = s5 >> 21;
        s6 += carry14;
        s5 -= carry14 << 21;
        carry3 = s6 >> 21;
        s7 += carry3;
        s6 -= carry3 << 21;
        carry15 = s7 >> 21;
        s8 += carry15;
        s7 -= carry15 << 21;
        carry4 = s8 >> 21;
        s9 += carry4;
        s8 -= carry4 << 21;
        carry16 = s9 >> 21;
        s10 += carry16;
        s9 -= carry16 << 21;
        carry5 = s10 >> 21;
        s11 += carry5;
        s10 -= carry5 << 21;
        carry17 = s11 >> 21;
        s12 += carry17;
        s11 -= carry17 << 21;
        carry6 = s12 >> 21;
        s13 += carry6;
        s12 -= carry6 << 21;
        carry18 = s13 >> 21;
        s14 += carry18;
        s13 -= carry18 << 21;
        s2 += s14 * 666643L;
        s3 += s14 * 470296L;
        s4 += s14 * 654183L;
        s5 -= s14 * 997805L;
        s6 += s14 * 136657L;
        s7 -= s14 * 683901L;
        carry0 = s2 >> 21;
        s3 += carry0;
        s2 -= carry0 << 21;
        carry13 = s3 >> 21;
        s4 += carry13;
        s3 -= carry13 << 21;
        carry2 = s4 >> 21;
        s5 += carry2;
        s4 -= carry2 << 21;
        carry14 = s5 >> 21;
        s6 += carry14;
        s5 -= carry14 << 21;
        carry3 = s6 >> 21;
        s7 += carry3;
        s6 -= carry3 << 21;
        carry15 = s7 >> 21;
        s8 += carry15;
        s7 -= carry15 << 21;
        carry4 = s8 >> 21;
        s9 += carry4;
        s8 -= carry4 << 21;
        carry16 = s9 >> 21;
        s10 += carry16;
        s9 -= carry16 << 21;
        carry5 = s10 >> 21;
        s11 += carry5;
        s10 -= carry5 << 21;
        carry17 = s11 >> 21;
        s12 += carry17;
        s11 -= carry17 << 21;
        carry6 = s12 >> 21;
        s13 += carry6;
        s12 -= carry6 << 21;
        s[0] = (byte)s2;
        s[1] = (byte)(s2 >> 8);
        s[2] = (byte)(s2 >> 16 | s3 << 5);
        s[3] = (byte)(s3 >> 3);
        s[4] = (byte)(s3 >> 11);
        s[5] = (byte)(s3 >> 19 | s4 << 2);
        s[6] = (byte)(s4 >> 6);
        s[7] = (byte)(s4 >> 14 | s5 << 7);
        s[8] = (byte)(s5 >> 1);
        s[9] = (byte)(s5 >> 9);
        s[10] = (byte)(s5 >> 17 | s6 << 4);
        s[11] = (byte)(s6 >> 4);
        s[12] = (byte)(s6 >> 12);
        s[13] = (byte)(s6 >> 20 | s7 << 1);
        s[14] = (byte)(s7 >> 7);
        s[15] = (byte)(s7 >> 15 | s8 << 6);
        s[16] = (byte)(s8 >> 2);
        s[17] = (byte)(s8 >> 10);
        s[18] = (byte)(s8 >> 18 | s9 << 3);
        s[19] = (byte)(s9 >> 5);
        s[20] = (byte)(s9 >> 13);
        s[21] = (byte)s10;
        s[22] = (byte)(s10 >> 8);
        s[23] = (byte)(s10 >> 16 | s11 << 5);
        s[24] = (byte)(s11 >> 3);
        s[25] = (byte)(s11 >> 11);
        s[26] = (byte)(s11 >> 19 | s12 << 2);
        s[27] = (byte)(s12 >> 6);
        s[28] = (byte)(s12 >> 14 | s13 << 7);
        s[29] = (byte)(s13 >> 1);
        s[30] = (byte)(s13 >> 9);
        s[31] = (byte)(s13 >> 17);
    }
    
    public static byte[] getHashedScalar(final byte[] privateKey) throws GeneralSecurityException {
        final MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");
        digest.update(privateKey, 0, 32);
        final byte[] h = digest.digest();
        h[0] &= (byte)248;
        h[31] &= 0x7F;
        h[31] |= 0x40;
        return h;
    }
    
    public static byte[] sign(final byte[] message, final byte[] publicKey, final byte[] hashedPrivateKey) throws GeneralSecurityException {
        final byte[] messageCopy = Arrays.copyOfRange(message, 0, message.length);
        final MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");
        digest.update(hashedPrivateKey, 32, 32);
        digest.update(messageCopy);
        final byte[] r = digest.digest();
        reduce(r);
        final byte[] rB = Arrays.copyOfRange(scalarMultWithBase(r).toBytes(), 0, 32);
        digest.reset();
        digest.update(rB);
        digest.update(publicKey);
        digest.update(messageCopy);
        final byte[] hram = digest.digest();
        reduce(hram);
        final byte[] s = new byte[32];
        mulAdd(s, hram, hashedPrivateKey, r);
        return Bytes.concat(new byte[][] { rB, s });
    }
    
    private static boolean isSmallerThanGroupOrder(final byte[] s) {
        for (int j = 31; j >= 0; --j) {
            final int a = s[j] & 0xFF;
            final int b = Ed25519.GROUP_ORDER[j] & 0xFF;
            if (a != b) {
                return a < b;
            }
        }
        return false;
    }
    
    public static boolean verify(final byte[] message, final byte[] signature, final byte[] publicKey) throws GeneralSecurityException {
        if (signature.length != 64) {
            return false;
        }
        final byte[] s = Arrays.copyOfRange(signature, 32, 64);
        if (!isSmallerThanGroupOrder(s)) {
            return false;
        }
        final MessageDigest digest = EngineFactory.MESSAGE_DIGEST.getInstance("SHA-512");
        digest.update(signature, 0, 32);
        digest.update(publicKey);
        digest.update(message);
        final byte[] h = digest.digest();
        reduce(h);
        final XYZT negPublicKey = fromBytesNegateVarTime(publicKey);
        final XYZ xyz = doubleScalarMultVarTime(h, negPublicKey, s);
        final byte[] expectedR = xyz.toBytes();
        for (int i = 0; i < 32; ++i) {
            if (expectedR[i] != signature[i]) {
                return false;
            }
        }
        return true;
    }
    
    public static void init() {
        if (Ed25519Constants.D == null) {
            throw new IllegalStateException("Could not initialize Ed25519.");
        }
    }
    
    private Ed25519() {
    }
    
    static {
        CACHED_NEUTRAL = new CachedXYT(new long[] { 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L }, new long[] { 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L }, new long[] { 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L });
        NEUTRAL = new PartialXYZT(new XYZ(new long[] { 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L }, new long[] { 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L }, new long[] { 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L }), new long[] { 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L });
        GROUP_ORDER = new byte[] { -19, -45, -11, 92, 26, 99, 18, 88, -42, -100, -9, -94, -34, -7, -34, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 };
    }
    
    private static class XYZ
    {
        final long[] x;
        final long[] y;
        final long[] z;
        
        XYZ() {
            this(new long[10], new long[10], new long[10]);
        }
        
        XYZ(final long[] x, final long[] y, final long[] z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        
        XYZ(final XYZ xyz) {
            this.x = Arrays.copyOf(xyz.x, 10);
            this.y = Arrays.copyOf(xyz.y, 10);
            this.z = Arrays.copyOf(xyz.z, 10);
        }
        
        XYZ(final PartialXYZT partialXYZT) {
            this();
            fromPartialXYZT(this, partialXYZT);
        }
        
        @CanIgnoreReturnValue
        static XYZ fromPartialXYZT(final XYZ out, final PartialXYZT in) {
            Field25519.mult(out.x, in.xyz.x, in.t);
            Field25519.mult(out.y, in.xyz.y, in.xyz.z);
            Field25519.mult(out.z, in.xyz.z, in.t);
            return out;
        }
        
        byte[] toBytes() {
            final long[] recip = new long[10];
            final long[] x = new long[10];
            final long[] y = new long[10];
            Field25519.inverse(recip, this.z);
            Field25519.mult(x, this.x, recip);
            Field25519.mult(y, this.y, recip);
            final byte[] s = Field25519.contract(y);
            s[31] ^= (byte)(getLsb(x) << 7);
            return s;
        }
        
        boolean isOnCurve() {
            final long[] x2 = new long[10];
            Field25519.square(x2, this.x);
            final long[] y2 = new long[10];
            Field25519.square(y2, this.y);
            final long[] z2 = new long[10];
            Field25519.square(z2, this.z);
            final long[] z3 = new long[10];
            Field25519.square(z3, z2);
            final long[] lhs = new long[10];
            Field25519.sub(lhs, y2, x2);
            Field25519.mult(lhs, lhs, z2);
            final long[] rhs = new long[10];
            Field25519.mult(rhs, x2, y2);
            Field25519.mult(rhs, rhs, Ed25519Constants.D);
            Field25519.sum(rhs, z3);
            Field25519.reduce(rhs, rhs);
            return Bytes.equal(Field25519.contract(lhs), Field25519.contract(rhs));
        }
    }
    
    private static class XYZT
    {
        final XYZ xyz;
        final long[] t;
        
        XYZT() {
            this(new XYZ(), new long[10]);
        }
        
        XYZT(final XYZ xyz, final long[] t) {
            this.xyz = xyz;
            this.t = t;
        }
        
        XYZT(final PartialXYZT partialXYZT) {
            this();
            fromPartialXYZT(this, partialXYZT);
        }
        
        @CanIgnoreReturnValue
        private static XYZT fromPartialXYZT(final XYZT out, final PartialXYZT in) {
            Field25519.mult(out.xyz.x, in.xyz.x, in.t);
            Field25519.mult(out.xyz.y, in.xyz.y, in.xyz.z);
            Field25519.mult(out.xyz.z, in.xyz.z, in.t);
            Field25519.mult(out.t, in.xyz.x, in.xyz.y);
            return out;
        }
        
        private static XYZT fromBytesNegateVarTime(final byte[] s) throws GeneralSecurityException {
            final long[] x = new long[10];
            final long[] y = Field25519.expand(s);
            final long[] z = new long[10];
            z[0] = 1L;
            final long[] t = new long[10];
            final long[] u = new long[10];
            final long[] v = new long[10];
            final long[] vxx = new long[10];
            final long[] check = new long[10];
            Field25519.square(u, y);
            Field25519.mult(v, u, Ed25519Constants.D);
            Field25519.sub(u, u, z);
            Field25519.sum(v, v, z);
            final long[] v2 = new long[10];
            Field25519.square(v2, v);
            Field25519.mult(v2, v2, v);
            Field25519.square(x, v2);
            Field25519.mult(x, x, v);
            Field25519.mult(x, x, u);
            pow2252m3(x, x);
            Field25519.mult(x, x, v2);
            Field25519.mult(x, x, u);
            Field25519.square(vxx, x);
            Field25519.mult(vxx, vxx, v);
            Field25519.sub(check, vxx, u);
            if (isNonZeroVarTime(check)) {
                Field25519.sum(check, vxx, u);
                if (isNonZeroVarTime(check)) {
                    throw new GeneralSecurityException("Cannot convert given bytes to extended projective coordinates. No square root exists for modulo 2^255-19");
                }
                Field25519.mult(x, x, Ed25519Constants.SQRTM1);
            }
            if (!isNonZeroVarTime(x) && (s[31] & 0xFF) >> 7 != 0) {
                throw new GeneralSecurityException("Cannot convert given bytes to extended projective coordinates. Computed x is zero and encoded x's least significant bit is not zero");
            }
            if (getLsb(x) == (s[31] & 0xFF) >> 7) {
                neg(x, x);
            }
            Field25519.mult(t, x, y);
            return new XYZT(new XYZ(x, y, z), t);
        }
    }
    
    private static class PartialXYZT
    {
        final XYZ xyz;
        final long[] t;
        
        PartialXYZT() {
            this(new XYZ(), new long[10]);
        }
        
        PartialXYZT(final XYZ xyz, final long[] t) {
            this.xyz = xyz;
            this.t = t;
        }
        
        PartialXYZT(final PartialXYZT other) {
            this.xyz = new XYZ(other.xyz);
            this.t = Arrays.copyOf(other.t, 10);
        }
    }
    
    static class CachedXYT
    {
        final long[] yPlusX;
        final long[] yMinusX;
        final long[] t2d;
        
        CachedXYT() {
            this(new long[10], new long[10], new long[10]);
        }
        
        CachedXYT(final long[] yPlusX, final long[] yMinusX, final long[] t2d) {
            this.yPlusX = yPlusX;
            this.yMinusX = yMinusX;
            this.t2d = t2d;
        }
        
        CachedXYT(final CachedXYT other) {
            this.yPlusX = Arrays.copyOf(other.yPlusX, 10);
            this.yMinusX = Arrays.copyOf(other.yMinusX, 10);
            this.t2d = Arrays.copyOf(other.t2d, 10);
        }
        
        void multByZ(final long[] output, final long[] in) {
            System.arraycopy(in, 0, output, 0, 10);
        }
        
        void copyConditional(final CachedXYT other, final int icopy) {
            Curve25519.copyConditional(this.yPlusX, other.yPlusX, icopy);
            Curve25519.copyConditional(this.yMinusX, other.yMinusX, icopy);
            Curve25519.copyConditional(this.t2d, other.t2d, icopy);
        }
    }
    
    private static class CachedXYZT extends CachedXYT
    {
        private final long[] z;
        
        CachedXYZT() {
            this(new long[10], new long[10], new long[10], new long[10]);
        }
        
        CachedXYZT(final XYZT xyzt) {
            this();
            Field25519.sum(this.yPlusX, xyzt.xyz.y, xyzt.xyz.x);
            Field25519.sub(this.yMinusX, xyzt.xyz.y, xyzt.xyz.x);
            System.arraycopy(xyzt.xyz.z, 0, this.z, 0, 10);
            Field25519.mult(this.t2d, xyzt.t, Ed25519Constants.D2);
        }
        
        CachedXYZT(final long[] yPlusX, final long[] yMinusX, final long[] z, final long[] t2d) {
            super(yPlusX, yMinusX, t2d);
            this.z = z;
        }
        
        public void multByZ(final long[] output, final long[] in) {
            Field25519.mult(output, in, this.z);
        }
    }
}
