All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaslang.test.Property Maven / Gradle / Ivy

There is a newer version: 2.1.0-alpha
Show newest version
/*     / \____  _    _  ____   ______  / \ ____  __    _______
 *    /  /    \/ \  / \/    \ /  /\__\/  //    \/  \  //  /\__\   JΛVΛSLΛNG
 *  _/  /  /\  \  \/  /  /\  \\__\\  \  //  /\  \ /\\/ \ /__\ \   Copyright 2014-2017 Javaslang, http://javaslang.io
 * /___/\_/  \_/\____/\_/  \_/\__\/__/\__\_/  \_//  \__/\_____/   Licensed under the Apache License, Version 2.0
 */
package javaslang.test;

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*\
   G E N E R A T O R   C R A F T E D
\*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

import java.util.Objects;
import java.util.Random;
import javaslang.*;
import javaslang.control.Option;
import javaslang.control.Try;
import javaslang.control.Try.NonFatalException;

/**
 * A property builder which provides a fluent API to build checkable properties.
 *
 * @author Daniel Dietrich
 * @since 1.2.0
 */
public class Property {

    private final String name;

    private Property(String name) {
        this.name = name;
    }

    /**
     * Defines a new Property.
     *
     * @param name property name
     * @return a new {@code Property}
     * @throws NullPointerException if name is null.
     * @throws IllegalArgumentException if name is empty or consists of whitespace only
     */
    public static Property def(String name) {
        Objects.requireNonNull(name, "name is null");
        if (name.trim().isEmpty()) {
            throw new IllegalArgumentException("name is empty");
        }
        return new Property(name);
    }

    private static void logSatisfied(String name, int tries, long millis, boolean exhausted) {
        if (exhausted) {
            log(String.format("%s: Exhausted after %s tests in %s ms.", name, tries, millis));
        } else {
            log(String.format("%s: OK, passed %s tests in %s ms.", name, tries, millis));
        }
    }

    private static void logFalsified(String name, int currentTry, long millis) {
        log(String.format("%s: Falsified after %s passed tests in %s ms.", name, currentTry - 1, millis));
    }

    private static void logErroneous(String name, int currentTry, long millis, String errorMessage) {
        log(String.format("%s: Errored after %s passed tests in %s ms with message: %s", name, Math.max(0, currentTry - 1), millis, errorMessage));
    }

    private static void log(String msg) {
        System.out.println(msg);
    }

    /**
     * Creates an Error caused by an exception when obtaining a generator.
     *
     * @param position The position of the argument within the argument list of the property, starting with 1.
     * @param size     The size hint passed to the {@linkplain Arbitrary} which caused the error.
     * @param cause    The error which occurred when the {@linkplain Arbitrary} tried to obtain the generator {@linkplain Gen}.
     * @return a new Error instance.
     */
    private static Error arbitraryError(int position, int size, Throwable cause) {
        return new Error(String.format("Arbitrary %s of size %s: %s", position, size, cause.getMessage()), cause);
    }

    /**
     * Creates an Error caused by an exception when generating a value.
     *
     * @param position The position of the argument within the argument list of the property, starting with 1.
     * @param size     The size hint of the arbitrary which called the generator {@linkplain Gen} which caused the error.
     * @param cause    The error which occurred when the {@linkplain Gen} tried to generate a random value.
     * @return a new Error instance.
     */
    private static Error genError(int position, int size, Throwable cause) {
        return new Error(String.format("Gen %s of size %s: %s", position, size, cause.getMessage()), cause);
    }

    /**
     * Creates an Error caused by an exception when testing a Predicate.
     *
     * @param cause The error which occurred when applying the {@linkplain java.util.function.Predicate}.
     * @return a new Error instance.
     */
    private static Error predicateError(Throwable cause) {
        return new Error("Applying predicate: " + cause.getMessage(), cause);
    }

    /**
     * Returns a logical for all quantor of 1 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @return a new {@code ForAll1} instance of 1 variables
     */
    public  ForAll1 forAll(Arbitrary a1) {
        return new ForAll1<>(name, a1);
    }

    /**
     * Returns a logical for all quantor of 2 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @return a new {@code ForAll2} instance of 2 variables
     */
    public  ForAll2 forAll(Arbitrary a1, Arbitrary a2) {
        return new ForAll2<>(name, a1, a2);
    }

