diff --git a/readme.md b/readme.md index 4c76b9f..1d888db 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ Apply you knowledge of _streams_, _lambdas_, _method handlers_ and other functio There are also some tasks that will make think even experienced programmers. Try to solve them all. -> **25** tasks are available now +> **> 30** tasks are available now ### What to do diff --git a/src/tasks/StreamTasks.java b/src/tasks/StreamTasks.java index 9efc856..80105a9 100644 --- a/src/tasks/StreamTasks.java +++ b/src/tasks/StreamTasks.java @@ -1,5 +1,6 @@ package tasks; import java.util.Collection; +import java.util.IntSummaryStatistics; import java.util.List; import java.util.Set; import java.util.function.Function; @@ -250,4 +251,76 @@ public IntStream task25 (List numbers1, List numbers2) { throw new UnsupportedOperationException ("Implement method instead of this line"); } + /** + * @return Stream of prefixes with length that equals to the index of prefix + 1 + * @see List#subList(int, int) + * @example [0, 1, 2, 3, 4] -> [[0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]] + * @lines 1 + */ + public Stream > task26 (List numbers) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + /** + * @return Stream of groups of values with length that is not more than given `sizeLimit` + * @see List#subList(int, int) + * @example [0, 1, 2, 3, 4, 5, 6, 7], 3 -> [[0, 1, 2], [3, 4, 5], [6, 7]] + * @lines 4- + */ + public Stream > task27 (List values, int sizeLimit) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + /** + * @return Stream of integer function roots from segment [-1000, 1000] in ascending order + * @see IntStream#filter(IntPredicate) + * @example x^2 - 3x - 10 -> [-2, 5] + * @lines 1 + */ + public IntStream task28 (Function function) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + /** + * @return Average value of given numbers (or -1 if stream is empty) + * @see Stream#mapToInt(ToIntFunction) + * @see IntStream#average() + * @lines 1 + */ + public double task29 (List numbers) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + /** + * @return Values variation (the difference between maximal and minimal values) + * @see IntStream#summaryStatistics() + * @see IntSummaryStatistics + * @lines 2- + */ + public int task30 (List numbers) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + // MULTI-DIMENTION // + + /** + * @return Stream of all values from given 2-dim list + * @see Stream#flatMap(Function) + * @see List#stream() + * @example [[1, 2], [4, 5, 3], [], [7]] -> [1, 2, 4, 5, 3, 7] + * @lines 1 + */ + public Stream task31 (List > numbers) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + + /** + * @return Stream of sums of values in internal lists + * @example [[1, 2], [4, 5, 3], [], [7]] -> [3, 12, 0, 7] + * @lines 1 + */ + public Stream task32 (List > numbers) { + throw new UnsupportedOperationException ("Implement method instead of this line"); + } + } diff --git a/src/tasks/StreamTasksMain.java b/src/tasks/StreamTasksMain.java index 1d36599..8a58d0a 100644 --- a/src/tasks/StreamTasksMain.java +++ b/src/tasks/StreamTasksMain.java @@ -1,4 +1,5 @@ package tasks; +import java.io.PrintStream; import java.util.Locale; import tasks.solution.StreamTasksSolution; @@ -11,6 +12,8 @@ public class StreamTasksMain { /* DO NOT TOUCH METHODS BELLOW */ /*******************************/ + public static PrintStream ORIGINAL_OUT = System.out; + public static void main (String ... args) { Locale.setDefault (Locale.ENGLISH); diff --git a/src/tasks/solution/StreamTasksSolution.java b/src/tasks/solution/StreamTasksSolution.java index e8472e7..e67fe52 100644 --- a/src/tasks/solution/StreamTasksSolution.java +++ b/src/tasks/solution/StreamTasksSolution.java @@ -136,4 +136,43 @@ public IntStream task25 (List numbers1, List numbers2) { . map (i -> numbers1.get (i) + numbers2.get (i)); } + @Override + public Stream > task26 (List numbers) { + return IntStream.range (0, numbers.size ()).mapToObj (i -> numbers.subList (0, i + 1)); + } + + @Override + public Stream > task27 (List values, int sl) { + final var len = values.size (); + return IntStream.range (0, (int) Math.ceil (len * 1.0 / sl)).mapToObj ( + i -> values.subList (i * sl, Math.min ((i + 1) * sl, len) + )); + } + + @Override + public IntStream task28 (Function function) { + return IntStream.rangeClosed (-1000, 1000).filter (i -> function.apply (i) == 0); + } + + @Override + public double task29 (List numbers) { + return numbers.stream ().mapToInt (i -> i).average ().orElse (0); + } + + @Override + public int task30 (List numbers) { + final var statistics = numbers.stream ().mapToInt (i -> i).summaryStatistics (); + return statistics.getMax () - statistics.getMin (); + } + + @Override + public Stream task31 (List > numbers) { + return numbers.stream ().flatMap (List::stream); + } + + @Override + public Stream task32 (List > numbers) { + return numbers.stream ().map (list -> list.stream ().mapToInt (i -> i).sum ()); + } + } diff --git a/src/tests/InvocationResult.java b/src/tests/InvocationResult.java index 508cdc3..512bef1 100644 --- a/src/tests/InvocationResult.java +++ b/src/tests/InvocationResult.java @@ -10,6 +10,7 @@ public class InvocationResult { private long runtime; private final List consumers = new ArrayList <> (); + private final List output = new ArrayList <> (); public InvocationResult (Object result, long runtime, List consumerResults) { this.result = result; this.runtime = runtime; @@ -30,6 +31,16 @@ public InvocationResult addConsumerValues (List values) { return this; } + public InvocationResult addOutput (String output) { + this.output.add (0, output); + return this; + } + + public InvocationResult addOutputs (List output) { + this.output.addAll (0, output); + return this; + } + public InvocationResult addRuntime (long delta) { runtime += delta; return this; @@ -38,6 +49,7 @@ public InvocationResult addRuntime (long delta) { public InvocationResult addAnotherResult (InvocationResult result) { addConsumerValues (result.getConsumers ()); addRuntime (result.getRuntime ()); + addOutputs (result.getOutput ()); return this; } @@ -45,9 +57,12 @@ public long getRuntime () { return runtime; } - public List getConsumers () { return Collections.unmodifiableList (consumers); } + public List getOutput () { + return Collections.unmodifiableList (output); + } + } diff --git a/src/tests/OutputAssertions.java b/src/tests/OutputAssertions.java index c53c539..81b7e08 100644 --- a/src/tests/OutputAssertions.java +++ b/src/tests/OutputAssertions.java @@ -67,6 +67,13 @@ public static void assertOutput (Integer actual, Integer expected) { ); } + public static void assertOutput (Double actual, Double expected) { + assert Objects.equals (actual, expected) : String.format ( + "Double value `%d` was expected (actual given: %d)", + expected, actual + ); + } + public static void assertOutput (Stream actual, boolean parallel, List expected) { assert actual.isParallel () == parallel : String.format ( "Stream should%s be parallel", parallel ? "" : " not" diff --git a/src/tests/StreamTasksTests.java b/src/tests/StreamTasksTests.java index 7f94c44..18ecdb9 100644 --- a/src/tests/StreamTasksTests.java +++ b/src/tests/StreamTasksTests.java @@ -7,6 +7,7 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import tests.inputs.LevelsArrange; import tests.presets.HexStrNumbers; import tests.presets.IntNumbers; import tests.presets.IntStrNumbers; @@ -228,5 +229,62 @@ public abstract IntStream task25 ( percentage = {1.3, 1.4}, variation = 10) List numbers2 ); + + @Test (order = 2) + @TestResult (repeat = 1, wrap = List.class) + public abstract Stream > task26 ( + @TestInputCollection (presets = {IntNumbers.class}, percentage = {0.4, 0.5, 1.2, 1.7, 2.0}, variation = 20) + List numbers + ); + + @Test (order = 2) + @TestResult (repeat = 1, wrap = List.class) + public abstract Stream > task27 ( + @TestInputCollection (presets = {IntNumbers.class, IntStrNumbers.class}, + percentage = {0.3, 0.6, 1.1, 1.8, 1.9, 2.4}, variation = 20) + List values, + @TestInputConstant (sequence = {0}, parameter = 4, variation = 200) + int sizeLimit + ); + + @Test (order = 2) + @TestResult (repeat = 1, wrap = List.class) + public abstract IntStream task28 ( + @TestInputFunction (indices = {5, 6}) + Function function + ); + + @Test (order = 2) + @TestResult (repeat = 1) + public abstract double task29 ( + @TestInputCollection (presets = {IntNumbers.class}, constant = {0, 0, 0, 0}, + percentage = {0.1, 0.5, 0.8, 1.4, 1.7}, variation = 10) + List numbers + ); + + @Test (order = 3) + @TestResult (repeat = 1) + public abstract int task30 ( + @TestInputCollection (presets = {IntNumbers.class}, constant = {0, 0, 0, 0}, + percentage = {0.1, 0.5, 0.8, 1.4, 1.7}, variation = 10) + List numbers + ); + + @Test (order = 3) + @TestResult (repeat = 1, wrap = List.class) + public abstract Stream task31 ( + @TestInputCollection (presets = {IntNumbers.class, HexStrNumbers.class, Names.class}, + levels = 2, arrengement = LevelsArrange.DISHONEST, percentage = {0.1, 0.2, 0.3, 1.1, 1.2}, + variation = 20) + List > numbers + ); + + @Test (order = 3) + @TestResult (repeat = 1, wrap = List.class) + public abstract Stream task32 ( + @TestInputCollection (presets = {IntNumbers.class}, levels = 2, arrengement = LevelsArrange.DISHONEST, + percentage = {0.1, 0.2, 0.3, 1.1, 1.2}, variation = 20) + List > numbers + ); } diff --git a/src/tests/TaskTests.java b/src/tests/TaskTests.java index 97bca7c..01f370e 100644 --- a/src/tests/TaskTests.java +++ b/src/tests/TaskTests.java @@ -41,7 +41,16 @@ public List getInputs () { public InvocationResult runTests (StreamTasksTests implementation, StreamTasksTests reference) { final var stub = new InvocationResult (null, 0); return cases.stream ().map (caze -> caze.apply (implementation, reference)) - . reduce (stub, (a, b) -> a == stub && b != null ? b : a); + . reduce (stub, (a, b) -> { + if (a == stub && b != null) { + return b; + } else if (a != stub && b != null) { + b.addAnotherResult (a); + return b; + } else { + return a; + } + }); } } diff --git a/src/tests/TestInputGenerator.java b/src/tests/TestInputGenerator.java index 98588b1..4bc7f1b 100644 --- a/src/tests/TestInputGenerator.java +++ b/src/tests/TestInputGenerator.java @@ -15,7 +15,6 @@ import tests.inputs.ConstantValueProvider; import tests.inputs.ConsumerGenerator; -import tests.inputs.SequenceWithStatistics; import tests.inputs.SupplierMode; import tests.presets.DataMappingPreset; import tests.presets.DataPreset; @@ -28,14 +27,17 @@ public class TestInputGenerator { - private final Map >, DataPreset > presets = new HashMap <> (); + private final Map , DataPreset> presets = new HashMap <> (); private final List > functions = List.of ( (String s) -> s.concat (" Solk"), (String s) -> Integer.parseInt (s), (Integer i) -> i + 2, (Integer i) -> i * i, - (Integer i) -> i * 7 + (Integer i) -> i * 7, + + (Integer x) -> x * x * x - 9 * x * x - 64 * x + 336, + (Integer x) -> 2 * x * x * x * x + 14 * x * x * x - 416 * x * x - 1376 * x + 10752 ); private final List > predicates = List.of ( @@ -69,11 +71,13 @@ public List prepareInputDataForParameter (Parameter parameter, Random random return List.of (); } - private List > prepareCollectionInputForParameter ( + private List prepareCollectionInputForParameter ( Parameter parameter, TestInputCollection annotation, Random random ) { - final var inputsCollector = new ArrayList > (); + final var arrangement = annotation.arrengement (); + final var inputsCollector = new ArrayList <> (); final var parallel = annotation.parallel (); + final var levels = annotation.levels (); for (final var presetType : annotation.presets ()) { final var preset = getPreset (presetType, random); @@ -81,17 +85,26 @@ private List > prepareCollectionInputForParameter ( final var varation = annotation.variation () + 1; final var unique = annotation.allUnique (); + final var nulls = annotation.nulls ().length == 0 ? new int [] {0} : annotation.nulls (); + int nullsIndex = 0; + for (final var constantInput : annotation.constant ()) { final var length = constantInput + random.nextInt (varation); - inputsCollector.add (preset.getRandomSequence (length, random, unique) - .setParallelStream (parallel)); + final var ns = nulls [nullsIndex]; + inputsCollector.add (preset.getRandomSequence ( + levels, arrangement, length, random, unique, ns + ).setParallelStream (parallel)); + nullsIndex = (nullsIndex + 1) % nulls.length; } for (final var percentageInput : annotation.percentage ()) { final var percent = percentageInput + random.nextInt (varation) / 100.0; final var length = (int) Math.round (percent * preset.getSize ()); - inputsCollector.add (preset.getRandomSequence (length, random, unique) - .setParallelStream (parallel)); + final var ns = nulls [nullsIndex]; + inputsCollector.add (preset.getRandomSequence ( + levels, arrangement, length, random, unique, ns + ).setParallelStream (parallel)); + nullsIndex = (nullsIndex + 1) % nulls.length; } } @@ -127,8 +140,8 @@ private List >> prepareSupplierInputForParameter for (final var cycle : annotation.cycles ()) { if (annotation.mode () == SupplierMode.SEQUENTIAL) { inputCollector.add (R -> { - final var sequence = cycle == -1 ? preset.getData () - : preset.getRandomSequence (cycle, R, false).data; + final var sequence = (List ) (cycle == -1 ? preset.getData () + : preset.getRandomSequence (1, cycle, R, false).data); final var counter = new AtomicInteger (); return () -> sequence.get (counter.getAndUpdate ( @@ -137,8 +150,8 @@ private List >> prepareSupplierInputForParameter }); } else if (annotation.mode () == SupplierMode.SHUFFLED_SEQUENTIAL) { inputCollector.add (R -> { - final var rawSequence = cycle == -1 ? preset.getData () - : preset.getRandomSequence (cycle, R, false).data; + final var rawSequence = (List ) (cycle == -1 ? preset.getData () + : preset.getRandomSequence (1, cycle, R, false).data); final var sequence = new ArrayList <> (rawSequence); final var counter = new AtomicInteger (); Collections.shuffle (sequence, R); @@ -148,7 +161,7 @@ private List >> prepareSupplierInputForParameter )); }); } else if (annotation.mode () == SupplierMode.RANDOM) { - final var data = preset.getData (); + final var data = (List ) preset.getData (); inputCollector.add (R -> () -> data.get (R.nextInt (data.size ()))); } } @@ -185,16 +198,17 @@ private List > prepareConsumerInputForParameter ( return List.of (new ConsumerGenerator <> (annotation.mode ())); } - @SuppressWarnings ("unchecked") - private DataPreset getPreset (Class > presetType, Random random) { + private DataPreset getPreset (Class presetType, Random random) { final var preset = presets.get (presetType); if (preset != null) { return preset; } + System.out.println ("Loading preset: " + presetType + "..."); // SYSOUT + try { final var data = presetType.getConstructor ().newInstance (); if (DataMappingPreset.class.isAssignableFrom (presetType)) { - final var mdata = (DataMappingPreset ) data; - final var source = (DataPreset ) getPreset (mdata.getSourcePreset (), random); + final var mdata = (DataMappingPreset) data; + final var source = (DataPreset) getPreset (mdata.getSourcePreset (), random); mdata.initialize (random, source); } else { data.initialize (random); @@ -208,6 +222,7 @@ private DataPreset getPreset (Class > presetType, R | NoSuchMethodException | SecurityException e ) { + e.printStackTrace (); return null; } } diff --git a/src/tests/TestInvokerGenerator.java b/src/tests/TestInvokerGenerator.java index 29a8d97..6752f5c 100644 --- a/src/tests/TestInvokerGenerator.java +++ b/src/tests/TestInvokerGenerator.java @@ -1,8 +1,11 @@ package tests; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -41,18 +44,26 @@ public BiFunction prepare return (implementation, reference) -> { final var seed = random.nextLong (); + final var customBuffer = new ByteArrayOutputStream (); + System.setOut (new PrintStream (customBuffer, true, StandardCharsets.UTF_8)); + final var resultImpl = prepareSingleInvoker ( method, paramInput, new Random (seed), result, pool, prepared, true ).apply (implementation, reference); + final var output = new String (customBuffer.toByteArray (), StandardCharsets.UTF_8); + resultImpl.addOutput (output); + final var resultRef = prepareSingleInvoker ( method, paramInput, new Random (seed), result, pool, prepared, false ).apply (implementation, reference); - final var wrappedRef = wrapResult (result.wrap (), resultRef.result); - if (method.getReturnType () != Void.class && method.getReturnType () != void.class - && (resultImpl.result != null && wrappedRef != null)) { - compareAnswers (resultImpl.result, wrappedRef, result.parallel ()); + if (result != null) { + final var wrappedRef = wrapResult (result.wrap (), resultRef.result); + if (method.getReturnType () != Void.class && method.getReturnType () != void.class + && (resultImpl.result != null && wrappedRef != null)) { + compareAnswers (resultImpl.result, wrappedRef, result.parallel ()); + } } final var consumersImpt = resultImpl.getConsumers (); @@ -79,7 +90,7 @@ public BiFunction prepare final var instance = forImplementation ? implementation : reference; final var seed = random.nextLong (); - if (result.checkBy () == -1) { + if (result == null || result.checkBy () == -1) { return prepareAndInvokeImplementation (instance, method, paramInput, seed); } else { final var value = prepareAndInvokeImplementation (instance, method, paramInput, seed); @@ -127,6 +138,7 @@ private Object wrapResult (Class wrapper, Object result) { int.class, double.class, Integer.class, Double.class ); + @SuppressWarnings ("unchecked") private InvocationResult prepareAndInvokeImplementation ( StreamTasksTests implementation, Method method, Object [] paramInput, long randomSeed ) { @@ -140,21 +152,48 @@ private InvocationResult prepareAndInvokeImplementation ( if (parameters [i].getType () == List.class) { if (paramInput [i] instanceof SequenceWithStatistics) { - input [i] = ((SequenceWithStatistics ) paramInput [i]).data; + final var data = ((SequenceWithStatistics ) paramInput [i]).data; + if (data instanceof List) { + input [i] = data; + } else if (data instanceof Collection) { + input [i] = List.copyOf ((Collection ) data); + } else { + requestCorrectType (method, i, List.class, data.getClass ()); + } } else if (paramInput [i] instanceof List) { input [i] = paramInput [i]; + } else if (paramInput [i] instanceof Collection) { + input [i] = List.copyOf ((Collection ) paramInput [i]); } else { requestAnnotation (method, i, TestInputCollection.class); } } else if (parameters [i].getType () == Set.class) { if (paramInput [i] instanceof SequenceWithStatistics) { - input [i] = Set.copyOf (((SequenceWithStatistics ) paramInput [i]).data); + final var data = ((SequenceWithStatistics ) paramInput [i]).data; + if (data instanceof Set) { + input [i] = data; + } else if (data instanceof Collection) { + input [i] = Set.copyOf ((Collection ) data); + } else { + requestCorrectType (method, i, Set.class, data.getClass ()); + } + } else { + requestAnnotation (method, i, TestInputCollection.class); + } + } else if (parameters [i].getType () == Map.class) { + if (paramInput [i] instanceof SequenceWithStatistics) { + final var data = ((SequenceWithStatistics ) paramInput [i]).data; + if (data instanceof Map) { + input [i] = data; + } else { + requestCorrectType (method, i, Map.class, data.getClass ()); + } } else { requestAnnotation (method, i, TestInputCollection.class); } } else if (parameters [i].getType () == Stream.class || isIntStream) { if (paramInput [i] instanceof SequenceWithStatistics) { - final var sws = (SequenceWithStatistics ) paramInput [i]; + final var sws = (SequenceWithStatistics >) paramInput [i]; final var stream = sws.isParallelStream () ? sws.data.parallelStream () : sws.data.stream (); if (isIntStream) { input [i] = stream.mapToInt (num -> (Integer) num); @@ -168,7 +207,6 @@ private InvocationResult prepareAndInvokeImplementation ( } } else if (parameters [i].getType () == Supplier.class) { if (paramInput [i] instanceof Function) { - @SuppressWarnings ("unchecked") final var supplier = (Function >) paramInput [i]; input [i] = supplier.apply (random); } else { @@ -251,16 +289,25 @@ private void requestAnnotation (Method method, int parameterIndex, Class expected, Class actual) { + throw new IllegalArgumentException (String.format ( + "In method `%s` parameter #%d has type `%s` and can't be assigned from type `%s`", + method.getName (), parameterIndex, expected.getSimpleName (), actual.getSimpleName () + )); + } + private void compareAnswers (Object implementation, Object reference, boolean parallel) { final var iType = implementation instanceof IntStream ? IntStream.class : implementation instanceof Stream ? Stream.class : implementation instanceof Integer ? Integer.class + : implementation instanceof Double ? Double.class : implementation instanceof List ? List.class : implementation instanceof Map ? Map.class : implementation instanceof Set ? Set.class : null; final var rType = reference instanceof IntStream ? IntStream.class : reference instanceof Stream ? Stream.class : reference instanceof Integer ? Integer.class + : reference instanceof Double ? Double.class : reference instanceof List ? List.class : reference instanceof Map ? Map.class : reference instanceof Set ? Set.class : null; diff --git a/src/tests/TestsPool.java b/src/tests/TestsPool.java index 6cd4a5f..3bcc07c 100644 --- a/src/tests/TestsPool.java +++ b/src/tests/TestsPool.java @@ -71,8 +71,9 @@ public TaskTests prepareTestsForMethod ( p -> inputGenerator.prepareInputDataForParameter (p, random) ).collect (Collectors.toList ()); - var maxCases = inputs.stream ().mapToInt (List::size).max ().orElse (0); - maxCases = maxCases == 0 ? result.repeat () : Math.max (maxCases, result.repeat ()); + final var repeats = result == null ? 1 : result.repeat (); + var maxCases = inputs.stream ().mapToInt (List::size).max ().orElse (1); + maxCases = maxCases == 0 ? result.repeat () : Math.max (maxCases, repeats); final var tests = new TaskTests (method, inputs); diff --git a/src/tests/TestsRunner.java b/src/tests/TestsRunner.java index 6543bfc..49876f9 100644 --- a/src/tests/TestsRunner.java +++ b/src/tests/TestsRunner.java @@ -1,8 +1,11 @@ package tests; +import java.util.List; import java.util.Locale; import java.util.Optional; +import tasks.StreamTasksMain; + public class TestsRunner { /*******************************/ @@ -30,12 +33,15 @@ public int test () { System.out.printf ("Test %3d -- ", i + 1); final var start = System.currentTimeMillis (); + List customOutput = List.of (); long internalRuntime = Long.MAX_VALUE; Throwable throwable = null; TestVerdict verdict = null; try { - internalRuntime = testsPool.runTest (i, implementation, reference).getRuntime (); + final var result = testsPool.runTest (i, implementation, reference); + internalRuntime = result.getRuntime (); + customOutput = result.getOutput (); verdict = TestVerdict.ACCEPTED; } catch (UnsupportedOperationException uoe) { verdict = TestVerdict.NOT_IMPL; @@ -49,6 +55,7 @@ public int test () { verdict = TestVerdict.RE; throwable = t.getMessage () != null ? t : null; } finally { + System.setOut (StreamTasksMain.ORIGINAL_OUT); final var end = System.currentTimeMillis (); final var runtime = Math.min (end - start, internalRuntime); @@ -57,6 +64,15 @@ public int test () { System.out.printf (" %s%n", t.getMessage ()); }); + for (int j = 0; customOutput != null && j < customOutput.size (); j++) { + final var output = customOutput.get (j); + + if (output != null && !output.isBlank ()) { + System.out.printf (" -- Your output (case %2d) --%n", j + 1); + System.out.println (customOutput.get (j)); + } + } + failed += verdict == TestVerdict.ACCEPTED ? 0 : 1; } } diff --git a/src/tests/inputs/LevelsArrange.java b/src/tests/inputs/LevelsArrange.java new file mode 100644 index 0000000..56cfa04 --- /dev/null +++ b/src/tests/inputs/LevelsArrange.java @@ -0,0 +1,8 @@ +package tests.inputs; + + +public enum LevelsArrange { + + HONEST, DISHONEST, CUBE + +} diff --git a/src/tests/inputs/SequenceWithStatistics.java b/src/tests/inputs/SequenceWithStatistics.java index a20a54e..c3ba7e4 100644 --- a/src/tests/inputs/SequenceWithStatistics.java +++ b/src/tests/inputs/SequenceWithStatistics.java @@ -1,16 +1,14 @@ package tests.inputs; -import java.util.List; - public class SequenceWithStatistics { - public final List data; + public final T data; public final int length; public final double min, max, average, median; public SequenceWithStatistics ( - List data, int length, double min, double max, + T data, int length, double min, double max, double average, double median ) { this.average = average; @@ -21,7 +19,7 @@ public SequenceWithStatistics ( this.max = max; } - public SequenceWithStatistics (List data) { + public SequenceWithStatistics (T data) { this (data, 0, 0.0, 0.0, 0.0, 0.0); } diff --git a/src/tests/presets/AbstractListPreset.java b/src/tests/presets/AbstractListPreset.java new file mode 100644 index 0000000..f29e140 --- /dev/null +++ b/src/tests/presets/AbstractListPreset.java @@ -0,0 +1,155 @@ +package tests.presets; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.function.ToDoubleFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import tests.inputs.LevelsArrange; +import tests.inputs.SequenceWithStatistics; + +public abstract class AbstractListPreset implements DataPreset { + + protected final List values = new ArrayList <> (); + + @Override + public List getData () { + return Collections.unmodifiableList (values); + } + + @Override + public SequenceWithStatistics getRandomSequence ( + int levels, LevelsArrange arrange, int length, Random r, + boolean unique, int nulls + ) { + List sequence = List.of (); + + if (unique) { + final var uniques = new ArrayList <> (Set.copyOf (getData ())); + if (uniques.size () < length) { + throw new IllegalArgumentException (String.format ( + "Requested number of unique value (%d) is more than actual number of unique values (%d)", + length, uniques.size () + )); + } + + for (int i = 0; i < nulls; i++) { + uniques.add (null); + } + + Collections.shuffle (uniques, r); + sequence = Collections.unmodifiableList (uniques.subList (0, length)); + } else { + final var data = getData (); + + final var list = IntStream.range (0, length).map (i -> r.nextInt (data.size ())) + . mapToObj (data::get).collect (Collectors.toList ()); + for (int i = 0; i < nulls; i++) { list.add (null); } + + Collections.shuffle (list, r); + sequence = Collections.unmodifiableList (list); + } + + @SuppressWarnings ("unchecked") + final var ss = (ST) arrangeSequence (sequence, levels, arrange, r); + + if (doesSupportStatistics () && !sequence.isEmpty ()) { + final var conv = getStatisticsConverter (); + + final var statistics = sequence.stream ().mapToDouble (conv).summaryStatistics (); + double avg = statistics.getAverage (), min = statistics.getMin (), max = statistics.getMax (); + double med = sequence.stream ().mapToDouble (conv).sorted ().skip (length / 2).findFirst ().orElse (0.0); + + return new SequenceWithStatistics <> (ss, length, min, max, avg, med); + } else { + return new SequenceWithStatistics <> (ss); + } + } + + private List arrangeSequence (List sequence, int levels, LevelsArrange arrange, Random r) { + List current = new ArrayList <> (sequence); + + if (arrange == LevelsArrange.CUBE) { + final var side = (int) Math.round (Math.pow (current.size (), 1.0 / levels)); + for (int i = 1; i < levels; i++) { + final var iterations = (int) Math.round (current.size () * 1.0 / side); + final var tmp = new ArrayList (); + + int p = 0; + for (int j = 0; j < iterations; j++) { + final var right = Math.min (p + side, current.size ()); + tmp.add (current.subList (p, right)); + p += right - p; + } + + current = tmp; + + } + + return Collections.unmodifiableList (current); + } else if (arrange == LevelsArrange.HONEST || arrange == LevelsArrange.DISHONEST) { + for (int i = 1; i < levels; i++) { + final var tmp = new ArrayList (); + + final int parts = 2 << (levels - i - 1), size = current.size (); + final var split = arrange == LevelsArrange.HONEST ? doHonestSplit (size, parts) + : doDishonestSplit (size, parts, r); + + int p = 0; + for (int j = 0; j < split.length; j++) { + tmp.add (current.subList (p, p + split [j])); + p += split [j]; + } + + current = tmp; + } + + return Collections.unmodifiableList (current); + } + + throw new IllegalArgumentException ("Unknown levels arrengement: " + arrange); + } + + public static int [] doHonestSplit (int length, int parts) { + int [] split = new int [parts]; + Arrays.fill (split, length / parts); + + final var rest = length % parts; + for (int i = 0; i < rest; i++) { + split [i] += 1; + } + + return split; + } + + public static int [] doDishonestSplit (int length, int parts, Random r) { + final var borders = IntStream.range (0, parts - 1) + . mapToObj (__ -> r.nextInt (length)).sorted () + . collect (Collectors.toList ()); + + int [] split = new int [parts]; + int prevBorder = 0, sum = 0; + + for (int i = 0; i < borders.size (); i++) { + split [i] = borders.get (i) - prevBorder; + prevBorder = borders.get (i); + sum += split [i]; + } + + split [parts - 1] = length - sum; + return split; + } + + @Override + public boolean doesSupportStatistics () { + return true; + } + + protected abstract ToDoubleFunction getStatisticsConverter (); + +} diff --git a/src/tests/presets/AbstractMapPreset.java b/src/tests/presets/AbstractMapPreset.java new file mode 100644 index 0000000..c0dea31 --- /dev/null +++ b/src/tests/presets/AbstractMapPreset.java @@ -0,0 +1,93 @@ +package tests.presets; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.function.ToDoubleBiFunction; + +import tests.inputs.LevelsArrange; +import tests.inputs.SequenceWithStatistics; + +public abstract class AbstractMapPreset implements DataMapPreset { + + protected final Map map = new HashMap <> (); + + @Override + public AbstractMapPreset initialize (Random r) { + final var values = initializeValues (r); + final var keys = initializeKeys (r); + + final var length = keys.size (); + for (int i = 0; i < length; i++) { + final var value = i >= values.size () ? null : values.get (i); + map.put (keys.get (i), value); + } + + return this; + } + + protected abstract List initializeValues (Random r); + + protected abstract List initializeKeys (Random r); + + @Override + public Map getData () { + return Collections.unmodifiableMap (map); + } + + @Override + public boolean doesSupportStatistics () { + return true; + } + + @Override + @SuppressWarnings ("unchecked") + public SequenceWithStatistics getRandomSequence ( + int levels, LevelsArrange arrange, int length, Random r, boolean unique, int nulls + ) { + if (length + nulls > map.size ()) { + throw new IllegalArgumentException (String.format ( + "Requested size of map (%d) is more than actual size of preset (%d)", + length + nulls, map.size () + )); + } + + final var statisticsData = new ArrayList (); + final var hasStatistics = doesSupportStatistics (); + final var keys = new ArrayList <> (map.keySet ()); + Collections.shuffle (keys, r); + + final var sequence = new HashMap (); + for (int i = 0; i < length; i++) { + final var key = keys.get (i); + final var value = map.get (key); + + sequence.put (key, value); + + if (hasStatistics) { + final var conv = getStatisticsConverter (); + statisticsData.add (conv.applyAsDouble (key, value)); + } + } + + for (int i = 0; i < nulls; i++) { + sequence.put (keys.get (length + i), null); + } + + if (hasStatistics && !sequence.isEmpty ()) { + final var statistics = statisticsData.stream ().mapToDouble (i -> i).summaryStatistics (); + double avg = statistics.getAverage (), min = statistics.getMin (), max = statistics.getMax (); + double med = statisticsData.stream ().mapToDouble (i -> i).sorted ().skip (length / 2).findFirst ().orElse (0.0); + + return new SequenceWithStatistics <> ((T) sequence, length, min, max, avg, med); + } else { + return new SequenceWithStatistics <> ((T) sequence); + } + } + + protected abstract ToDoubleBiFunction getStatisticsConverter (); + +} diff --git a/src/tests/presets/AbstractMappingListPreset.java b/src/tests/presets/AbstractMappingListPreset.java new file mode 100644 index 0000000..e019a46 --- /dev/null +++ b/src/tests/presets/AbstractMappingListPreset.java @@ -0,0 +1,5 @@ +package tests.presets; + +public abstract class AbstractMappingListPreset extends AbstractListPreset implements DataMappingPreset { + +} diff --git a/src/tests/presets/Boxes.java b/src/tests/presets/Boxes.java index fb42c5e..03c1565 100644 --- a/src/tests/presets/Boxes.java +++ b/src/tests/presets/Boxes.java @@ -1,7 +1,5 @@ package tests.presets; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Random; import java.util.function.ToDoubleFunction; @@ -10,26 +8,14 @@ import tasks.utils.Box; import tasks.utils.Item; -public class Boxes implements DataMappingPreset { - - private final List boxes = new ArrayList <> (); +public class Boxes extends AbstractMappingListPreset { @Override - public void initialize (Random r, DataPreset items) { - items.getRandomSequence (200 / 10 + r.nextInt (10), r, false); - IntStream.range (0, 1000).mapToObj (i -> items.getRandomSequence (i / 10 + r.nextInt (10), r, false).data) + public Boxes initialize (Random r, DataPreset items) { + IntStream.range (0, 1000).mapToObj (i -> items.> getRandomSequence (1, i / 10 + r.nextInt (10), r, false).data) . map (its -> its.stream ().reduce (new Box (), Box::addItem, (a, b) -> a.addItems (b.getItems ()))) - . forEach (boxes::add); - } - - @Override - public List getData () { - return Collections.unmodifiableList (boxes); - } - - @Override - public boolean doesSupportStatistics () { - return true; + . forEach (values::add); + return this; } @Override @@ -38,7 +24,7 @@ public ToDoubleFunction getStatisticsConverter () { } @Override - public Class > getSourcePreset () { + public Class getSourcePreset () { return Items.class; } diff --git a/src/tests/presets/DataMapPreset.java b/src/tests/presets/DataMapPreset.java new file mode 100644 index 0000000..14a30a8 --- /dev/null +++ b/src/tests/presets/DataMapPreset.java @@ -0,0 +1,5 @@ +package tests.presets; + +public interface DataMapPreset extends DataPreset { + +} diff --git a/src/tests/presets/DataMappingPreset.java b/src/tests/presets/DataMappingPreset.java index 548e0ef..fb9dad2 100644 --- a/src/tests/presets/DataMappingPreset.java +++ b/src/tests/presets/DataMappingPreset.java @@ -2,14 +2,14 @@ import java.util.Random; -public interface DataMappingPreset extends DataPreset { +public interface DataMappingPreset extends DataPreset { - Class > getSourcePreset (); + Class getSourcePreset (); - void initialize (Random r, DataPreset preset); + DataMappingPreset initialize (Random r, DataPreset preset); @Override - default void initialize (Random r) { + default DataMappingPreset initialize (Random r) { throw new UnsupportedOperationException (); } diff --git a/src/tests/presets/DataPreset.java b/src/tests/presets/DataPreset.java index 23b1569..72b38df 100644 --- a/src/tests/presets/DataPreset.java +++ b/src/tests/presets/DataPreset.java @@ -1,62 +1,39 @@ package tests.presets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.Collection; +import java.util.Map; import java.util.Random; -import java.util.Set; -import java.util.function.ToDoubleFunction; -import java.util.stream.Collectors; -import java.util.stream.IntStream; +import tests.inputs.LevelsArrange; import tests.inputs.SequenceWithStatistics; -public interface DataPreset { +public interface DataPreset { - void initialize (Random r); + DataPreset initialize (Random r); - List getData (); + Object getData (); default int getSize () { - return getData ().size (); + final var data = getData (); + if (data instanceof Map) { + return ((Map ) data).size (); + } else if (data instanceof Collection) { + return ((Collection ) data).size (); + } + + return 0; } boolean doesSupportStatistics (); - ToDoubleFunction getStatisticsConverter (); + default SequenceWithStatistics getRandomSequence (int levels, int length, Random r, boolean unique) { + return getRandomSequence (levels, length, r, unique, 0); + } - default SequenceWithStatistics getRandomSequence (int length, Random r, boolean unique) { - List sequence = List.of (); - - if (unique) { - final var uniques = new ArrayList <> (Set.copyOf (getData ())); - if (uniques.size () < length) { - throw new IllegalArgumentException (String.format ( - "Requested number of unique value (%d) is more than actual number of unique values (%d)", - length, uniques.size () - )); - } - - Collections.shuffle (uniques, r); - sequence = uniques.subList (0, length); - } else { - final var data = getData (); - - sequence = IntStream.range (0, length).map (i -> r.nextInt (data.size ())) - . mapToObj (data::get).collect (Collectors.toUnmodifiableList ()); - } - - if (doesSupportStatistics () && !sequence.isEmpty ()) { - final var conv = getStatisticsConverter (); - - final var statistics = sequence.stream ().mapToDouble (conv).summaryStatistics (); - double avg = statistics.getAverage (), min = statistics.getMin (), max = statistics.getMax (); - double med = sequence.stream ().mapToDouble (conv).sorted ().skip (length / 2).findFirst ().orElse (0.0); - - return new SequenceWithStatistics <> (sequence, length, min, max, avg, med); - } else { - return new SequenceWithStatistics <> (sequence); - } + default SequenceWithStatistics getRandomSequence (int levels, int length, Random r, boolean unique, double nulls) { + return getRandomSequence (levels, LevelsArrange.DISHONEST, length, r, unique, (int) Math.round (length * nulls)); } + SequenceWithStatistics getRandomSequence (int levels, LevelsArrange arrage, int length, Random r, boolean unique, int nulls); + } diff --git a/src/tests/presets/HexStrNumbers.java b/src/tests/presets/HexStrNumbers.java index 3e8b15e..a97ae68 100644 --- a/src/tests/presets/HexStrNumbers.java +++ b/src/tests/presets/HexStrNumbers.java @@ -7,10 +7,11 @@ public class HexStrNumbers extends IntStrNumbers { @Override - public void initialize (Random r) { + public HexStrNumbers initialize (Random r) { IntStream.range (0, 1000).map (__ -> r.nextInt (1000)) .mapToObj (n -> String.format ("0x%x", n)) - .forEach (numbers::add); + .forEach (values::add); + return this; } @Override diff --git a/src/tests/presets/IntNumbers.java b/src/tests/presets/IntNumbers.java index f7a9fc6..3dd6761 100644 --- a/src/tests/presets/IntNumbers.java +++ b/src/tests/presets/IntNumbers.java @@ -1,29 +1,15 @@ package tests.presets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Random; import java.util.function.ToDoubleFunction; import java.util.stream.IntStream; -public class IntNumbers implements DataPreset { +public class IntNumbers extends AbstractListPreset { - private final List numbers = new ArrayList <> (); - - @Override - public void initialize (Random r) { - IntStream.range (0, 1000).map (__ -> r.nextInt (1000)).forEach (numbers::add); - } - - @Override - public List getData () { - return Collections.unmodifiableList (numbers); - } - @Override - public boolean doesSupportStatistics () { - return true; + public IntNumbers initialize (Random r) { + IntStream.range (0, 1000).map (__ -> r.nextInt (1000)).forEach (values::add); + return this; } @Override diff --git a/src/tests/presets/IntStrNumbers.java b/src/tests/presets/IntStrNumbers.java index bd073aa..693680b 100644 --- a/src/tests/presets/IntStrNumbers.java +++ b/src/tests/presets/IntStrNumbers.java @@ -1,30 +1,16 @@ package tests.presets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Random; import java.util.function.ToDoubleFunction; import java.util.stream.IntStream; -public class IntStrNumbers implements DataPreset { +public class IntStrNumbers extends AbstractListPreset { - protected final List numbers = new ArrayList <> (); - @Override - public void initialize (Random r) { + public IntStrNumbers initialize (Random r) { IntStream.range (0, 1000).map (__ -> r.nextInt (1000)) - .mapToObj (String::valueOf).forEach (numbers::add); - } - - @Override - public List getData () { - return Collections.unmodifiableList (numbers); - } - - @Override - public boolean doesSupportStatistics () { - return true; + .mapToObj (String::valueOf).forEach (values::add); + return this; } @Override diff --git a/src/tests/presets/Items.java b/src/tests/presets/Items.java index a468576..db1d218 100644 --- a/src/tests/presets/Items.java +++ b/src/tests/presets/Items.java @@ -1,20 +1,15 @@ package tests.presets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import java.util.Random; import java.util.function.ToDoubleFunction; import java.util.stream.IntStream; import tasks.utils.Item; -public class Items implements DataPreset { - - private final List items = new ArrayList <> (); +public class Items extends AbstractListPreset { @Override - public void initialize (Random r) { + public Items initialize (Random r) { IntStream.range (0, 1000).mapToObj (__ -> { final var item = new Item (); @@ -25,23 +20,14 @@ public void initialize (Random r) { item.setCategory (randomCategory (r)); return item; - }).forEach (items::add); + }).forEach (values::add); + return this; } public static char randomCategory (Random r) { return (char) ('A' + r.nextInt (26)); } - @Override - public List getData () { - return Collections.unmodifiableList (items); - } - - @Override - public boolean doesSupportStatistics () { - return true; - } - @Override public ToDoubleFunction getStatisticsConverter () { return Item::getWeight; diff --git a/src/tests/presets/Names.java b/src/tests/presets/Names.java index 5a96c2e..e5f70a2 100644 --- a/src/tests/presets/Names.java +++ b/src/tests/presets/Names.java @@ -4,25 +4,18 @@ import java.util.Random; import java.util.function.ToDoubleFunction; -public class Names implements DataPreset { - - private final List names = List.of ( - "Andrey", "Boris", "Clement", "David", "Efim", "Feofan", - "Grigory", "Georgy", "Hoang", "Ilon", "Igor", "Jastin", - "Kevin", "Lewis", "Leander", "Marko", "Natan" - ); - - @Override - public List getData () { - return names; - } +public class Names extends AbstractListPreset { @Override - public void initialize (Random r) {} - - @Override - public boolean doesSupportStatistics () { - return true; + public Names initialize (Random r) { + values.addAll (List.of ( + "Andrey", "Boris", "Clement", "David", "Efim", "Feofan", + "Grigory", "Georgy", "Hoang", "Ilon", "Igor", "Jastin", + "Kevin", "Lewis", "Leander", "Marko", "Natan", "Olaf", + "Oleg", "Ostin", "Pavel", "Pedro", "Paul", "Ralf", "Roman", + "Sebastian", "Sergey", "Thor", "Tirley", "Taras" + )); + return this; } @Override diff --git a/src/tests/presets/NamesAges.java b/src/tests/presets/NamesAges.java new file mode 100644 index 0000000..d2379d4 --- /dev/null +++ b/src/tests/presets/NamesAges.java @@ -0,0 +1,26 @@ +package tests.presets; + +import java.util.List; +import java.util.Random; +import java.util.function.ToDoubleBiFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class NamesAges extends AbstractMapPreset { + + @Override + protected List initializeValues (Random r) { + return IntStream.range (0, 100).mapToObj (__ -> 5 + r.nextInt (40)).collect (Collectors.toList ()); + } + + @Override + protected List initializeKeys (Random r) { + return new Names ().initialize (r).> getRandomSequence (1, 30, r, true).data; + } + + @Override + protected ToDoubleBiFunction getStatisticsConverter () { + return (k, v) -> v; + } + +} diff --git a/src/tests/utils/TestInputCollection.java b/src/tests/utils/TestInputCollection.java index 09bcb42..f79c322 100644 --- a/src/tests/utils/TestInputCollection.java +++ b/src/tests/utils/TestInputCollection.java @@ -6,6 +6,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; +import tests.inputs.LevelsArrange; import tests.presets.DataPreset; @@ -13,7 +14,7 @@ @Retention (RUNTIME) public @interface TestInputCollection { - Class > [] presets () default {}; + Class [] presets () default {}; int [] constant () default {}; @@ -21,6 +22,16 @@ int variation () default 0; + int [] nulls () default {0}; + + int levels () default 1; + + /** + * Now works only for non-Map inputs + * @return + */ + LevelsArrange arrengement () default LevelsArrange.DISHONEST; + boolean allUnique () default false; boolean parallel () default false; diff --git a/src/tests/utils/TestInputConstant.java b/src/tests/utils/TestInputConstant.java index 7961483..66ad2be 100644 --- a/src/tests/utils/TestInputConstant.java +++ b/src/tests/utils/TestInputConstant.java @@ -15,6 +15,10 @@ int [] sequence () default {}; + /** + * 0 - length, 1 - minimum, 2 - max, 3 - average, 4 - median + * @return + */ int parameter () default -1; int variation () default 0; diff --git a/src/tests/utils/TestInputSupplier.java b/src/tests/utils/TestInputSupplier.java index b1c8128..0603840 100644 --- a/src/tests/utils/TestInputSupplier.java +++ b/src/tests/utils/TestInputSupplier.java @@ -14,7 +14,7 @@ @Retention (RUNTIME) public @interface TestInputSupplier { - Class > [] presets (); + Class [] presets (); SupplierMode mode () default SupplierMode.SHUFFLED_SEQUENTIAL;