/*
 * Copyright (c) 2024, 2025, Oracle and/or its affiliates. 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.
 */

/*
 * @test
 * @bug 8366845
 * @summary TODO: desc
 * @modules java.base/jdk.internal.misc
 * @library /test/lib /
 * @run driver compiler.loopopts.superword.Test_8366845
 */

package compiler.loopopts.superword;
// --- IMPORTS start ---
import compiler.lib.ir_framework.*;
import java.lang.foreign.*;
import java.util.Random;
import compiler.lib.generators.*;
import compiler.lib.verify.*;
import jdk.test.lib.Utils;
// --- IMPORTS end   ---
public class Test_8366845 {
// --- CLASS_HOOK insertions start ---
// --- CLASS_HOOK insertions end   ---
    public static void main(String[] vmFlags) {
        TestFramework framework = new TestFramework(Test_8366845.class);
        framework.addFlags(vmFlags);
        framework.start();
    }
// --- LIST OF TESTS start ---
private static final Random RANDOM = Utils.getRandomInstance();

public static record IndexForm(int con, int ivScale, int invar0Scale, int[] invarRestScales, int size) {
    public IndexForm {
        if (ivScale == 0 || invar0Scale == 0) {
            throw new RuntimeException("Bad scales: " + ivScale + " " + invar0Scale);
        }
    }

    public static record Range(int lo, int hi) {
        public Range {
            if (lo >= hi) { throw new RuntimeException("Bad range: " + lo + " " + hi); }
        }
    }

    public int err() {
        int sum = 0;
        for (int scale : invarRestScales) { sum += Math.abs(scale); }
        return sum;
    }

    public int invar0ForIvLo(Range range, int ivLo) {
        if (ivScale > 0) {
            // index(iv) is smallest for iv = ivLo, so we must satisfy:
            //   range.lo <= con + iv.lo * ivScale + invar0 * invar0Scale + invarRest
            //            <= con + iv.lo * ivScale + invar0 * invar0Scale - err
            // It follows:
            //   invar0 * invar0Scale >= range.lo - con - iv.lo * ivScale + err
            int rhs = range.lo() - con - ivLo * ivScale + err();
            int invar0 = (invar0Scale > 0)
            ?
                // invar0 * invar0Scale >=  range.lo - con - iv.lo * ivScale + err
                // invar0               >= (range.lo - con - iv.lo * ivScale + err) / invar0Scale
                Math.floorDiv(rhs + invar0Scale - 1, invar0Scale) // round up division
            :
                // invar0 * invar0Scale >=  range.lo - con - iv.lo * ivScale + err
                // invar0               <= (range.lo - con - iv.lo * ivScale + err) / invar0Scale
                Math.floorDiv(rhs, invar0Scale); // round down division
            if (range.lo() > con + ivLo * ivScale + invar0 * invar0Scale - err()) {
                throw new RuntimeException("sanity check failed (1)");
            }
            return invar0;
        } else {
            // index(iv) is largest for iv = ivLo, so we must satisfy:
            //   range.hi >= con + iv.lo * ivScale + invar0 * invar0Scale + invarRest + size
            //            >= con + iv.lo * ivScale + invar0 * invar0Scale + err       + size
            // It follows:
            //   invar0 * invar0Scale <= range.hi - con - iv.lo * ivScale - err - size
            int rhs = range.hi() - con - ivLo * ivScale - err() - size();
            int invar0 = (invar0Scale > 0)
            ?
                // invar0 * invar0Scale <= rhs
                // invar0               <= rhs / invar0Scale
                Math.floorDiv(rhs, invar0Scale) // round down division
            :
                // invar0 * invar0Scale <= rhs
                // invar0               >= rhs / invar0Scale
                Math.floorDiv(rhs + invar0Scale + 1, invar0Scale); // round up division
            if (range.hi() < con + ivLo * ivScale + invar0 * invar0Scale + err() + size()) {
                throw new RuntimeException("sanity check failed (2)");
            }
            return invar0;

        }
    }

