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

package org.bouncycastle.pqc.crypto.mldsa;

import org.bouncycastle.crypto.digests.SHAKEDigest;

class Poly
{
    private static final int DilithiumN = 256;
    private final int polyUniformNBlocks;
    private int[] coeffs;
    private final MLDSAEngine engine;
    private final Symmetric symmetric;
    
    public Poly(final MLDSAEngine engine) {
        this.coeffs = new int[256];
        this.engine = engine;
        this.symmetric = engine.GetSymmetric();
        this.polyUniformNBlocks = (768 + this.symmetric.stream128BlockBytes - 1) / this.symmetric.stream128BlockBytes;
    }
    
    void copyTo(final Poly poly) {
        System.arraycopy(this.coeffs, 0, poly.coeffs, 0, 256);
    }
    
    public int getCoeffIndex(final int n) {
        return this.coeffs[n];
    }
    
    public int[] getCoeffs() {
        return this.coeffs;
    }
    
    public void setCoeffIndex(final int n, final int n2) {
        this.coeffs[n] = n2;
    }
    
    public void setCoeffs(final int[] coeffs) {
        this.coeffs = coeffs;
    }
    
    public void uniformBlocks(final byte[] array, final short n) {
        int n2 = this.polyUniformNBlocks * this.symmetric.stream128BlockBytes;
        final byte[] array2 = new byte[n2 + 2];
        this.symmetric.stream128init(array, n);
        this.symmetric.stream128squeezeBlocks(array2, 0, n2);
        for (int i = rejectUniform(this, 0, 256, array2, n2); i < 256; i += rejectUniform(this, i, 256 - i, array2, n2)) {
            final int n3 = n2 % 3;
            for (int j = 0; j < n3; ++j) {
                array2[j] = array2[n2 - n3 + j];
            }
            this.symmetric.stream128squeezeBlocks(array2, n3, this.symmetric.stream128BlockBytes);
            n2 = this.symmetric.stream128BlockBytes + n3;
        }
    }
    
    private static int rejectUniform(final Poly poly, final int n, final int n2, final byte[] array, final int n3) {
        int n5;
        for (int n4 = n5 = 0; n5 < n2 && n4 + 3 <= n3; ++n5) {
            final int n6 = ((array[n4++] & 0xFF) | (array[n4++] & 0xFF) << 8 | (array[n4++] & 0xFF) << 16) & 0x7FFFFF;
            if (n6 < 8380417) {
                poly.setCoeffIndex(n + n5, n6);
            }
        }
        return n5;
    }
    
    public void uniformEta(final byte[] array, final short n) {
        final int dilithiumEta = this.engine.getDilithiumEta();
        int n2;
        if (this.engine.getDilithiumEta() == 2) {
            n2 = (136 + this.symmetric.stream256BlockBytes - 1) / this.symmetric.stream256BlockBytes;
        }
        else {
            if (this.engine.getDilithiumEta() != 4) {
                throw new RuntimeException("Wrong Dilithium Eta!");
            }
            n2 = (227 + this.symmetric.stream256BlockBytes - 1) / this.symmetric.stream256BlockBytes;
        }
        final int n3 = n2 * this.symmetric.stream256BlockBytes;
        final byte[] array2 = new byte[n3];
        this.symmetric.stream256init(array, n);
        this.symmetric.stream256squeezeBlocks(array2, 0, n3);
        for (int i = rejectEta(this, 0, 256, array2, n3, dilithiumEta); i < 256; i += rejectEta(this, i, 256 - i, array2, this.symmetric.stream256BlockBytes, dilithiumEta)) {
            this.symmetric.stream256squeezeBlocks(array2, 0, this.symmetric.stream256BlockBytes);
        }
    }
    
