Upload files to "java/reverse"

This commit is contained in:
2026-04-13 10:44:55 +03:00
parent 1002cf01be
commit 398c8c1929
2 changed files with 575 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
package reverse;
import base.Named;
import base.Selector;
import base.TestCounter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.IntToLongFunction;
import java.util.function.LongBinaryOperator;
import java.util.stream.IntStream;
import reverse.ReverseTester.Op;
/**
* Tests for {@code Reverse} homework.
*
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ReverseTest {
// === Base
public static final Named<Op> REVERSE = Named.of(
"",
ReverseTester::transform
);
// === Max
public static final Named<Op> MAX_C = Named.of("MaxC", scan2((a, b) -> b));
public static final Named<Op> MAX = Named.of("Max", scan2(Math::max));
private static Op scan2(final LongBinaryOperator reduce) {
return ints -> {
// This code is intentionally obscure
final int length = Arrays.stream(ints)
.mapToInt(r -> r.length)
.max()
.orElse(0);
final long[] cs = new long[length];
final long[] cc = new long[length + 1];
Arrays.fill(cs, Integer.MIN_VALUE);
Arrays.fill(cc, Integer.MIN_VALUE);
//noinspection NestedAssignment
final long[][] rows = range(ints.length)
.mapToObj(i -> {
range(ints[i].length).forEachOrdered(j ->
cc[j] = reduce.applyAsLong(
cc[j + 1],
cs[j] = Math.max(cs[j], ints[i][j])
)
);
return Arrays.copyOf(cc, ints[i].length);
})
.toArray(long[][]::new);
return range(ints.length)
.mapToObj(i -> rows[i])
.toArray(long[][]::new);
};
}
private static IntStream range(final int length) {
return IntStream.iterate(length - 1, i -> i >= 0, i -> i - 1);
}
// === Rotate
public static final Named<Op> ROTATE = Named.of("Rotate", ints -> {
final List<int[]> rows = new ArrayList<>(List.of(ints));
return IntStream.range(
0,
Arrays.stream(ints)
.mapToInt(r -> r.length)
.max()
.orElse(0)
)
.mapToObj(c -> {
rows.removeIf(r -> r.length <= c);
return range(rows.size())
.mapToObj(rows::get)
.mapToLong(r -> r[c])
.toArray();
})
.toArray(long[][]::new);
});
// === Even
public static final Named<Op> EVEN = Named.of("Even", ints ->
ReverseTester.transform(
IntStream.range(0, ints.length)
.mapToObj(i ->
IntStream.range(0, ints[i].length)
.filter(j -> (i + j) % 2 == 0)
.map(j -> ints[i][j])
)
.map(IntStream::toArray)
.toArray(int[][]::new)
)
);
// Sum
@FunctionalInterface
interface LongTernaryOperator {
long applyAsLong(long a, long b, long c);
}
public static final Named<Op> SUM = cross(
"Sum",
0,
Long::sum,
(r, c, v) -> r + c - v
);
private static long[][] cross(
final int[][] ints,
final IntToLongFunction map,
final LongBinaryOperator reduce,
final int zero,
final LongTernaryOperator get
) {
// This code is intentionally obscure
final long[] rt = Arrays.stream(ints)
.map(Arrays::stream)
.mapToLong(row -> row.mapToLong(map).reduce(zero, reduce))
.toArray();
final long[] ct = new long[Arrays.stream(ints)
.mapToInt(r -> r.length)
.max()
.orElse(0)];
Arrays.fill(ct, zero);
Arrays.stream(ints).forEach(r ->
IntStream.range(0, r.length).forEach(i ->
ct[i] = reduce.applyAsLong(ct[i], map.applyAsLong(r[i]))
)
);
return IntStream.range(0, ints.length)
.mapToObj(r ->
IntStream.range(0, ints[r].length)
.mapToLong(c -> get.applyAsLong(rt[r], ct[c], ints[r][c]))
.toArray()
)
.toArray(long[][]::new);
}
private static Named<Op> cross(
final String name,
final int zero,
final LongBinaryOperator reduce,
final LongTernaryOperator get
) {
return Named.of(name, ints -> cross(ints, n -> n, reduce, zero, get));
}
public static final Named<Op> AVG = avg(
"Avg",
ints -> cross(ints, n -> n, Long::sum, 0, (r, c, v) -> r + c - v),
ints -> cross(ints, n -> 1, Long::sum, 0, (r1, c1, v1) -> r1 + c1 - 1)
);
private static Named<Op> avg(final String name, final Op fs, final Op fc) {
return Named.of(name, ints ->
avg(ints, fs.apply(ints), fc.apply(ints))
);
}
private static long[][] avg(
final int[][] ints,
final long[][] as,
final long[][] ac
) {
return IntStream.range(0, ints.length)
.mapToObj(i ->
IntStream.range(0, ints[i].length)
.mapToLong(j -> as[i][j] / ac[i][j])
.toArray()
)
.toArray(long[][]::new);
}
// === Common
public static final int MAX_SIZE = 10_000 / TestCounter.DENOMINATOR;
public static final Selector SELECTOR = selector(
ReverseTest.class,
MAX_SIZE
);
private ReverseTest() {
// Utility class
}
public static Selector selector(final Class<?> owner, final int maxSize) {
return new Selector(owner)
.variant("Base", ReverseTester.variant(maxSize, REVERSE))
.variant("3637", ReverseTester.variant(maxSize, MAX_C))
.variant("3839", ReverseTester.variant(maxSize, MAX))
.variant("3435", ReverseTester.variant(maxSize, ROTATE))
.variant("3233", ReverseTester.variant(maxSize, EVEN))
.variant("4142", ReverseTester.variant(maxSize, AVG))
.variant("4749", ReverseTester.variant(maxSize, SUM));
}
public static void main(final String... args) {
SELECTOR.main(args);
}
}

View File

@@ -0,0 +1,370 @@
package reverse;
import base.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
public final class ReverseTester {
public static final Named<Op> TRANSFORM = Named.of(
"",
ReverseTester::transform
);
public static final Named<String> SPACE = Named.of("", " ");
@FunctionalInterface
public interface Op extends Function<int[][], long[][]> {}
private static final int[] DIVISORS = { 100, 10, 1 };
private final Op transform;
private final BiFunction<ExtendedRandom, Integer, String> inputToString;
private final BiFunction<ExtendedRandom, Integer, String> outputToString;
private final String name;
private final String spaces;
private ReverseTester(
final String className,
final Op transform,
final String spaces
) {
this(
className,
transform,
spaces,
(r, i) -> Integer.toString(i),
(r, i) -> Long.toString(i)
);
}
private ReverseTester(
final String className,
final Op transform,
final String spaces,
final BiFunction<ExtendedRandom, Integer, String> inputToString,
final BiFunction<ExtendedRandom, Integer, String> outputToString
) {
name = className;
this.transform = transform;
this.spaces = spaces;
this.inputToString = inputToString;
this.outputToString = outputToString;
}
private static Consumer<TestCounter> variant(
final int maxSize,
final Supplier<ReverseTester> tester
) {
return counter -> tester.get().run(counter, maxSize);
}
public static Consumer<TestCounter> variant(
final int maxSize,
final Named<Op> transform
) {
return variant(maxSize, transform, SPACE);
}
public static Consumer<TestCounter> variant(
final int maxSize,
final Named<Op> transform,
final Named<String> spaces
) {
Objects.requireNonNull(transform);
Objects.requireNonNull(spaces);
return variant(maxSize, () ->
new ReverseTester(
"Reverse" + transform.name() + spaces.name(),
transform.value(),
spaces.value()
)
);
}
public static Consumer<TestCounter> variant(
final int maxSize,
final String suffix,
final Named<BiFunction<ExtendedRandom, Integer, String>> input,
final Named<BiFunction<ExtendedRandom, Integer, String>> output
) {
return variant(maxSize, suffix, TRANSFORM, input, output);
}
public static Consumer<TestCounter> variant(
final int maxSize,
final String suffix,
final Named<Op> op,
final Named<BiFunction<ExtendedRandom, Integer, String>> input,
final Named<BiFunction<ExtendedRandom, Integer, String>> output
) {
return variant(maxSize, suffix, op, input, output, SPACE);
}
public static Consumer<TestCounter> variant(
final int maxSize,
final String suffix,
final Named<Op> op,
final Named<BiFunction<ExtendedRandom, Integer, String>> input,
final Named<BiFunction<ExtendedRandom, Integer, String>> output,
final Named<String> spaces
) {
final String out = input.name().contains(output.name())
? ""
: output.name();
return variant(maxSize, () ->
new ReverseTester(
"Reverse" +
op.name() +
input.name() +
out +
suffix +
spaces.name(),
op.value(),
spaces.value(),
input.value(),
output.value()
)
);
}
private void run(final TestCounter counter, final int maxSize) {
new Checker(
counter,
maxSize,
Runner.packages("", "reverse").std(name),
spaces
).test();
}
@Override
public String toString() {
return name;
}
public static long[][] transform(final int[][] ints) {
return IntStream.range(1, ints.length + 1)
.mapToObj(i -> ints[ints.length - i])
.map(is ->
IntStream.range(1, is.length + 1)
.mapToLong(i -> is[is.length - i])
.toArray()
)
.toArray(long[][]::new);
}
/**
* @author Georgiy Korneev (kgeorgiy@kgeorgiy.info)
*/
private class Checker extends BaseChecker {
private final int maxSize;
private final Runner runner;
private final String spaces;
private final Set<String> manualTests = new HashSet<>();
Checker(
final TestCounter counter,
final int maxSize,
final Runner runner,
final String spaces
) {
super(counter);
this.maxSize = maxSize;
this.runner = runner;
this.spaces = spaces;
}
public void manualTest(final int[][] ints) {
for (final List<int[]> permutation : permutations(
new ArrayList<>(Arrays.asList(ints))
)) {
final int[][] input = permutation.toArray(int[][]::new);
final String[][] lines = toString(input, inputToString);
if (manualTests.add(Arrays.deepToString(lines))) {
test(
lines,
toString(transform.apply(input), outputToString)
);
}
}
}
public void test(final int[][] ints) {
test(
toString(ints, inputToString),
toString(transform.apply(ints), outputToString)
);
}
public void test(final String[][] input, final String[][] output) {
final List<String> inputLines = toLines(
input,
random().randomString(spaces, 1, 10)
);
final List<String> outputLines = toLines(output, " ");
runner.testEquals(counter, inputLines, outputLines);
}
private String[][] toString(
final int[][] ints,
final BiFunction<ExtendedRandom, Integer, String> toString
) {
return Arrays.stream(ints)
.map(row ->
Arrays.stream(row)
.mapToObj(i -> toString.apply(random(), i))
.toArray(String[]::new)
)
.toArray(String[][]::new);
}
private String[][] toString(
final long[][] ints,
final BiFunction<ExtendedRandom, Integer, String> toString
) {
return Arrays.stream(ints)
.map(row ->
Arrays.stream(row)
.mapToObj(i -> toString.apply(random(), (int) i))
.toArray(String[]::new)
)
.toArray(String[][]::new);
}
private List<String> toLines(
final String[][] data,
final String delimiter
) {
if (data.length == 0) {
return Collections.singletonList("");
}
return Arrays.stream(data)
.map(row -> String.join(delimiter, row))
.collect(Collectors.toList());
}
public int[][] random(final int[] profile) {
final int col = random().nextInt(
Arrays.stream(profile).max().orElse(0)
);
final int row = random().nextInt(profile.length);
final int m = random().nextInt(5) - 2;
final int[][] ints = Arrays.stream(profile)
.mapToObj(random().getRandom()::ints)
.map(IntStream::toArray)
.toArray(int[][]::new);
Arrays.stream(ints)
.filter(r -> col < r.length)
.forEach(r -> r[col] = (Math.abs(r[col]) / 2) * m);
ints[row] = Arrays.stream(ints[row])
.map(Math::abs)
.map(v -> (v / 2) * m)
.toArray();
return ints;
}
public void test() {
manualTest(new int[][] { { 1 } });
manualTest(new int[][] { { 1, 2 }, { 3 } });
manualTest(new int[][] { { 1, 2, 3 }, { 4, 5 }, { 6 } });
manualTest(new int[][] { { 1, 2, 3 }, {}, { 4, 5 }, { 6 } });
manualTest(new int[][] { { 1, 2, 3 }, { -4, -5 }, { 6 } });
manualTest(new int[][] { { 1, -2, 3 }, {}, { 4, -5 }, { 6 } });
manualTest(new int[][] { { 1, 2, 0 }, { 1, 0 }, { 0 } });
manualTest(new int[][] { { 1 }, { 1, 3 }, { 1, 2, 3 } });
manualTest(new int[][] { { -1 }, { -1, -2 }, { -1, -2, -3 } });
manualTest(new int[][] { {} });
manualTest(new int[][] { {}, {}, {} });
testRandom(tweakProfile(constProfile(10, 10), new int[][] {}));
testRandom(tweakProfile(constProfile(100, 100), new int[][] {}));
testRandom(randomProfile(100, maxSize));
testRandom(randomProfile(maxSize / 10, maxSize));
testRandom(randomProfile(maxSize, maxSize));
for (final int d : DIVISORS) {
final int size = maxSize / d;
testRandom(
tweakProfile(
constProfile(size / 2, 0),
new int[][] { { size / 2, 0 } }
)
);
testRandom(
tweakProfile(
randomProfile(size, size / 2),
new int[][] { { size / 2, 0 } }
)
);
testRandom(
tweakProfile(
constProfile(size / 2, 0),
new int[][] { { size / 2, size / 2 - 1 } }
)
);
testRandom(
tweakProfile(
constProfile(size / 3, 1),
new int[][] { { size / 3, size / 6, size / 3 - 1 } }
)
);
}
}
private int[] randomProfile(final int length, final int values) {
final int[] profile = new int[length];
for (int i = 0; i < values; i++) {
profile[random().nextInt(0, length - 1)]++;
}
return profile;
}
private void testRandom(final int[] profile) {
test(random(profile));
}
private int[] constProfile(final int length, final int value) {
final int[] profile = new int[length];
Arrays.fill(profile, value);
return profile;
}
private int[] tweakProfile(final int[] profile, final int[][] mods) {
for (final int[] mod : mods) {
Arrays.stream(mod)
.skip(1)
.forEach(i -> profile[i] = mod[0]);
}
return profile;
}
}
private static <T> List<List<T>> permutations(final List<T> elements) {
final List<List<T>> result = new ArrayList<>();
permutations(new ArrayList<>(elements), result, elements.size() - 1);
return result;
}
private static <T> void permutations(
final List<T> elements,
final List<List<T>> result,
final int n
) {
if (n == 0) {
result.add(List.copyOf(elements));
} else {
for (int i = 0; i < n; i++) {
permutations(elements, result, n - 1);
if (n % 2 == 1) {
Collections.swap(elements, i, n);
} else {
Collections.swap(elements, 0, n);
}
}
permutations(elements, result, n - 1);
}
}
}