    public int ivHiForInvar0(Range range, int invar0) {
        if (ivScale > 0) {
            // index(iv) is largest for iv = ivHi, so we must satisfy:
            //   range.hi >= con + iv.hi * ivScale + invar0 * invar0Scale + invarRest + size
            //            >= con + iv.hi * ivScale + invar0 * invar0Scale + err       + size
            // It follows:
            //   iv.hi * ivScale <=  range.hi - con - invar0 * invar0Scale - err - size
            //   iv.hi           <= (range.hi - con - invar0 * invar0Scale - err - size) / ivScale
            int rhs = range.hi() - con - invar0 * invar0Scale - err() - size();
            int ivHi = Math.floorDiv(rhs, ivScale); // round down division
            if (range.hi() < con + ivHi * ivScale + invar0 * invar0Scale + err() + size()) {
                throw new RuntimeException("sanity check failed (3)");
            }
            return ivHi;
        } else {
            // index(iv) is smallest for iv = ivHi, so we must satisfy:
            //   range.lo <= con + iv.hi * ivScale + invar0 * invar0Scale + invarRest
            //            <= con + iv.hi * ivScale + invar0 * invar0Scale - err
            // It follows:
            //   iv.hi * ivScale >=  range.lo - con - invar0 * invar0Scale + err
            //   iv.hi           <= (range.lo - con - invar0 * invar0Scale + err) / ivScale
            int rhs = range.lo() - con - invar0 * invar0Scale + err();
            int ivHi = Math.floorDiv(rhs, ivScale); // round down division
            if (range.lo() > con + ivHi * ivScale + invar0 * invar0Scale - err()) {
                throw new RuntimeException("sanity check failed (4)");
            }
            return ivHi;

        }
    }
}


// --- test_955 end ---
// --- test_989 start ---
// invarRest fields:
private static int invar0_989 = 0;
private static int invar1_989 = 0;
private static int invar2_989 = 0;
// Containers fields:
private static MemorySegment original_container0_989  = MemorySegment.ofArray(new float[36756]);
private static MemorySegment test_container0_989      = MemorySegment.ofArray(new float[36756]);
private static MemorySegment reference_container0_989 = MemorySegment.ofArray(new float[36756]);
private static MemorySegment original_container1_989  = MemorySegment.ofArray(new float[36756]);
private static MemorySegment test_container1_989      = MemorySegment.ofArray(new float[36756]);
private static MemorySegment reference_container1_989 = MemorySegment.ofArray(new float[36756]);
// Index forms for the accesses:
private static IndexForm index0_989 = new IndexForm(-46316, -8, -8, new int[] {1, 1, 1}, 8);
private static IndexForm index1_989 = new IndexForm(-23107, -4, 4, new int[] {0, 1, 1}, 4);
// Count the run invocations.
private static int iterations_989 = 0;

@Run(test = "test_989")
@Warmup(100)
public static void run_989(RunInfo info) {

    // Once warmup is over (100x), repeat 10x to get reasonable coverage of the
    // randomness in the tests.
    int reps = info.isWarmUp() ? 10 : 1;
    for (int r = 0; r < reps; r++) {

        iterations_989++;
// Init containers from original data:
test_container0_989.copyFrom(original_container0_989);
reference_container0_989.copyFrom(original_container0_989);
test_container1_989.copyFrom(original_container1_989);
reference_container1_989.copyFrom(original_container1_989);
// Container aliasing:
var test_0      = (iterations_989 % 2 == 0) ? test_container0_989      : test_container0_989;
var reference_0 = (iterations_989 % 2 == 0) ? reference_container0_989 : reference_container0_989;
var test_1      = (iterations_989 % 2 == 0) ? test_container0_989      : test_container0_989;
var reference_1 = (iterations_989 % 2 == 0) ? reference_container0_989 : reference_container0_989;
// Generate ranges:
int middle = RANDOM.nextInt(147024 / 3, 147024 * 2 / 3);
int rnd = Math.min(256, 147024 / 10);
int range = 147024 / 3 - RANDOM.nextInt(rnd);
var r0 = new IndexForm.Range(0, middle);
var r1 = new IndexForm.Range(middle, 147024);
if (RANDOM.nextBoolean()) {
    var tmp = r0;
    r0 = r1;
    r1 = tmp;
}
// Compute loop bounds and loop invariants.
int ivLo = RANDOM.nextInt(-1000, 1000);
int ivHi = ivLo + 147024;
int invar0_0 = index0_989.invar0ForIvLo(r0, ivLo);
ivHi = Math.min(ivHi, index0_989.ivHiForInvar0(r0, invar0_0));
int invar0_1 = index1_989.invar0ForIvLo(r1, ivLo);
ivHi = Math.min(ivHi, index1_989.ivHiForInvar0(r1, invar0_1));
// Let's check that the range is large enough, so that the vectorized
// main loop can even be entered.
if (ivLo + 1000 > ivHi) { throw new RuntimeException("iv range too small: " + ivLo + " " + ivHi); }
invar0_989 = RANDOM.nextInt(-1, 2);
invar1_989 = RANDOM.nextInt(-1, 2);
invar2_989 = RANDOM.nextInt(-1, 2);
// Verify the bounds we just created, just to be sure there is no unexpected aliasing!
int i = ivLo;
int lo_0 = (int)(-46316 + -8 * i + -8 * invar0_0 + 1 * invar0_989 + 1 * invar1_989 + 1 * invar2_989);
int lo_1 = (int)(-23107 + -4 * i + 4 * invar0_1 + 0 * invar0_989 + 1 * invar1_989 + 1 * invar2_989);
i = ivHi;
int hi_0 =  (int)(-46316 + -8 * i + -8 * invar0_0 + 1 * invar0_989 + 1 * invar1_989 + 1 * invar2_989);
int hi_1 =  (int)(-23107 + -4 * i + 4 * invar0_1 + 0 * invar0_989 + 1 * invar1_989 + 1 * invar2_989);
// Bounds should not overlap.
if (false
|| (lo_1 < lo_0 && lo_1 < hi_0 && hi_1 < lo_0 && hi_1 < hi_0)
|| (lo_1 > lo_0 && lo_1 > hi_0 && hi_1 > lo_0 && hi_1 > hi_0)
) {
    // pass
} else {
    throw new RuntimeException("bounds overlap!");
}
        // Run test and compare with interpreter results.
var result = test_989(test_0, invar0_0, test_1, invar0_1, ivLo, ivHi);
var expected = reference_989(reference_0, invar0_0, reference_1, invar0_1, ivLo, ivHi);
        Verify.checkEQ(result, expected);
    } // end reps
} // end run_989

@Test
// Unfortunately, there are some issues that prevent RangeCheck elimination.
// The cases are currently quite unpredictable, so we cannot create any IR
// rules - sometimes there are vectors sometimes not.
// Due to cases like JDK-8360204 and JDK-8365982, there can be issues
// with RCE leading cases where we remove predicates and then unroll again
// and then end up multiversioning. These cases seem relatively rare but
// prevent us from asserting that there is never multiversioning in these cases.
public static Object test_989(MemorySegment container_0, int invar0_0, MemorySegment container_1, int invar0_1, int ivLo, int ivHi) {
for (int i = ivHi-1; i >= ivLo; i-=1) {
float v = (float)container_0.get(ValueLayout.JAVA_DOUBLE_UNALIGNED, -46316L + -8L * i + -8L * invar0_0 + 1L * invar0_989 + 1L * invar1_989 + 1L * invar2_989);
container_1.set(ValueLayout.JAVA_FLOAT_UNALIGNED, -23107L + -4L * i + 4L * invar0_1 + 0L * invar0_989 + 1L * invar1_989 + 1L * invar2_989, v);
    }
    return new Object[] {
container_0, container_1
    };
}
@DontCompile
public static Object reference_989(MemorySegment container_0, int invar0_0, MemorySegment container_1, int invar0_1, int ivLo, int ivHi) {
for (int i = ivHi-1; i >= ivLo; i-=1) {
float v = (float)container_0.get(ValueLayout.JAVA_DOUBLE_UNALIGNED, -46316L + -8L * i + -8L * invar0_0 + 1L * invar0_989 + 1L * invar1_989 + 1L * invar2_989);
container_1.set(ValueLayout.JAVA_FLOAT_UNALIGNED, -23107L + -4L * i + 4L * invar0_1 + 0L * invar0_989 + 1L * invar1_989 + 1L * invar2_989, v);
    }
    return new Object[] {
container_0, container_1
    };
}

// --- test_989 end ---
// --- LIST OF TESTS end   ---
}
