package sum; import base.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.function.*; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.Stream; /** * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) */ public class SumTester { private static final List SPACES = List.of( " \t\n\u000B\u2029\f", IntStream.rangeClosed(0, Character.MAX_VALUE) .filter(Character::isWhitespace) .mapToObj(Character::toString) .collect(Collectors.joining()) ); private final BinaryOperator add; private final LongFunction fromLong; private BiFunction toString; private final BiFunction randomValue; private final BiConsumer verifier; private List spaces; private final List limits; private final List> tests = new ArrayList<>(); @SafeVarargs public SumTester( final BinaryOperator add, final LongFunction fromLong, final BiFunction randomValue, final BiConsumer verifier, final T... limits ) { this.add = add; this.fromLong = fromLong; this.randomValue = randomValue; this.verifier = verifier; this.limits = List.of(limits); setSpaces(SPACES); setToString(String::valueOf); test(1, "1"); test(6, "1", "2", "3"); test(1, " 1"); test(1, "1 "); test(1, " 1 "); test(12345, " 12345 "); test(60, "010", "020", "030"); testSpaces(1368, " 123 456 789 "); test(-1, "-1"); test(-6, "-1", "-2", "-3"); test(-12345, " -12345 "); testSpaces(-1368, " -123 -456 -789 "); test(1, "+1"); test(6, "+1", "+2", "+3"); test(12345, " +12345 "); testSpaces(1368, " +123 +456 +789 "); test(0); testSpaces(0, " ", " "); } protected SumTester setSpaces(final List spaces) { this.spaces = spaces; return this; } protected SumTester addSpaces(final String... spaces) { this.spaces = Stream.concat(this.spaces.stream(), Arrays.stream(spaces)).toList(); return this; } public SumTester setToString(final Function toString) { return setToString((r, n) -> toString.apply(n)); } public SumTester setToString(final BiFunction toString) { this.toString = toString; return this; } protected SumTester test(final Function toString, final long... input) { return testT( fromLong.apply(LongStream.of(input).sum()), LongStream.of(input).mapToObj(fromLong).map(toString).collect(Collectors.joining(" ")) ); } protected SumTester test(final long result, final String... input) { return testT(fromLong.apply(result), input); } protected SumTester testT(final T result, final String... input) { return testT(result, Arrays.asList(input)); } private SumTester testT(final T result, final List input) { tests.add(checker -> checker.test(result, input)); return this; } public SumTester testSpaces(final long result, final String... input) { final T res = fromLong.apply(result); tests.add(checker -> spaces.stream() .flatMapToInt(String::chars) .forEach(space -> checker.test( res, Functional.map(Arrays.asList(input), s -> s.replace(' ', (char) space)) )) ); return this; } public void test(final String name, final TestCounter counter, final Function runner) { new Checker(counter, runner.apply(name)).test(); } private class Checker extends BaseChecker { private final Runner runner; public Checker(final TestCounter counter, final Runner runner) { super(counter); this.runner = runner; } public void test() { tests.forEach(test -> test.accept(this)); for (final T limit : limits) { for (int n = 10; n <= 10_000 / TestCounter.DENOMINATOR; n *= 10) { randomTest(n, limit); } } } private void test(final T result, final List input) { counter.test(() -> { final List out = runner.run(counter, input); Asserts.assertEquals("Single line expected", 1, out.size()); verifier.accept(result, out.get(0)); }); } private void randomTest(final int numbers, final T max) { for (final String spaces : spaces) { randomTest(numbers, max, spaces); } } private void randomTest(final int numbers, final T max, final String spaces) { final List values = new ArrayList<>(); for (int i = 0; i < numbers; i++) { values.add(randomValue.apply(random(), max)); } testRandom(values.stream().reduce(fromLong.apply(0), add), values, spaces); } private void testRandom(final T result, final List args, final String spaces) { final List spaced = args.stream() .map(n -> toString.apply(random(), n)) .map(value -> randomSpace(spaces) + value + randomSpace(spaces)) .toList(); final List argsList = new ArrayList<>(); for (final Iterator i = spaced.listIterator(); i.hasNext(); ) { final StringBuilder next = new StringBuilder(i.next()); while (i.hasNext() && random().nextBoolean()) { next.append(randomSpace(spaces)).append(i.next()); } argsList.add(next.toString()); } test(result, argsList); } private String randomSpace(final String spaces) { return random().randomString(spaces); } } }