import java.util.Random;

public class TestWithPrimitiveAndIfReassociated {
    public static void main(String[] args) {
        Random r = new Random(42);
        long[] array = new long[10_000];
        for (int i = 0; i < array.length; i++) {
            array[i] = r.nextLong() & 0xFFFF_FFFFL;
        }

        System.out.println("Warmup");
        for (int i = 0; i < 100_000; i++) {
            test(array);
        }

        System.out.println("Running");
        for (int run = 0; run < 10; run++) {
            for (int i = 0; i < array.length; i++) {
                array[i] = r.nextLong() & 0xFFFF_FFFFL;
            }
            long t0 = System.nanoTime();
            for (int i = 0; i < 100_000; i++) {
                test(array);
            }
            long t1 = System.nanoTime();
            System.out.println("elapsed: " + (t1 - t0));
        }
    }

    public static long test(long[] array) {
        long x = Integer.MIN_VALUE;
        for (int i = 0; i < array.length; i+=4) {
            long v0 = array[i+0];
            long v1 = array[i+1];
            long v2 = array[i+2];
            long v3 = array[i+3];
            // Control flow that prevents vectorization:
            if (v0 < 0) { throw new RuntimeException("some error condition, probably deopt"); }
            if (v1 < 0) { throw new RuntimeException("some error condition, probably deopt"); }
            if (v2 < 0) { throw new RuntimeException("some error condition, probably deopt"); }
            if (v3 < 0) { throw new RuntimeException("some error condition, probably deopt"); }
            // Reassociate: compute max among the 4 values first, only then add it onto the reduction chain.
            long v = Math.max(Math.max(v0, v1), Math.max(v2, v3));
            x = Math.max(x, v); // now we have a 1/4 as many operations on the reduction chain.
            // Without re-association, we are much slower:
            //x = Math.max(x, v0);
            //x = Math.max(x, v1);
            //x = Math.max(x, v2);
            //x = Math.max(x, v3);
        }
        return x;
    }
}
