From 51898dd368da33dc969d8e646df7b385dc01afba Mon Sep 17 00:00:00 2001 From: codejava Date: Mon, 13 Apr 2026 10:40:54 +0300 Subject: [PATCH] Upload files to "java/sum" --- java/sum/SumDoubleHex.java | 45 ++++++ java/sum/SumHex.java | 42 +++++ java/sum/SumLongOctal.java | 45 ++++++ java/sum/SumTest.java | 311 +++++++++++++++++++++++++++++++++++++ java/sum/SumTester.java | 232 +++++++++++++++++++++++++++ 5 files changed, 675 insertions(+) create mode 100644 java/sum/SumDoubleHex.java create mode 100644 java/sum/SumHex.java create mode 100644 java/sum/SumLongOctal.java create mode 100644 java/sum/SumTest.java create mode 100644 java/sum/SumTester.java diff --git a/java/sum/SumDoubleHex.java b/java/sum/SumDoubleHex.java new file mode 100644 index 0000000..2de0e78 --- /dev/null +++ b/java/sum/SumDoubleHex.java @@ -0,0 +1,45 @@ +package sum; + +/** + * @author Nikita Doschennikov (me@fymio.us) + */ +public class SumDoubleHex { + + public static void main(String[] args) { + double res = 0; + for (String arg : args) { + StringBuilder builder = new StringBuilder(); + boolean containsDot = false; + for (char c : arg.toCharArray()) { + if (!Character.isWhitespace(c)) { + builder.append(c); + if (c == '.') { + containsDot = true; + } + } else { + res += compute(builder.toString(), containsDot); + containsDot = false; + builder = new StringBuilder(); + } + } + res += compute(builder.toString(), containsDot); + } + System.out.println(res); + } + + static double compute(String num, boolean containsDot) { + double res = 0; + if (num.isEmpty()) { + res += 0; + } else if ( + num.charAt(0) == '0' && + (num.charAt(1) == 'x' || num.charAt(1) == 'X') + ) { + res += (containsDot) ? Double.parseDouble(num) : Long.decode(num); + } else { + res += Double.parseDouble(num); + } + + return res; + } +} diff --git a/java/sum/SumHex.java b/java/sum/SumHex.java new file mode 100644 index 0000000..f18906d --- /dev/null +++ b/java/sum/SumHex.java @@ -0,0 +1,42 @@ +package sum; + +/** + * @author Nikita Doschennikov (me@fymio.us) + */ +public class SumHex { + + public static void main(String[] args) { + int res = 0; + for (String arg : args) { + StringBuilder builder = new StringBuilder(); + for (char c : arg.toCharArray()) { + if (!Character.isWhitespace(c)) { + builder.append(c); + } else { + res += compute(builder.toString()); + + builder = new StringBuilder(); + } + } + res += compute(builder.toString()); + } + System.out.println(res); + } + + static int compute(String num) { + int res = 0; + if (num.isEmpty()) { + res += 0; + } else if ( + num.length() >= 2 && + num.charAt(0) == '0' && + (num.charAt(1) == 'x' || num.charAt(1) == 'X') + ) { + res += Long.decode(num); + } else { + res += Integer.parseInt(num); + } + + return res; + } +} diff --git a/java/sum/SumLongOctal.java b/java/sum/SumLongOctal.java new file mode 100644 index 0000000..472fb5e --- /dev/null +++ b/java/sum/SumLongOctal.java @@ -0,0 +1,45 @@ +package sum; + +import java.math.BigInteger; + +/** + * @author Nikita Doschennikov (me@fymio.us) + */ +public class SumLongOctal { + + public static void main(String[] args) { + long res = 0; + for (String arg : args) { + StringBuilder builder = new StringBuilder(); + for (char c : arg.toCharArray()) { + if (!Character.isWhitespace(c)) { + builder.append(c); + } else { + res += compute(builder.toString()); + builder = new StringBuilder(); + } + } + res += compute(builder.toString()); + } + System.out.println(res); + } + + static long compute(String num) { + if (num.isEmpty()) { + return 0L; + } + + int numLength = num.length(); + + if ( + num.charAt(numLength - 1) == 'o' || num.charAt(numLength - 1) == 'O' + ) { + return new BigInteger( + num.substring(0, num.length() - 1), + 8 + ).longValue(); + } else { + return Long.parseLong(num); + } + } +} diff --git a/java/sum/SumTest.java b/java/sum/SumTest.java new file mode 100644 index 0000000..82a5662 --- /dev/null +++ b/java/sum/SumTest.java @@ -0,0 +1,311 @@ +package sum; + +import base.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Locale; +import java.util.function.*; +import java.util.stream.Collectors; + +/** + * @author Georgiy Korneev (kgeorgiy@kgeorgiy.info) + */ +public final class SumTest { + + // === Base + + @FunctionalInterface + /* package-private */ interface Op< + T extends Number + > extends UnaryOperator> {} + + private static final BiConsumer TO_STRING = ( + expected, + out + ) -> Asserts.assertEquals("Sum", expected.toString(), out); + + private static final Named>> BASE = Named.of( + "", + () -> + new SumTester<>( + Integer::sum, + n -> (int) n, + (r, max) -> r.nextInt() % max, + TO_STRING, + 10, + 100, + Integer.MAX_VALUE + ) + ); + + /* package-private */ static Named> plain() { + return Named.of("", test -> test); + } + + // === DoubleHex + + private static BiConsumer approximate( + final Function parser, + final double precision + ) { + return (expected, out) -> + Asserts.assertEquals( + "Sum", + expected.doubleValue(), + parser.apply(out).doubleValue(), + precision + ); + } + + private static final Named>> DOUBLE = Named.of( + "Double", + () -> + new SumTester<>( + Double::sum, + n -> (double) n, + (r, max) -> (r.getRandom().nextDouble() - 0.5) * 2 * max, + approximate(Double::parseDouble, 1e-10), + 10.0, + 0.01, + 1e20, + 1e100, + Double.MAX_VALUE / 10000 + ) + .test(5, "2.5 2.5") + .test(0, "1e100 -1e100") + .testT(2e100, "1.5e100 0.5e100") + ); + + private static Named> hexFull( + final Function toHex + ) { + final Function toHexSpoiled = toHex.andThen(s -> + s + .chars() + .map(ch -> + ((ch ^ s.hashCode()) & 1) == 0 + ? Character.toLowerCase(ch) + : Character.toUpperCase(ch) + ) + .mapToObj(Character::toString) + .collect(Collectors.joining()) + ); + return Named.of("Hex", test -> + test + .test(toHex, 1) + .test(toHex, 0x1a) + .test(toHexSpoiled, 0xA2) + .test(toHexSpoiled, 0X0, 0X1, 0XF, 0XF, 0x0, 0x1, 0xF, 0xf) + .test(toHexSpoiled, 0x12345678) + .test(toHexSpoiled, 0x09abcdef) + .test(toHexSpoiled, 0x3CafeBab) + .test(toHexSpoiled, 0x3DeadBee) + .test(toHex, Integer.MAX_VALUE) + .test(toHex, Integer.MIN_VALUE) + .setToString(number -> { + final int hashCode = number.hashCode(); + if ((hashCode & 1) == 0) { + return number.toString(); + } + + return toHexSpoiled.apply(number); + }) + ); + } + + // === Octal + private static Named> octal( + final Function toOctal + ) { + //noinspection OctalInteger,StringConcatenationMissingWhitespace + return Named.of("Octal", test -> + test + .test(1, "1o") + .test(017, "17o") + .testSpaces(6, " 1o 2o 3O ") + .test(01234567, "1234567O") + .test( + Integer.MIN_VALUE, + "-0" + String.valueOf(Integer.MIN_VALUE).substring(1) + ) + .test(Integer.MAX_VALUE, "0" + Integer.MAX_VALUE) + .test( + Integer.MAX_VALUE, + Integer.toOctalString(Integer.MAX_VALUE) + "o" + ) + .test( + Integer.MAX_VALUE, + "0" + Integer.toOctalString(Integer.MAX_VALUE) + "O" + ) + .setToString(number -> { + final int hashCode = number.hashCode(); + if ((hashCode & 1) == 0) { + return number.toString(); + } + + final String lower = + toOctal.apply(number).toLowerCase(Locale.ROOT) + "o"; + return (hashCode & 2) == 0 + ? lower + : lower.toUpperCase(Locale.ROOT); + }) + ); + } + + // === Long + + private static final Named>> LONG = Named.of( + "Long", + () -> + new SumTester<>( + Long::sum, + n -> n, + (r, max) -> r.getRandom().nextLong() % max, + TO_STRING, + 10L, + 100L, + (long) Integer.MAX_VALUE, + Long.MAX_VALUE + ) + .test(12345678901234567L, " +12345678901234567 ") + .test(0L, " +12345678901234567 -12345678901234567") + .test(0L, " +12345678901234567 -12345678901234567") + ); + + // === BigInteger + + private static final Named>> BIG_INTEGER = + Named.of("BigInteger", () -> + new SumTester<>( + BigInteger::add, + BigInteger::valueOf, + (r, max) -> new BigInteger(max.bitLength(), r.getRandom()), + TO_STRING, + BigInteger.TEN, + BigInteger.TEN.pow(10), + BigInteger.TEN.pow(100), + BigInteger.TWO.pow(1000) + ).test( + 0, + "10000000000000000000000000000000000000000 -10000000000000000000000000000000000000000" + ) + ); + + // === BigDecimalHex + + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + private static final Named>> BIG_DECIMAL = + Named.of("BigDecimal", () -> + new SumTester<>( + BigDecimal::add, + BigDecimal::valueOf, + (r, max) -> { + final BigInteger unscaled = new BigInteger( + (max.precision() - max.scale() + 2) * 3, + r.getRandom() + ); + return new BigDecimal(unscaled, 3); + }, + TO_STRING, + BigDecimal.TEN, + BigDecimal.TEN.pow(10), + BigDecimal.TEN.pow(100), + BigDecimal.ONE.add(BigDecimal.ONE).pow(1000) + ) + .testT( + BigDecimal.ZERO.setScale(3), + "10000000000000000000000000000000000000000.123 -10000000000000000000000000000000000000000.123" + ) + ); + + private static String bigDecimalToString(final BigDecimal number) { + final int scale = number.scale(); + return ( + "0x" + + number.unscaledValue().toString(16) + + (scale == 0 ? "" : "s" + Integer.toString(scale, 16)) + ); + } + + // === Hex + + private static Named> hex( + final Function toHex + ) { + return hexFull(v -> "0x" + toHex.apply(v)); + } + + // === Common + + /* package-private */ static Consumer< + TestCounter + > variant( + final Named> runner, + final Named>> test, + final Named< + ? extends Function, ? extends SumTester> + > modifier + ) { + return counter -> + modifier + .value() + .apply(test.value().get()) + .test( + "Sum" + test.name() + modifier.name() + runner.name(), + counter, + runner.value() + ); + } + + /* package-private */ static final Named> RUNNER = + Named.of("", Runner.packages("", "sum")::args); + + public static final Selector SELECTOR = selector(SumTest.class, RUNNER); + + private SumTest() { + // Utility class + } + + public static Selector selector( + final Class owner, + final Named> runner + ) { + return new Selector(owner) + .variant("Base", variant(runner, BASE, plain())) + .variant( + "3637", + variant( + runner, + DOUBLE, + hexFull(value -> + value == value.intValue() && value > 0 + ? "0x" + Integer.toHexString(value.intValue()) + : Double.toHexString(value) + ) + ) + ) + .variant( + "3839", + variant( + runner, + BIG_DECIMAL, + hexFull(SumTest::bigDecimalToString) + ) + ) + .variant("3435", variant(runner, BASE, hex(Integer::toHexString))) + .variant("3233", variant(runner, DOUBLE, plain())) + .variant("4749", variant(runner, LONG, octal(Long::toOctalString))) + .variant( + "4142", + variant( + runner, + BIG_INTEGER, + octal(number -> number.toString(8)) + ) + ); + } + + public static void main(final String... args) { + SELECTOR.main(args); + } +} diff --git a/java/sum/SumTester.java b/java/sum/SumTester.java new file mode 100644 index 0000000..277fd60 --- /dev/null +++ b/java/sum/SumTester.java @@ -0,0 +1,232 @@ +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); + } + } +}