    private static int rejectEta(final Poly poly, final int n, final int n2, final byte[] array, final int n3, final int n4) {
        int n6;
        int n5 = n6 = 0;
        while (n6 < n2 && n5 < n3) {
            final int n7 = array[n5] & 0xFF & 0xF;
            final int n8 = (array[n5++] & 0xFF) >> 4;
            if (n4 == 2) {
                if (n7 < 15) {
                    poly.setCoeffIndex(n + n6, 2 - (n7 - (205 * n7 >> 10) * 5));
                    ++n6;
                }
                if (n8 >= 15 || n6 >= n2) {
                    continue;
                }
                poly.setCoeffIndex(n + n6, 2 - (n8 - (205 * n8 >> 10) * 5));
                ++n6;
            }
            else {
                if (n4 != 4) {
                    continue;
                }
                if (n7 < 9) {
                    poly.setCoeffIndex(n + n6, 4 - n7);
                    ++n6;
                }
                if (n8 >= 9 || n6 >= n2) {
                    continue;
                }
                poly.setCoeffIndex(n + n6, 4 - n8);
                ++n6;
            }
        }
        return n6;
    }
    
    public void polyNtt() {
        this.setCoeffs(Ntt.ntt(this.coeffs));
    }
    
    public void pointwiseMontgomery(final Poly poly, final Poly poly2) {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, Reduce.montgomeryReduce(poly.getCoeffIndex(i) * (long)poly2.getCoeffIndex(i)));
        }
    }
    
    public void pointwiseAccountMontgomery(final PolyVecL polyVecL, final PolyVecL polyVecL2) {
        final Poly poly = new Poly(this.engine);
        this.pointwiseMontgomery(polyVecL.getVectorIndex(0), polyVecL2.getVectorIndex(0));
        for (int i = 1; i < this.engine.getDilithiumL(); ++i) {
            poly.pointwiseMontgomery(polyVecL.getVectorIndex(i), polyVecL2.getVectorIndex(i));
            this.addPoly(poly);
        }
    }
    
    public void addPoly(final Poly poly) {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, this.getCoeffIndex(i) + poly.getCoeffIndex(i));
        }
    }
    
    public void reduce() {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, Reduce.reduce32(this.getCoeffIndex(i)));
        }
    }
    
    public void invNttToMont() {
        this.setCoeffs(Ntt.invNttToMont(this.getCoeffs()));
    }
    
    public void conditionalAddQ() {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, Reduce.conditionalAddQ(this.getCoeffIndex(i)));
        }
    }
    
    public void power2Round(final Poly poly) {
        Rounding.power2RoundAll(this.coeffs, poly.coeffs);
    }
    
    public byte[] polyt1Pack() {
        final byte[] array = new byte[320];
        for (int i = 0; i < 64; ++i) {
            array[5 * i + 0] = (byte)(this.coeffs[4 * i + 0] >> 0);
            array[5 * i + 1] = (byte)(this.coeffs[4 * i + 0] >> 8 | this.coeffs[4 * i + 1] << 2);
            array[5 * i + 2] = (byte)(this.coeffs[4 * i + 1] >> 6 | this.coeffs[4 * i + 2] << 4);
            array[5 * i + 3] = (byte)(this.coeffs[4 * i + 2] >> 4 | this.coeffs[4 * i + 3] << 6);
            array[5 * i + 4] = (byte)(this.coeffs[4 * i + 3] >> 2);
        }
        return array;
    }
    
    public void polyt1Unpack(final byte[] array) {
        for (int i = 0; i < 64; ++i) {
            this.setCoeffIndex(4 * i + 0, ((array[5 * i + 0] & 0xFF) >> 0 | (array[5 * i + 1] & 0xFF) << 8) & 0x3FF);
            this.setCoeffIndex(4 * i + 1, ((array[5 * i + 1] & 0xFF) >> 2 | (array[5 * i + 2] & 0xFF) << 6) & 0x3FF);
            this.setCoeffIndex(4 * i + 2, ((array[5 * i + 2] & 0xFF) >> 4 | (array[5 * i + 3] & 0xFF) << 4) & 0x3FF);
            this.setCoeffIndex(4 * i + 3, ((array[5 * i + 3] & 0xFF) >> 6 | (array[5 * i + 4] & 0xFF) << 2) & 0x3FF);
        }
    }
    
    public byte[] polyEtaPack(final byte[] array, final int n) {
        final byte[] array2 = new byte[8];
        if (this.engine.getDilithiumEta() == 2) {
            for (int i = 0; i < 32; ++i) {
                array2[0] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 0));
                array2[1] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 1));
                array2[2] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 2));
                array2[3] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 3));
                array2[4] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 4));
                array2[5] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 5));
                array2[6] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 6));
                array2[7] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 7));
                array[n + 3 * i + 0] = (byte)(array2[0] >> 0 | array2[1] << 3 | array2[2] << 6);
                array[n + 3 * i + 1] = (byte)(array2[2] >> 2 | array2[3] << 1 | array2[4] << 4 | array2[5] << 7);
                array[n + 3 * i + 2] = (byte)(array2[5] >> 1 | array2[6] << 2 | array2[7] << 5);
            }
        }
        else {
            if (this.engine.getDilithiumEta() != 4) {
                throw new RuntimeException("Eta needs to be 2 or 4!");
            }
            for (int j = 0; j < 128; ++j) {
                array2[0] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(2 * j + 0));
                array2[1] = (byte)(this.engine.getDilithiumEta() - this.getCoeffIndex(2 * j + 1));
                array[n + j] = (byte)(array2[0] | array2[1] << 4);
            }
        }
        return array;
    }
    
    public void polyEtaUnpack(final byte[] array, final int n) {
        final int dilithiumEta = this.engine.getDilithiumEta();
        if (this.engine.getDilithiumEta() == 2) {
            for (int i = 0; i < 32; ++i) {
                final int n2 = n + 3 * i;
                this.setCoeffIndex(8 * i + 0, (array[n2 + 0] & 0xFF) >> 0 & 0x7);
                this.setCoeffIndex(8 * i + 1, (array[n2 + 0] & 0xFF) >> 3 & 0x7);
                this.setCoeffIndex(8 * i + 2, (array[n2 + 0] & 0xFF) >> 6 | ((array[n2 + 1] & 0xFF) << 2 & 0x7));
                this.setCoeffIndex(8 * i + 3, (array[n2 + 1] & 0xFF) >> 1 & 0x7);
                this.setCoeffIndex(8 * i + 4, (array[n2 + 1] & 0xFF) >> 4 & 0x7);
                this.setCoeffIndex(8 * i + 5, (array[n2 + 1] & 0xFF) >> 7 | ((array[n2 + 2] & 0xFF) << 1 & 0x7));
                this.setCoeffIndex(8 * i + 6, (array[n2 + 2] & 0xFF) >> 2 & 0x7);
                this.setCoeffIndex(8 * i + 7, (array[n2 + 2] & 0xFF) >> 5 & 0x7);
                this.setCoeffIndex(8 * i + 0, dilithiumEta - this.getCoeffIndex(8 * i + 0));
                this.setCoeffIndex(8 * i + 1, dilithiumEta - this.getCoeffIndex(8 * i + 1));
                this.setCoeffIndex(8 * i + 2, dilithiumEta - this.getCoeffIndex(8 * i + 2));
                this.setCoeffIndex(8 * i + 3, dilithiumEta - this.getCoeffIndex(8 * i + 3));
                this.setCoeffIndex(8 * i + 4, dilithiumEta - this.getCoeffIndex(8 * i + 4));
                this.setCoeffIndex(8 * i + 5, dilithiumEta - this.getCoeffIndex(8 * i + 5));
                this.setCoeffIndex(8 * i + 6, dilithiumEta - this.getCoeffIndex(8 * i + 6));
                this.setCoeffIndex(8 * i + 7, dilithiumEta - this.getCoeffIndex(8 * i + 7));
            }
        }
        else if (this.engine.getDilithiumEta() == 4) {
            for (int j = 0; j < 128; ++j) {
                this.setCoeffIndex(2 * j + 0, array[n + j] & 0xF);
                this.setCoeffIndex(2 * j + 1, (array[n + j] & 0xFF) >> 4);
                this.setCoeffIndex(2 * j + 0, dilithiumEta - this.getCoeffIndex(2 * j + 0));
                this.setCoeffIndex(2 * j + 1, dilithiumEta - this.getCoeffIndex(2 * j + 1));
            }
        }
    }
    
    public byte[] polyt0Pack(final byte[] array, final int n) {
        final int[] array2 = new int[8];
        for (int i = 0; i < 32; ++i) {
            array2[0] = 4096 - this.getCoeffIndex(8 * i + 0);
            array2[1] = 4096 - this.getCoeffIndex(8 * i + 1);
            array2[2] = 4096 - this.getCoeffIndex(8 * i + 2);
            array2[3] = 4096 - this.getCoeffIndex(8 * i + 3);
            array2[4] = 4096 - this.getCoeffIndex(8 * i + 4);
            array2[5] = 4096 - this.getCoeffIndex(8 * i + 5);
            array2[6] = 4096 - this.getCoeffIndex(8 * i + 6);
            array2[7] = 4096 - this.getCoeffIndex(8 * i + 7);
            final int n2 = n + 13 * i;
            array[n2 + 0] = (byte)array2[0];
            array[n2 + 1] = (byte)(array2[0] >> 8);
            array[n2 + 1] |= (byte)(array2[1] << 5);
            array[n2 + 2] = (byte)(array2[1] >> 3);
            array[n2 + 3] = (byte)(array2[1] >> 11);
            array[n2 + 3] |= (byte)(array2[2] << 2);
            array[n2 + 4] = (byte)(array2[2] >> 6);
            array[n2 + 4] |= (byte)(array2[3] << 7);
            array[n2 + 5] = (byte)(array2[3] >> 1);
            array[n2 + 6] = (byte)(array2[3] >> 9);
            array[n2 + 6] |= (byte)(array2[4] << 4);
            array[n2 + 7] = (byte)(array2[4] >> 4);
            array[n2 + 8] = (byte)(array2[4] >> 12);
            array[n2 + 8] |= (byte)(array2[5] << 1);
            array[n2 + 9] = (byte)(array2[5] >> 7);
            array[n2 + 9] |= (byte)(array2[6] << 6);
            array[n2 + 10] = (byte)(array2[6] >> 2);
            array[n2 + 11] = (byte)(array2[6] >> 10);
            array[n2 + 11] |= (byte)(array2[7] << 3);
            array[n2 + 12] = (byte)(array2[7] >> 5);
        }
        return array;
    }
    
    public void polyt0Unpack(final byte[] array, final int n) {
        for (int i = 0; i < 32; ++i) {
            final int n2 = n + 13 * i;
            this.setCoeffIndex(8 * i + 0, ((array[n2 + 0] & 0xFF) | (array[n2 + 1] & 0xFF) << 8) & 0x1FFF);
            this.setCoeffIndex(8 * i + 1, ((array[n2 + 1] & 0xFF) >> 5 | (array[n2 + 2] & 0xFF) << 3 | (array[n2 + 3] & 0xFF) << 11) & 0x1FFF);
            this.setCoeffIndex(8 * i + 2, ((array[n2 + 3] & 0xFF) >> 2 | (array[n2 + 4] & 0xFF) << 6) & 0x1FFF);
            this.setCoeffIndex(8 * i + 3, ((array[n2 + 4] & 0xFF) >> 7 | (array[n2 + 5] & 0xFF) << 1 | (array[n2 + 6] & 0xFF) << 9) & 0x1FFF);
            this.setCoeffIndex(8 * i + 4, ((array[n2 + 6] & 0xFF) >> 4 | (array[n2 + 7] & 0xFF) << 4 | (array[n2 + 8] & 0xFF) << 12) & 0x1FFF);
            this.setCoeffIndex(8 * i + 5, ((array[n2 + 8] & 0xFF) >> 1 | (array[n2 + 9] & 0xFF) << 7) & 0x1FFF);
            this.setCoeffIndex(8 * i + 6, ((array[n2 + 9] & 0xFF) >> 6 | (array[n2 + 10] & 0xFF) << 2 | (array[n2 + 11] & 0xFF) << 10) & 0x1FFF);
            this.setCoeffIndex(8 * i + 7, ((array[n2 + 11] & 0xFF) >> 3 | (array[n2 + 12] & 0xFF) << 5) & 0x1FFF);
            this.setCoeffIndex(8 * i + 0, 4096 - this.getCoeffIndex(8 * i + 0));
            this.setCoeffIndex(8 * i + 1, 4096 - this.getCoeffIndex(8 * i + 1));
            this.setCoeffIndex(8 * i + 2, 4096 - this.getCoeffIndex(8 * i + 2));
            this.setCoeffIndex(8 * i + 3, 4096 - this.getCoeffIndex(8 * i + 3));
            this.setCoeffIndex(8 * i + 4, 4096 - this.getCoeffIndex(8 * i + 4));
            this.setCoeffIndex(8 * i + 5, 4096 - this.getCoeffIndex(8 * i + 5));
            this.setCoeffIndex(8 * i + 6, 4096 - this.getCoeffIndex(8 * i + 6));
            this.setCoeffIndex(8 * i + 7, 4096 - this.getCoeffIndex(8 * i + 7));
        }
    }
    
    public void uniformGamma1(final byte[] array, final short n) {
        final byte[] array2 = new byte[this.engine.getPolyUniformGamma1NBlocks() * this.symmetric.stream256BlockBytes];
        this.symmetric.stream256init(array, n);
        this.symmetric.stream256squeezeBlocks(array2, 0, this.engine.getPolyUniformGamma1NBlocks() * this.symmetric.stream256BlockBytes);
        this.unpackZ(array2);
    }
    
    private void unpackZ(final byte[] array) {
        final int dilithiumGamma1 = this.engine.getDilithiumGamma1();
        if (dilithiumGamma1 == 131072) {
            for (int i = 0; i < 64; ++i) {
                this.setCoeffIndex(4 * i + 0, ((array[9 * i + 0] & 0xFF) | (array[9 * i + 1] & 0xFF) << 8 | (array[9 * i + 2] & 0xFF) << 16) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 1, ((array[9 * i + 2] & 0xFF) >> 2 | (array[9 * i + 3] & 0xFF) << 6 | (array[9 * i + 4] & 0xFF) << 14) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 2, ((array[9 * i + 4] & 0xFF) >> 4 | (array[9 * i + 5] & 0xFF) << 4 | (array[9 * i + 6] & 0xFF) << 12) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 3, ((array[9 * i + 6] & 0xFF) >> 6 | (array[9 * i + 7] & 0xFF) << 2 | (array[9 * i + 8] & 0xFF) << 10) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 0, dilithiumGamma1 - this.getCoeffIndex(4 * i + 0));
                this.setCoeffIndex(4 * i + 1, dilithiumGamma1 - this.getCoeffIndex(4 * i + 1));
                this.setCoeffIndex(4 * i + 2, dilithiumGamma1 - this.getCoeffIndex(4 * i + 2));
                this.setCoeffIndex(4 * i + 3, dilithiumGamma1 - this.getCoeffIndex(4 * i + 3));
            }
        }
        else {
            if (dilithiumGamma1 != 524288) {
                throw new RuntimeException("Wrong Dilithiumn Gamma1!");
            }
            for (int j = 0; j < 128; ++j) {
                this.setCoeffIndex(2 * j + 0, ((array[5 * j + 0] & 0xFF) | (array[5 * j + 1] & 0xFF) << 8 | (array[5 * j + 2] & 0xFF) << 16) & 0xFFFFF);
                this.setCoeffIndex(2 * j + 1, ((array[5 * j + 2] & 0xFF) >> 4 | (array[5 * j + 3] & 0xFF) << 4 | (array[5 * j + 4] & 0xFF) << 12) & 0xFFFFF);
                this.setCoeffIndex(2 * j + 0, dilithiumGamma1 - this.getCoeffIndex(2 * j + 0));
                this.setCoeffIndex(2 * j + 1, dilithiumGamma1 - this.getCoeffIndex(2 * j + 1));
            }
        }
    }
    
    public void decompose(final Poly poly) {
        final int dilithiumGamma2 = this.engine.getDilithiumGamma2();
        for (int i = 0; i < 256; ++i) {
            final int[] decompose = Rounding.decompose(this.getCoeffIndex(i), dilithiumGamma2);
            this.setCoeffIndex(i, decompose[1]);
            poly.setCoeffIndex(i, decompose[0]);
        }
    }
    
    void packW1(final byte[] array, final int n) {
        if (this.engine.getDilithiumGamma2() == 95232) {
            for (int i = 0; i < 64; ++i) {
                array[n + 3 * i + 0] = (byte)((byte)this.getCoeffIndex(4 * i + 0) | this.getCoeffIndex(4 * i + 1) << 6);
                array[n + 3 * i + 1] = (byte)((byte)(this.getCoeffIndex(4 * i + 1) >> 2) | this.getCoeffIndex(4 * i + 2) << 4);
                array[n + 3 * i + 2] = (byte)((byte)(this.getCoeffIndex(4 * i + 2) >> 4) | this.getCoeffIndex(4 * i + 3) << 2);
            }
        }
        else if (this.engine.getDilithiumGamma2() == 261888) {
            for (int j = 0; j < 128; ++j) {
                array[n + j] = (byte)(this.getCoeffIndex(2 * j + 0) | this.getCoeffIndex(2 * j + 1) << 4);
            }
        }
    }
    
    public void challenge(final byte[] array, final int n, final int n2) {
        final byte[] array2 = new byte[this.symmetric.stream256BlockBytes];
        final SHAKEDigest shakeDigest = new SHAKEDigest(256);
        shakeDigest.update(array, n, n2);
        shakeDigest.doOutput(array2, 0, this.symmetric.stream256BlockBytes);
        long n3 = 0L;
        for (int i = 0; i < 8; ++i) {
            n3 |= (long)(array2[i] & 0xFF) << 8 * i;
        }
        int n4 = 8;
        for (int j = 0; j < 256; ++j) {
            this.setCoeffIndex(j, 0);
        }
        for (int k = 256 - this.engine.getDilithiumTau(); k < 256; ++k) {
            int l;
            do {
                if (n4 >= this.symmetric.stream256BlockBytes) {
                    shakeDigest.doOutput(array2, 0, this.symmetric.stream256BlockBytes);
                    n4 = 0;
                }
                l = (array2[n4++] & 0xFF);
            } while (l > k);
            this.setCoeffIndex(k, this.getCoeffIndex(l));
            this.setCoeffIndex(l, (int)(1L - 2L * (n3 & 0x1L)));
            n3 >>= 1;
        }
    }
    
    public boolean checkNorm(final int n) {
        if (n > 1047552) {
            return true;
        }
        for (int i = 0; i < 256; ++i) {
            if (this.getCoeffIndex(i) - (this.getCoeffIndex(i) >> 31 & 2 * this.getCoeffIndex(i)) >= n) {
                return true;
            }
        }
        return false;
    }
    
    public void subtract(final Poly poly) {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, this.getCoeffIndex(i) - poly.getCoeffIndex(i));
        }
    }
    
    public int polyMakeHint(final Poly poly, final Poly poly2) {
        int n = 0;
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, Rounding.makeHint(poly.getCoeffIndex(i), poly2.getCoeffIndex(i), this.engine));
            n += this.getCoeffIndex(i);
        }
        return n;
    }
    
    public void polyUseHint(final Poly poly, final Poly poly2) {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, Rounding.useHint(poly.getCoeffIndex(i), poly2.getCoeffIndex(i), this.engine.getDilithiumGamma2()));
        }
    }
    
    public void zPack(final byte[] array, final int n) {
        final int dilithiumGamma1 = this.engine.getDilithiumGamma1();
        if (dilithiumGamma1 == 131072) {
            for (int i = 0; i < 64; ++i) {
                final int n2 = dilithiumGamma1 - this.getCoeffIndex(4 * i + 0);
                final int n3 = dilithiumGamma1 - this.getCoeffIndex(4 * i + 1);
                final int n4 = dilithiumGamma1 - this.getCoeffIndex(4 * i + 2);
                final int n5 = dilithiumGamma1 - this.getCoeffIndex(4 * i + 3);
                array[n + 9 * i + 0] = (byte)n2;
                array[n + 9 * i + 1] = (byte)(n2 >> 8);
                array[n + 9 * i + 2] = (byte)((byte)(n2 >> 16) | n3 << 2);
                array[n + 9 * i + 3] = (byte)(n3 >> 6);
                array[n + 9 * i + 4] = (byte)((byte)(n3 >> 14) | n4 << 4);
                array[n + 9 * i + 5] = (byte)(n4 >> 4);
                array[n + 9 * i + 6] = (byte)((byte)(n4 >> 12) | n5 << 6);
                array[n + 9 * i + 7] = (byte)(n5 >> 2);
                array[n + 9 * i + 8] = (byte)(n5 >> 10);
            }
        }
        else {
            if (dilithiumGamma1 != 524288) {
                throw new RuntimeException("Wrong Dilithium Gamma1!");
            }
            for (int j = 0; j < 128; ++j) {
                final int n6 = dilithiumGamma1 - this.getCoeffIndex(2 * j + 0);
                final int n7 = dilithiumGamma1 - this.getCoeffIndex(2 * j + 1);
                array[n + 5 * j + 0] = (byte)n6;
                array[n + 5 * j + 1] = (byte)(n6 >> 8);
                array[n + 5 * j + 2] = (byte)((byte)(n6 >> 16) | n7 << 4);
                array[n + 5 * j + 3] = (byte)(n7 >> 4);
                array[n + 5 * j + 4] = (byte)(n7 >> 12);
            }
        }
    }
    
    void zUnpack(final byte[] array) {
        if (this.engine.getDilithiumGamma1() == 131072) {
            for (int i = 0; i < 64; ++i) {
                this.setCoeffIndex(4 * i + 0, ((array[9 * i + 0] & 0xFF) | (array[9 * i + 1] & 0xFF) << 8 | (array[9 * i + 2] & 0xFF) << 16) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 1, ((array[9 * i + 2] & 0xFF) >>> 2 | (array[9 * i + 3] & 0xFF) << 6 | (array[9 * i + 4] & 0xFF) << 14) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 2, ((array[9 * i + 4] & 0xFF) >>> 4 | (array[9 * i + 5] & 0xFF) << 4 | (array[9 * i + 6] & 0xFF) << 12) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 3, ((array[9 * i + 6] & 0xFF) >>> 6 | (array[9 * i + 7] & 0xFF) << 2 | (array[9 * i + 8] & 0xFF) << 10) & 0x3FFFF);
                this.setCoeffIndex(4 * i + 0, this.engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 0));
                this.setCoeffIndex(4 * i + 1, this.engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 1));
                this.setCoeffIndex(4 * i + 2, this.engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 2));
                this.setCoeffIndex(4 * i + 3, this.engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 3));
            }
        }
        else {
            if (this.engine.getDilithiumGamma1() != 524288) {
                throw new RuntimeException("Wrong Dilithium Gamma1!");
            }
            for (int j = 0; j < 128; ++j) {
                this.setCoeffIndex(2 * j + 0, ((array[5 * j + 0] & 0xFF) | (array[5 * j + 1] & 0xFF) << 8 | (array[5 * j + 2] & 0xFF) << 16) & 0xFFFFF);
                this.setCoeffIndex(2 * j + 1, ((array[5 * j + 2] & 0xFF) >>> 4 | (array[5 * j + 3] & 0xFF) << 4 | (array[5 * j + 4] & 0xFF) << 12) & 0xFFFFF);
                this.setCoeffIndex(2 * j + 0, this.engine.getDilithiumGamma1() - this.getCoeffIndex(2 * j + 0));
                this.setCoeffIndex(2 * j + 1, this.engine.getDilithiumGamma1() - this.getCoeffIndex(2 * j + 1));
            }
        }
    }
    
    public void shiftLeft() {
        for (int i = 0; i < 256; ++i) {
            this.setCoeffIndex(i, this.getCoeffIndex(i) << 13);
        }
    }
    
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < this.coeffs.length; ++i) {
            sb.append(this.coeffs[i]);
            if (i != this.coeffs.length - 1) {
                sb.append(", ");
            }
        }
        sb.append("]");
        return sb.toString();
    }
}