    /**
     * Returns a logical for all quantor of 3 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @return a new {@code ForAll3} instance of 3 variables
     */
    public  ForAll3 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3) {
        return new ForAll3<>(name, a1, a2, a3);
    }

    /**
     * Returns a logical for all quantor of 4 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @param a4 4th variable of this for all quantor
     * @return a new {@code ForAll4} instance of 4 variables
     */
    public  ForAll4 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4) {
        return new ForAll4<>(name, a1, a2, a3, a4);
    }

    /**
     * Returns a logical for all quantor of 5 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @param a4 4th variable of this for all quantor
     * @param a5 5th variable of this for all quantor
     * @return a new {@code ForAll5} instance of 5 variables
     */
    public  ForAll5 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5) {
        return new ForAll5<>(name, a1, a2, a3, a4, a5);
    }

    /**
     * Returns a logical for all quantor of 6 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @param a4 4th variable of this for all quantor
     * @param a5 5th variable of this for all quantor
     * @param a6 6th variable of this for all quantor
     * @return a new {@code ForAll6} instance of 6 variables
     */
    public  ForAll6 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6) {
        return new ForAll6<>(name, a1, a2, a3, a4, a5, a6);
    }

    /**
     * Returns a logical for all quantor of 7 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @param  7th variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @param a4 4th variable of this for all quantor
     * @param a5 5th variable of this for all quantor
     * @param a6 6th variable of this for all quantor
     * @param a7 7th variable of this for all quantor
     * @return a new {@code ForAll7} instance of 7 variables
     */
    public  ForAll7 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7) {
        return new ForAll7<>(name, a1, a2, a3, a4, a5, a6, a7);
    }

    /**
     * Returns a logical for all quantor of 8 given variables.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @param  7th variable type of this for all quantor
     * @param  8th variable type of this for all quantor
     * @param a1 1st variable of this for all quantor
     * @param a2 2nd variable of this for all quantor
     * @param a3 3rd variable of this for all quantor
     * @param a4 4th variable of this for all quantor
     * @param a5 5th variable of this for all quantor
     * @param a6 6th variable of this for all quantor
     * @param a7 7th variable of this for all quantor
     * @param a8 8th variable of this for all quantor
     * @return a new {@code ForAll8} instance of 8 variables
     */
    public  ForAll8 forAll(Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7, Arbitrary a8) {
        return new ForAll8<>(name, a1, a2, a3, a4, a5, a6, a7, a8);
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll1 {

        private final String name;
        private final Arbitrary a1;

        ForAll1(String name, Arbitrary a1) {
            this.name = name;
            this.a1 = a1;
        }

        /**
         * Returns a checkable property that checks values of the 1 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 1-ary predicate
         * @return a new {@code Property1} of 1 variables.
         */
        public Property1 suchThat(CheckedFunction1 predicate) {
            final CheckedFunction1 proposition = (t1) -> new Condition(true, predicate.apply(t1));
            return new Property1<>(name, a1, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll2 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;

        ForAll2(String name, Arbitrary a1, Arbitrary a2) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
        }

        /**
         * Returns a checkable property that checks values of the 2 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 2-ary predicate
         * @return a new {@code Property2} of 2 variables.
         */
        public Property2 suchThat(CheckedFunction2 predicate) {
            final CheckedFunction2 proposition = (t1, t2) -> new Condition(true, predicate.apply(t1, t2));
            return new Property2<>(name, a1, a2, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll3 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;

        ForAll3(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
        }

        /**
         * Returns a checkable property that checks values of the 3 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 3-ary predicate
         * @return a new {@code Property3} of 3 variables.
         */
        public Property3 suchThat(CheckedFunction3 predicate) {
            final CheckedFunction3 proposition = (t1, t2, t3) -> new Condition(true, predicate.apply(t1, t2, t3));
            return new Property3<>(name, a1, a2, a3, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll4 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;

        ForAll4(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
        }

        /**
         * Returns a checkable property that checks values of the 4 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 4-ary predicate
         * @return a new {@code Property4} of 4 variables.
         */
        public Property4 suchThat(CheckedFunction4 predicate) {
            final CheckedFunction4 proposition = (t1, t2, t3, t4) -> new Condition(true, predicate.apply(t1, t2, t3, t4));
            return new Property4<>(name, a1, a2, a3, a4, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll5 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;

        ForAll5(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
        }

        /**
         * Returns a checkable property that checks values of the 5 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 5-ary predicate
         * @return a new {@code Property5} of 5 variables.
         */
        public Property5 suchThat(CheckedFunction5 predicate) {
            final CheckedFunction5 proposition = (t1, t2, t3, t4, t5) -> new Condition(true, predicate.apply(t1, t2, t3, t4, t5));
            return new Property5<>(name, a1, a2, a3, a4, a5, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll6 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;

        ForAll6(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
        }

        /**
         * Returns a checkable property that checks values of the 6 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 6-ary predicate
         * @return a new {@code Property6} of 6 variables.
         */
        public Property6 suchThat(CheckedFunction6 predicate) {
            final CheckedFunction6 proposition = (t1, t2, t3, t4, t5, t6) -> new Condition(true, predicate.apply(t1, t2, t3, t4, t5, t6));
            return new Property6<>(name, a1, a2, a3, a4, a5, a6, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @param  7th variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll7 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;
        private final Arbitrary a7;

        ForAll7(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
            this.a7 = a7;
        }

        /**
         * Returns a checkable property that checks values of the 7 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 7-ary predicate
         * @return a new {@code Property7} of 7 variables.
         */
        public Property7 suchThat(CheckedFunction7 predicate) {
            final CheckedFunction7 proposition = (t1, t2, t3, t4, t5, t6, t7) -> new Condition(true, predicate.apply(t1, t2, t3, t4, t5, t6, t7));
            return new Property7<>(name, a1, a2, a3, a4, a5, a6, a7, proposition);
        }
    }

    /**
     * Represents a logical for all quantor.
     *
     * @param  1st variable type of this for all quantor
     * @param  2nd variable type of this for all quantor
     * @param  3rd variable type of this for all quantor
     * @param  4th variable type of this for all quantor
     * @param  5th variable type of this for all quantor
     * @param  6th variable type of this for all quantor
     * @param  7th variable type of this for all quantor
     * @param  8th variable type of this for all quantor
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class ForAll8 {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;
        private final Arbitrary a7;
        private final Arbitrary a8;

        ForAll8(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7, Arbitrary a8) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
            this.a7 = a7;
            this.a8 = a8;
        }

        /**
         * Returns a checkable property that checks values of the 8 variables of this {@code ForAll} quantor.
         *
         * @param predicate A 8-ary predicate
         * @return a new {@code Property8} of 8 variables.
         */
        public Property8 suchThat(CheckedFunction8 predicate) {
            final CheckedFunction8 proposition = (t1, t2, t3, t4, t5, t6, t7, t8) -> new Condition(true, predicate.apply(t1, t2, t3, t4, t5, t6, t7, t8));
            return new Property8<>(name, a1, a2, a3, a4, a5, a6, a7, a8, proposition);
        }
    }

    /**
     * Represents a 1-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property1 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final CheckedFunction1 predicate;

        Property1(String name, Arbitrary a1, CheckedFunction1 predicate) {
            this.name = name;
            this.a1 = a1;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction1 postcondition) {
            final CheckedFunction1 implication = (t1) -> {
                final Condition precondition = predicate.apply(t1);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1));
                }
            };
            return new Property1<>(name, a1, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 2-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property2 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final CheckedFunction2 predicate;

        Property2(String name, Arbitrary a1, Arbitrary a2, CheckedFunction2 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction2 postcondition) {
            final CheckedFunction2 implication = (t1, t2) -> {
                final Condition precondition = predicate.apply(t1, t2);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2));
                }
            };
            return new Property2<>(name, a1, a2, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 3-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property3 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final CheckedFunction3 predicate;

        Property3(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, CheckedFunction3 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction3 postcondition) {
            final CheckedFunction3 implication = (t1, t2, t3) -> {
                final Condition precondition = predicate.apply(t1, t2, t3);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3));
                }
            };
            return new Property3<>(name, a1, a2, a3, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 4-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property4 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final CheckedFunction4 predicate;

        Property4(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, CheckedFunction4 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction4 postcondition) {
            final CheckedFunction4 implication = (t1, t2, t3, t4) -> {
                final Condition precondition = predicate.apply(t1, t2, t3, t4);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3, t4));
                }
            };
            return new Property4<>(name, a1, a2, a3, a4, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                final Gen gen4 = Try.of(() -> a4.apply(size)).recover(x -> { throw arbitraryError(4, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        final T4 val4 = Try.of(() -> gen4.apply(random)).recover(x -> { throw genError(4, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3, val4)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 5-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property5 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final CheckedFunction5 predicate;

        Property5(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, CheckedFunction5 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction5 postcondition) {
            final CheckedFunction5 implication = (t1, t2, t3, t4, t5) -> {
                final Condition precondition = predicate.apply(t1, t2, t3, t4, t5);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3, t4, t5));
                }
            };
            return new Property5<>(name, a1, a2, a3, a4, a5, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                final Gen gen4 = Try.of(() -> a4.apply(size)).recover(x -> { throw arbitraryError(4, size, x); }).get();
                final Gen gen5 = Try.of(() -> a5.apply(size)).recover(x -> { throw arbitraryError(5, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        final T4 val4 = Try.of(() -> gen4.apply(random)).recover(x -> { throw genError(4, size, x); }).get();
                        final T5 val5 = Try.of(() -> gen5.apply(random)).recover(x -> { throw genError(5, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3, val4, val5)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 6-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property6 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;
        private final CheckedFunction6 predicate;

        Property6(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, CheckedFunction6 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction6 postcondition) {
            final CheckedFunction6 implication = (t1, t2, t3, t4, t5, t6) -> {
                final Condition precondition = predicate.apply(t1, t2, t3, t4, t5, t6);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3, t4, t5, t6));
                }
            };
            return new Property6<>(name, a1, a2, a3, a4, a5, a6, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                final Gen gen4 = Try.of(() -> a4.apply(size)).recover(x -> { throw arbitraryError(4, size, x); }).get();
                final Gen gen5 = Try.of(() -> a5.apply(size)).recover(x -> { throw arbitraryError(5, size, x); }).get();
                final Gen gen6 = Try.of(() -> a6.apply(size)).recover(x -> { throw arbitraryError(6, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        final T4 val4 = Try.of(() -> gen4.apply(random)).recover(x -> { throw genError(4, size, x); }).get();
                        final T5 val5 = Try.of(() -> gen5.apply(random)).recover(x -> { throw genError(5, size, x); }).get();
                        final T6 val6 = Try.of(() -> gen6.apply(random)).recover(x -> { throw genError(6, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3, val4, val5, val6)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 7-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property7 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;
        private final Arbitrary a7;
        private final CheckedFunction7 predicate;

        Property7(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7, CheckedFunction7 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
            this.a7 = a7;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction7 postcondition) {
            final CheckedFunction7 implication = (t1, t2, t3, t4, t5, t6, t7) -> {
                final Condition precondition = predicate.apply(t1, t2, t3, t4, t5, t6, t7);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3, t4, t5, t6, t7));
                }
            };
            return new Property7<>(name, a1, a2, a3, a4, a5, a6, a7, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                final Gen gen4 = Try.of(() -> a4.apply(size)).recover(x -> { throw arbitraryError(4, size, x); }).get();
                final Gen gen5 = Try.of(() -> a5.apply(size)).recover(x -> { throw arbitraryError(5, size, x); }).get();
                final Gen gen6 = Try.of(() -> a6.apply(size)).recover(x -> { throw arbitraryError(6, size, x); }).get();
                final Gen gen7 = Try.of(() -> a7.apply(size)).recover(x -> { throw arbitraryError(7, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        final T4 val4 = Try.of(() -> gen4.apply(random)).recover(x -> { throw genError(4, size, x); }).get();
                        final T5 val5 = Try.of(() -> gen5.apply(random)).recover(x -> { throw genError(5, size, x); }).get();
                        final T6 val6 = Try.of(() -> gen6.apply(random)).recover(x -> { throw genError(6, size, x); }).get();
                        final T7 val7 = Try.of(() -> gen7.apply(random)).recover(x -> { throw genError(7, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3, val4, val5, val6, val7)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6, val7));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Represents a 8-ary checkable property.
     *
     * @author Daniel Dietrich
     * @since 1.2.0
     */
    public static class Property8 implements Checkable {

        private final String name;
        private final Arbitrary a1;
        private final Arbitrary a2;
        private final Arbitrary a3;
        private final Arbitrary a4;
        private final Arbitrary a5;
        private final Arbitrary a6;
        private final Arbitrary a7;
        private final Arbitrary a8;
        private final CheckedFunction8 predicate;

        Property8(String name, Arbitrary a1, Arbitrary a2, Arbitrary a3, Arbitrary a4, Arbitrary a5, Arbitrary a6, Arbitrary a7, Arbitrary a8, CheckedFunction8 predicate) {
            this.name = name;
            this.a1 = a1;
            this.a2 = a2;
            this.a3 = a3;
            this.a4 = a4;
            this.a5 = a5;
            this.a6 = a6;
            this.a7 = a7;
            this.a8 = a8;
            this.predicate = predicate;
        }

        /**
         * Returns an implication which composes this Property as pre-condition and a given post-condition.
         *
         * @param postcondition The postcondition of this implication
         * @return A new Checkable implication
         */
        public Checkable implies(CheckedFunction8 postcondition) {
            final CheckedFunction8 implication = (t1, t2, t3, t4, t5, t6, t7, t8) -> {
                final Condition precondition = predicate.apply(t1, t2, t3, t4, t5, t6, t7, t8);
                if (precondition.isFalse()) {
                    return Condition.EX_FALSO_QUODLIBET;
                } else {
                    return new Condition(true, postcondition.apply(t1, t2, t3, t4, t5, t6, t7, t8));
                }
            };
            return new Property8<>(name, a1, a2, a3, a4, a5, a6, a7, a8, implication);
        }

        @Override
        public CheckResult check(Random random, int size, int tries) {
            Objects.requireNonNull(random, "random is null");
            if (tries < 0) {
                throw new IllegalArgumentException("tries < 0");
            }
            final long startTime = System.currentTimeMillis();
            try {
                final Gen gen1 = Try.of(() -> a1.apply(size)).recover(x -> { throw arbitraryError(1, size, x); }).get();
                final Gen gen2 = Try.of(() -> a2.apply(size)).recover(x -> { throw arbitraryError(2, size, x); }).get();
                final Gen gen3 = Try.of(() -> a3.apply(size)).recover(x -> { throw arbitraryError(3, size, x); }).get();
                final Gen gen4 = Try.of(() -> a4.apply(size)).recover(x -> { throw arbitraryError(4, size, x); }).get();
                final Gen gen5 = Try.of(() -> a5.apply(size)).recover(x -> { throw arbitraryError(5, size, x); }).get();
                final Gen gen6 = Try.of(() -> a6.apply(size)).recover(x -> { throw arbitraryError(6, size, x); }).get();
                final Gen gen7 = Try.of(() -> a7.apply(size)).recover(x -> { throw arbitraryError(7, size, x); }).get();
                final Gen gen8 = Try.of(() -> a8.apply(size)).recover(x -> { throw arbitraryError(8, size, x); }).get();
                boolean exhausted = true;
                for (int i = 1; i <= tries; i++) {
                    try {
                        final T1 val1 = Try.of(() -> gen1.apply(random)).recover(x -> { throw genError(1, size, x); }).get();
                        final T2 val2 = Try.of(() -> gen2.apply(random)).recover(x -> { throw genError(2, size, x); }).get();
                        final T3 val3 = Try.of(() -> gen3.apply(random)).recover(x -> { throw genError(3, size, x); }).get();
                        final T4 val4 = Try.of(() -> gen4.apply(random)).recover(x -> { throw genError(4, size, x); }).get();
                        final T5 val5 = Try.of(() -> gen5.apply(random)).recover(x -> { throw genError(5, size, x); }).get();
                        final T6 val6 = Try.of(() -> gen6.apply(random)).recover(x -> { throw genError(6, size, x); }).get();
                        final T7 val7 = Try.of(() -> gen7.apply(random)).recover(x -> { throw genError(7, size, x); }).get();
                        final T8 val8 = Try.of(() -> gen8.apply(random)).recover(x -> { throw genError(8, size, x); }).get();
                        try {
                            final Condition condition = Try.of(() -> predicate.apply(val1, val2, val3, val4, val5, val6, val7, val8)).recover(x -> { throw predicateError(x); }).get();
                            if (condition.precondition) {
                                exhausted = false;
                                if (!condition.postcondition) {
                                    logFalsified(name, i, System.currentTimeMillis() - startTime);
                                    return new CheckResult.Falsified(name, i, Tuple.of(val1, val2, val3, val4, val5, val6, val7, val8));
                                }
                            }
                        } catch(NonFatalException nonFatal) {
                            logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                            return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.some(Tuple.of(val1, val2, val3, val4, val5, val6, val7, val8)));
                        }
                    } catch(NonFatalException nonFatal) {
                        logErroneous(name, i, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                        return new CheckResult.Erroneous(name, i, (Error) nonFatal.getCause(), Option.none());
                    }
                }
                logSatisfied(name, tries, System.currentTimeMillis() - startTime, exhausted);
                return new CheckResult.Satisfied(name, tries, exhausted);
            } catch(NonFatalException nonFatal) {
                logErroneous(name, 0, System.currentTimeMillis() - startTime, nonFatal.getCause().getMessage());
                return new CheckResult.Erroneous(name, 0, (Error) nonFatal.getCause(), Option.none());
            }
        }
    }

    /**
     * Internally used to model conditions composed of pre- and post-condition.
     */
    static class Condition {

        static final Condition EX_FALSO_QUODLIBET = new Condition(false, true);

        final boolean precondition;
        final boolean postcondition;

        Condition(boolean precondition, boolean postcondition) {
            this.precondition = precondition;
            this.postcondition = postcondition;
        }

        // ¬(p => q) ≡ ¬(¬p ∨ q) ≡ p ∧ ¬q
        boolean isFalse() {
            return precondition && !postcondition;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy