/*
 * Copyright (c) 2025, Intel Corporation. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.openjdk.bench.javax.crypto.full;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.OutputTimeUnit;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@Measurement(iterations = 3, time = 10)
@Warmup(iterations = 3, time = 10)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 1, jvmArgs = {"--add-opens", "java.base/sun.security.provider=ALL-UNNAMED"})
public class MLDSABench extends CryptoBase {
    public static final int SET_SIZE = 128;

    private int[][] coeffs1;
    private int[][] coeffs2;
    private int[][] prod1;
    private int[][] prod2;
    private MethodHandle mult, multConst, decompose, almostNtt, inverseNtt;
    int index = 0;

    public static int[][] fillRandom(int[][] data) {
        Random rnd = new Random();
        for (int[] d : data) {
            for (int j = 0; j<d.length; j++) {
                d[j] = rnd.nextInt();
            }
        }
        return data;
    }

    @Setup
    public void setup() throws Exception {
        coeffs1 = fillRandom(new int[SET_SIZE][ML_DSA_N]);
        coeffs2 = fillRandom(new int[SET_SIZE][ML_DSA_N]);
        prod1 = fillRandom(new int[SET_SIZE][ML_DSA_N]);
        prod2 = fillRandom(new int[SET_SIZE][ML_DSA_N]);

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        Class<?> kClazz = Class.forName("sun.security.provider.ML_DSA");
        Constructor<?> constructor = kClazz.getDeclaredConstructor(
                int.class);
        constructor.setAccessible(true);

        Method m = kClazz.getDeclaredMethod("implDilithiumNttMult",
                int[].class, int[].class, int[].class);
        m.setAccessible(true);
        mult = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumMontMulByConstant",
                int[].class, int.class);
        m.setAccessible(true);
        multConst = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumDecomposePoly",
                int[].class, int[].class, int[].class, int.class, int.class);
        m.setAccessible(true);
        decompose = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostNtt",
        int[].class, int[].class);
        m.setAccessible(true);
        almostNtt = lookup.unreflect(m);

        m = kClazz.getDeclaredMethod("implDilithiumAlmostInverseNtt",
        int[].class, int[].class);
        m.setAccessible(true);
        inverseNtt = lookup.unreflect(m);
    }

    @Benchmark
    public void mult() throws Exception, Throwable {
        mult.invoke(prod1[index], coeffs1[index], coeffs2[index]);
        index = (index + 1) % SET_SIZE;
    }

    @Benchmark
    public void multConst() throws Exception, Throwable {
        multConst.invoke(prod1[index], coeffs1[index][index]);
        index = (index + 1) % SET_SIZE;
    }

    @Benchmark
    public void multDecompose() throws Exception, Throwable {
        int gamma2 = 95232;
        if (coeffs1[index][0]%2==1) {
            gamma2 = coeffs1[index][1];
        }
        int multiplier = (gamma2 == 95232 ? 22 : 8);
        decompose.invoke(coeffs1[index], prod1[index], prod2[index], 2 * gamma2, multiplier);
        index = (index + 1) % SET_SIZE;
    }

    @Benchmark
    public void multAlmostNtt() throws Exception, Throwable {
        almostNtt.invoke(coeffs1[index], MONT_ZETAS_FOR_VECTOR_NTT);
        index = (index + 1) % SET_SIZE;
    }

    @Benchmark
    public void multInverseNtt() throws Exception, Throwable {
        inverseNtt.invoke(coeffs2[index], MONT_ZETAS_FOR_VECTOR_INVERSE_NTT);
        index = (index + 1) % SET_SIZE;
    }

    // Copied constants from sun.security.provider.ML_DSA
    private static final int ML_DSA_N = 256;
    private static final int[] MONT_ZETAS_FOR_VECTOR_INVERSE_NTT = new int[]{
            -1976782, 846154, -1400424, -3937738, 1362209, 48306, -3919660, 554416,
            3545687, -1612842, 976891, -183443, 2286327, 420899, 2235985, 2939036,
            3833893, 260646, 1104333, 1667432, -1910376, 1803090, -1723600, 426683,
            -472078, -1717735, 975884, -2213111, -269760, -3866901, -3523897, 3038916,
            1799107, 3694233, -1652634, -810149, -3014001, -1616392, -162844, 3183426,
            1207385, -185531, -3369112, -1957272, 164721, -2454455, -2432395, 2013608,
            3776993, -594136, 3724270, 2584293, 1846953, 1671176, 2831860, 542412,
            -3406031, -2235880, -777191, -1500165, 1374803, 2546312, -1917081, 1279661,
            1962642, -3306115, -1312455, 451100, 1430225, 3318210, -1237275, 1333058,
            1050970, -1903435, -1869119, 2994039, 3548272, -2635921, -1250494, 3767016,
            -1595974, -2486353, -1247620, -4055324, -1265009, 2590150, -2691481, -2842341,
            -203044, -1735879, 3342277, -3437287, -4108315, 2437823, -286988, -342297,
            3595838, 768622, 525098, 3556995, -3207046, -2031748, 3122442, 655327,
            522500, 43260, 1613174, -495491, -819034, -909542, -1859098, -900702,
            3193378, 1197226, 3759364, 3520352, -3513181, 1235728, -2434439, -266997,
            3562462, 2446433, -2244091, 3342478, -3817976, -2316500, -3407706, -2091667,

            -3839961, -3839961, 3628969, 3628969, 3881060, 3881060, 3019102, 3019102,
            1439742, 1439742, 812732, 812732, 1584928, 1584928, -1285669, -1285669,
            -1341330, - 1341330, -1315589, -1315589, 177440, 177440, 2409325, 2409325,
            1851402, 1851402, -3159746, -3159746, 3553272, 3553272, -189548, -189548,
            1316856, 1316856, -759969, -759969, 210977, 210977, -2389356, -2389356,
            3249728, 3249728, -1653064, -1653064, 8578, 8578, 3724342, 3724342,
            -3958618, -3958618, -904516, -904516, 1100098, 1100098, -44288, -44288,
            -3097992, -3097992, -508951, -508951, -264944, -264944, 3343383, 3343383,
            1430430, 1430430, -1852771, -1852771, -1349076, -1349076, 381987, 381987,
            1308169, 1308169, 22981, 22981, 1228525, 1228525, 671102, 671102,
            2477047, 2477047, 411027, 411027, 3693493, 3693493, 2967645, 2967645,
            -2715295, -2715295, -2147896, -2147896, 983419, 983419, -3412210, -3412210,
            -126922, -126922, 3632928, 3632928, 3157330, 3157330, 3190144, 3190144,
            1000202, 1000202, 4083598, 4083598, -1939314, -1939314, 1257611, 1257611,
            1585221, 1585221, -2176455, -2176455, -3475950, -3475950, 1452451, 1452451,
            3041255, 3041255, 3677745, 3677745, 1528703, 1528703, 3930395, 3930395,

            2797779, 2797779, 2797779, 2797779, -2071892, -2071892, -2071892, -2071892,
            2556880, 2556880, 2556880, 2556880, -3900724, -3900724, -3900724, -3900724,
            -3881043, -3881043, -3881043, -3881043, -954230, -954230, -954230, -954230,
            -531354, -531354, -531354, -531354, -811944, -811944, -811944, -811944,
            -3699596, -3699596, -3699596, -3699596, 1600420, 1600420, 1600420, 1600420,
            2140649, 2140649, 2140649, 2140649, -3507263, -3507263, -3507263, -3507263,
            3821735, 3821735, 3821735, 3821735, -3505694, -3505694, -3505694, -3505694,
            1643818, 1643818, 1643818, 1643818, 1699267, 1699267, 1699267, 1699267,
            539299, 539299, 539299, 539299, -2348700, -2348700, -2348700, -2348700,
            300467, 300467, 300467, 300467, -3539968, -3539968, -3539968, -3539968,
            2867647, 2867647, 2867647, 2867647, -3574422, -3574422, -3574422, -3574422,
            3043716, 3043716, 3043716, 3043716, 3861115, 3861115, 3861115, 3861115,
            -3915439, -3915439, -3915439, -3915439, 2537516, 2537516, 2537516, 2537516,
            3592148, 3592148, 3592148, 3592148, 1661693, 1661693, 1661693, 1661693,
            -3530437, -3530437, -3530437, -3530437, -3077325, -3077325, -3077325, -3077325,
            -95776, -95776, -95776, -95776, -2706023, -2706023, -2706023, -2706023,

            -280005, -280005, -280005, -280005, -280005, -280005, -280005, -280005,
            -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497, -4010497,
            19422, 19422, 19422, 19422, 19422, 19422, 19422, 19422,
            -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237, -1757237,
            3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672, 3277672,
            1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561, 1399561,
            3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737, 3859737,
            2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186, 2118186,
            2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549, 2108549,
            -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752, -2619752,
            1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584, 1119584,
            549488, 549488, 549488, 549488, 549488, 549488, 549488, 549488,
            -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928, -3585928,
            1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900, 1079900,
            -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112, -1024112,
            -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464, -2725464,

            -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103,
            -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103, -2680103,
            -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497,
            -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497, -3111497,
            2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855,
            2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855, 2884855,
            -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733,
            -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733, -3119733,
            2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905,
            2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905, 2091905,
            359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251,
            359251, 359251, 359251, 359251, 359251, 359251, 359251, 359251,
            -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451,
            -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451, -2353451,
            -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347,
            -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347, -1826347,

            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            -466468, -466468, -466468, -466468, -466468, -466468, -466468, -466468,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            876248, 876248, 876248, 876248, 876248, 876248, 876248, 876248,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            777960, 777960, 777960, 777960, 777960, 777960, 777960, 777960,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,
            -237124, -237124, -237124, -237124, -237124, -237124, -237124, -237124,

            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            518909, 518909, 518909, 518909, 518909, 518909, 518909, 518909,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,
            2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894, 2608894,

            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847,
            -25847, -25847, -25847, -25847, -25847, -25847, -25847, -25847
    };

    private static final int[] MONT_ZETAS_FOR_VECTOR_NTT = new int[]{
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,
            25847, 25847, 25847, 25847, 25847, 25847, 25847, 25847,

            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894, -2608894,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,
            -518909, -518909, -518909, -518909, -518909, -518909, -518909, -518909,

            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            237124, 237124, 237124, 237124, 237124, 237124, 237124, 237124,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -777960, -777960, -777960, -777960, -777960, -777960, -777960, -777960,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            -876248, -876248, -876248, -876248, -876248, -876248, -876248, -876248,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,
            466468, 466468, 466468, 466468, 466468, 466468, 466468, 466468,

            1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347,
            1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347, 1826347,
            2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451,
            2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451, 2353451,
            -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251,
            -359251, -359251, -359251, -359251, -359251, -359251, -359251, -359251,
            -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905,
            -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905, -2091905,
            3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733,
            3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733, 3119733,
            -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855,
            -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855, -2884855,
            3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497,
            3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497, 3111497,
            2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103,
            2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103, 2680103,

            2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464, 2725464,
            1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112, 1024112,
            -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900, -1079900,
            3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928, 3585928,
            -549488, -549488, -549488, -549488, -549488, -549488, -549488, -549488,
            -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584, -1119584,
            2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752, 2619752,
            -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549, -2108549,
            -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186, -2118186,
            -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737, -3859737,
            -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561, -1399561,
            -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672, -3277672,
            1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237, 1757237,
            -19422, -19422, -19422, -19422, -19422, -19422, -19422, -19422,
            4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497, 4010497,
            280005, 280005, 280005, 280005, 280005, 280005, 280005, 280005,

            2706023, 2706023, 2706023, 2706023, 95776, 95776, 95776, 95776,
            3077325, 3077325, 3077325, 3077325, 3530437, 3530437, 3530437, 3530437,
            -1661693, -1661693, -1661693, -1661693, -3592148, -3592148, -3592148, -3592148,
            -2537516, -2537516, -2537516, -2537516, 3915439, 3915439, 3915439, 3915439,
            -3861115, -3861115, -3861115, -3861115, -3043716, -3043716, -3043716, -3043716,
            3574422, 3574422, 3574422, 3574422, -2867647, -2867647, -2867647, -2867647,
            3539968, 3539968, 3539968, 3539968, -300467, -300467, -300467, -300467,
            2348700, 2348700, 2348700, 2348700, -539299, -539299, -539299, -539299,
            -1699267, -1699267, -1699267, -1699267, -1643818, -1643818, -1643818, -1643818,
            3505694, 3505694, 3505694, 3505694, -3821735, -3821735, -3821735, -3821735,
            3507263, 3507263, 3507263, 3507263, -2140649, -2140649, -2140649, -2140649,
            -1600420, -1600420, -1600420, -1600420, 3699596, 3699596, 3699596, 3699596,
            811944, 811944, 811944, 811944, 531354, 531354, 531354, 531354,
            954230, 954230, 954230, 954230, 3881043, 3881043, 3881043, 3881043,
            3900724, 3900724, 3900724, 3900724, -2556880, -2556880, -2556880, -2556880,
            2071892, 2071892, 2071892, 2071892, -2797779, -2797779, -2797779, -2797779,

            -3930395, -3930395, -1528703, -1528703, -3677745, -3677745, -3041255, -3041255,
            -1452451, -1452451, 3475950, 3475950, 2176455, 2176455, -1585221, -1585221,
            -1257611, -1257611, 1939314, 1939314, -4083598, -4083598, -1000202, -1000202,
            -3190144, -3190144, -3157330, -3157330, -3632928, -3632928, 126922, 126922,
            3412210, 3412210, -983419, -983419, 2147896, 2147896, 2715295, 2715295,
            -2967645, -2967645, -3693493, -3693493, -411027, -411027, -2477047, -2477047,
            -671102, -671102, -1228525, -1228525, -22981, -22981, -1308169, -1308169,
            -381987, -381987, 1349076, 1349076, 1852771, 1852771, -1430430, -1430430,
            -3343383, -3343383, 264944, 264944, 508951, 508951, 3097992, 3097992,
            44288, 44288, -1100098, -1100098, 904516, 904516, 3958618, 3958618,
            -3724342, -3724342, -8578, -8578, 1653064, 1653064, -3249728, -3249728,
            2389356, 2389356, -210977, -210977, 759969, 759969, -1316856, -1316856,
            189548, 189548, -3553272, -3553272, 3159746, 3159746, -1851402, -1851402,
            -2409325, -2409325, -177440, -177440, 1315589, 1315589, 1341330, 1341330,
            1285669, 1285669, -1584928, -1584928, -812732, -812732, -1439742, -1439742,
            -3019102, -3019102, -3881060, -3881060, -3628969, -3628969, 3839961, 3839961,

            2091667, 3407706, 2316500, 3817976, -3342478, 2244091, -2446433, -3562462,
            266997, 2434439, -1235728, 3513181, -3520352, -3759364, -1197226, -3193378,
            900702, 1859098, 909542, 819034, 495491, -1613174, -43260, -522500,
            -655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838,
            342297, 286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044,
            2842341, 2691481, -2590150, 1265009, 4055324, 1247620, 2486353, 1595974,
            -3767016, 1250494, 2635921, -3548272, -2994039, 1869119, 1903435, -1050970,
            -1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115, -1962642,
            -1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031,
            -542412, -2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993,
            -2013608, 2432395, 2454455, -164721, 1957272, 3369112, 185531, -1207385,
            -3183426, 162844, 1616392, 3014001, 810149, 1652634, -3694233, -1799107,
            -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735, 472078,
            -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893,
            -2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687,
            -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782
    };
}
