@Override
    public Predicate negate() {
        int size = predicates.length;
        Predicate[] inners = new Predicate[size];
        for (int i = 0; i < size; i++) {
            Predicate original = predicates[i];
            Predicate negated;
            if (original instanceof NegatablePredicate predicate) {
                negated = predicate.negate();
            } else {
                negated = new NotPredicate(original);
            }
            inners[i] = negated;
        }
        OrPredicate orPredicate = new OrPredicate(inners);
        return orPredicate;
    }
@Test
    public void negate_whenContainsNegatablePredicate_thenReturnOrPredicateWithNegationInside() {
        // ~(foo and bar)  -->  (~foo or ~bar)
        // this is testing the case where the inner predicate implements {@link Negatable}

        Predicate negated = mock(Predicate.class);
        Predicate negatable = createMockNegatablePredicate(negated);

        AndPredicate and = (AndPredicate) and(negatable);
        OrPredicate result = (OrPredicate) and.negate();

        Predicate[] inners = result.predicates;
        assertThat(inners).hasSize(1);
        assertThat(inners).containsExactlyInAnyOrder(negated);
    }