// Exercise 3: laziness/short-circuiting and a custom functional interface (hardest)
//
// Implement:
//   - firstMatchingCount(nums, predicate): a small instrumented pipeline —
//     use a stream to find the FIRST number matching `predicate` via
//     findFirst(), incrementing the static EVALUATIONS counter once per
//     element actually examined (inside the predicate lambda you pass to
//     filter). Because filter+findFirst is lazy/short-circuiting, this
//     must NOT examine elements past the first match.
//   - applyTwice(value, op): takes a custom functional interface `IntOp`
//     (defined below) and applies it to `value` twice in a row,
//     e.g. applyTwice(3, x -> x * 2) === (3*2)*2 === 12.

import java.util.List;
import java.util.OptionalInt;

public class java_streams_and_lambdas_exercise_3 {

    static int EVALUATIONS = 0;

    @FunctionalInterface
    interface IntOp {
        int apply(int x);
    }

    static OptionalInt firstMatchingCount(List<Integer> nums, java.util.function.IntPredicate predicate) {
        // TODO: implement using nums.stream().mapToInt(Integer::intValue)
        //       .filter(n -> { EVALUATIONS++; return predicate.test(n); })
        //       .findFirst()
        return OptionalInt.empty();
    }

    static int applyTwice(int value, IntOp op) {
        // TODO: implement — return op.apply(op.apply(value))
        return 0;
    }

    // --- self-check: do not modify below this line ---
    public static void main(String[] args) {
        boolean ok = true;

        EVALUATIONS = 0;
        List<Integer> nums = List.of(1, 3, 5, 8, 9, 11, 14);
        OptionalInt firstEven = firstMatchingCount(nums, n -> n % 2 == 0);
        ok &= firstEven.isPresent() && firstEven.getAsInt() == 8;
        // 1,3,5 examined before the match at index 3 (value 8) => exactly 4 evaluations,
        // NOT all 7 — proves short-circuiting stopped the pipeline at the first match.
        ok &= EVALUATIONS == 4;

        EVALUATIONS = 0;
        OptionalInt noMatch = firstMatchingCount(List.of(1, 3, 5), n -> n % 2 == 0);
        ok &= noMatch.isEmpty();
        ok &= EVALUATIONS == 3; // exhausted the whole list with no match

        ok &= applyTwice(3, x -> x * 2) == 12;
        ok &= applyTwice(5, x -> x + 1) == 7;
        ok &= applyTwice(2, x -> x * x) == 16; // (2*2)=4, then (4*4)=16

        System.out.println(ok ? "PASS" : "FAIL");
    }
}
