From 7f14232243c14543321f4f3f57c4d5ece4b0be44 Mon Sep 17 00:00:00 2001 From: Andrej Fink Date: Mon, 17 Oct 2022 23:32:52 +0300 Subject: [PATCH] [#3] External functional loops --- .../src/main/java/org/jooq/lambda/Loops.java | 211 ++++++++++++++++++ jOOL/src/main/java/org/jooq/lambda/Loops.java | 211 ++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 jOOL-java-8/src/main/java/org/jooq/lambda/Loops.java create mode 100644 jOOL/src/main/java/org/jooq/lambda/Loops.java diff --git a/jOOL-java-8/src/main/java/org/jooq/lambda/Loops.java b/jOOL-java-8/src/main/java/org/jooq/lambda/Loops.java new file mode 100644 index 0000000..b8e00ae --- /dev/null +++ b/jOOL-java-8/src/main/java/org/jooq/lambda/Loops.java @@ -0,0 +1,211 @@ +package org.jooq.lambda; + +import org.jooq.lambda.tuple.Tuple4; + +import java.util.Arrays; +import java.util.LongSummaryStatistics; +import java.util.function.IntConsumer; +import java.util.function.IntUnaryOperator; +import java.util.function.LongConsumer; +import java.util.function.LongPredicate; +import java.util.function.Predicate; + +/** External lambda "for"-loops. */ +public class Loops { + private Loops () {}//static utils + + + /** + Simple loop (numberOfRepetitions .. 0]. +
{@code
+   while (numberOfRepetitions-- > 0)
+     body.run();
+   }
+ */ + public static void loop (long numberOfRepetitions, Runnable body) { + while (numberOfRepetitions-- > 0) { + body.run(); + } + } + + /** + Simple while-loop (numberOfRepetitions .. 0]. +
{@code}
+   while (numberOfRepetitions-- > 0)
+     body.accept(numberOfRepetitions);
+   }
+ */ + public static void loop (long numberOfRepetitions, LongConsumer body) { + while (numberOfRepetitions-- > 0) { + body.accept(numberOfRepetitions); + } + } + + /** + Simple for-loop [0 .. numberOfRepetitions). +
{@code}
+   for (int i=0; i<max; i++)
+     body.accept(i);
+   }
+ */ + public static void forLoop (int maxExclusive, IntConsumer body) { + for (int i=0; i{@code + while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) {} + } + @param body do-while Predicate: loops while it is true and numberOfRepetitions > 0 + */ + public static void loopWhile (long numberOfRepetitions, LongPredicate body) { + //noinspection StatementWithEmptyBody + while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) { + // do while + } + } + + // Ad-Hoc JMH (not for serious benchmarks) + + static final long NANO = 1000_000_000L; + + /** Similar to {@link #loop(long, Runnable)}, but with metrics */ + public static Tuple4 loopMeasured (long numberOfRepetitions, Runnable body) { + LongSummaryStatistics st = new LongSummaryStatistics(); + Exception ex = null; + long n = System.nanoTime(); + + while (numberOfRepetitions-- > 0) { + long t = System.nanoTime(); + try { + body.run(); + } catch (Exception showStopper) { + ex = showStopper; + break; + } finally { + st.accept(System.nanoTime() - t); + } + } + n = System.nanoTime() - n; + + StringBuilder sb = new StringBuilder(200);// ≤ ≥ + sb.append("LOOP: ").append(st.getCount()) + .append(" nanos= ").append(st.getSum()).append(" ≤ ").append(n) + .append(" ~ op/sec= "); + if (st.getSum() > 0) { + sb.append((st.getCount() * NANO) / st.getSum()).append(" ≥ "); + } + sb.append((st.getCount() * NANO) / Math.max(n, 1)); + if (st.getCount()>0) { + sb.append(" ~ nano/op= ").append(st.getSum() / st.getCount()).append(" ≤ ").append(n / st.getCount()); + } + sb.append(" min(n, st, ex, sb.toString()); + } + + /** Similar to {@link #loop(long, Runnable)}, but with metrics and warm-up */ + public static Tuple4 loopMeasuredWarm (long numberOfRepetitions, Runnable body) { + long warmCnt = Math.min(50_000, numberOfRepetitions/10+10); + + loop(warmCnt, body); + + return loopMeasured(numberOfRepetitions, body); + } + + + public static class Incrementer { + protected final int[] indexes; + private final IntUnaryOperator maxForIndex; + + /** All indexes have the same max and rotate in range [0..maxExclusive) + @param degree size of index vector = how many nested for-loops + @param maxExclusive number of repetitions: every index variable rotates in range [0..maxExclusive) + */ + public Incrementer (int degree, final int maxExclusive) { + assert degree > 0 : "degree must be > 0, but "+degree; + assert maxExclusive > 0 : "maxExclusive must be > 0, but "+maxExclusive; + + indexes = new int[degree]; + maxForIndex = (index) -> maxExclusive; + init(); + }//new + + /** + * Permutation with repetition. Every index has its own max. + * @param maxExclusive all indexes have their own max and rotate in range [0..maxExclusive[index]) + */ + public Incrementer (final int[] maxExclusive) { + assert maxExclusive.length > 0 : "degree must be > 0, but "+maxExclusive.length; + + indexes = new int[maxExclusive.length]; + maxForIndex = (index) -> maxExclusive[index]; + init(); + }//new + + protected void init () {} + + public int degree () { + return indexes.length; + } + + public int indexAt (int indexIndex) { + return indexes[indexIndex]; + } + + public int maxAt (int indexIndex) { + return maxForIndex.applyAsInt(indexIndex); + } + + @Override public String toString () { + return "Incrementer"+ Arrays.toString(indexes); + } + + + /** + Increments index vector (array with indexes) by 1. + E.g. for base 2: 0,0,0→0,0,1→0,1,0 + Emulates one step in multi-nested-for loop. + + @return we "overflowed" the array size = all indexes have reached their max-1 + @see #forLoop(Predicate) + */ + public boolean incrementIndexVector () { + final int len = indexes.length; + + for (int i = 0; i < len; ) { + if (++indexes[i] < maxForIndex.applyAsInt(i)) { + return false; + + } else {// overflow + indexes[i] = 0;// e.g. [10]: 09 → 10 + i++; + } + } + return true; + } + + /** + * Permutation with repetition. + * @param loopBody receive this Incrementer with indexes and maxExclusive, must return boolean searchIsOver/found + * @return searchIsOver/found + */ + public boolean forLoop (Predicate loopBody) { + while (true) { + boolean found = loopBody.test(this); + if (found) { + return true; + } + boolean overflow = incrementIndexVector(); + if (overflow) { + return false; + } + } + } + }//Incrementer + +} \ No newline at end of file diff --git a/jOOL/src/main/java/org/jooq/lambda/Loops.java b/jOOL/src/main/java/org/jooq/lambda/Loops.java new file mode 100644 index 0000000..b8e00ae --- /dev/null +++ b/jOOL/src/main/java/org/jooq/lambda/Loops.java @@ -0,0 +1,211 @@ +package org.jooq.lambda; + +import org.jooq.lambda.tuple.Tuple4; + +import java.util.Arrays; +import java.util.LongSummaryStatistics; +import java.util.function.IntConsumer; +import java.util.function.IntUnaryOperator; +import java.util.function.LongConsumer; +import java.util.function.LongPredicate; +import java.util.function.Predicate; + +/** External lambda "for"-loops. */ +public class Loops { + private Loops () {}//static utils + + + /** + Simple loop (numberOfRepetitions .. 0]. +
{@code
+   while (numberOfRepetitions-- > 0)
+     body.run();
+   }
+ */ + public static void loop (long numberOfRepetitions, Runnable body) { + while (numberOfRepetitions-- > 0) { + body.run(); + } + } + + /** + Simple while-loop (numberOfRepetitions .. 0]. +
{@code}
+   while (numberOfRepetitions-- > 0)
+     body.accept(numberOfRepetitions);
+   }
+ */ + public static void loop (long numberOfRepetitions, LongConsumer body) { + while (numberOfRepetitions-- > 0) { + body.accept(numberOfRepetitions); + } + } + + /** + Simple for-loop [0 .. numberOfRepetitions). +
{@code}
+   for (int i=0; i<max; i++)
+     body.accept(i);
+   }
+ */ + public static void forLoop (int maxExclusive, IntConsumer body) { + for (int i=0; i{@code + while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) {} + } + @param body do-while Predicate: loops while it is true and numberOfRepetitions > 0 + */ + public static void loopWhile (long numberOfRepetitions, LongPredicate body) { + //noinspection StatementWithEmptyBody + while (numberOfRepetitions-- > 0 && body.test(numberOfRepetitions)) { + // do while + } + } + + // Ad-Hoc JMH (not for serious benchmarks) + + static final long NANO = 1000_000_000L; + + /** Similar to {@link #loop(long, Runnable)}, but with metrics */ + public static Tuple4 loopMeasured (long numberOfRepetitions, Runnable body) { + LongSummaryStatistics st = new LongSummaryStatistics(); + Exception ex = null; + long n = System.nanoTime(); + + while (numberOfRepetitions-- > 0) { + long t = System.nanoTime(); + try { + body.run(); + } catch (Exception showStopper) { + ex = showStopper; + break; + } finally { + st.accept(System.nanoTime() - t); + } + } + n = System.nanoTime() - n; + + StringBuilder sb = new StringBuilder(200);// ≤ ≥ + sb.append("LOOP: ").append(st.getCount()) + .append(" nanos= ").append(st.getSum()).append(" ≤ ").append(n) + .append(" ~ op/sec= "); + if (st.getSum() > 0) { + sb.append((st.getCount() * NANO) / st.getSum()).append(" ≥ "); + } + sb.append((st.getCount() * NANO) / Math.max(n, 1)); + if (st.getCount()>0) { + sb.append(" ~ nano/op= ").append(st.getSum() / st.getCount()).append(" ≤ ").append(n / st.getCount()); + } + sb.append(" min(n, st, ex, sb.toString()); + } + + /** Similar to {@link #loop(long, Runnable)}, but with metrics and warm-up */ + public static Tuple4 loopMeasuredWarm (long numberOfRepetitions, Runnable body) { + long warmCnt = Math.min(50_000, numberOfRepetitions/10+10); + + loop(warmCnt, body); + + return loopMeasured(numberOfRepetitions, body); + } + + + public static class Incrementer { + protected final int[] indexes; + private final IntUnaryOperator maxForIndex; + + /** All indexes have the same max and rotate in range [0..maxExclusive) + @param degree size of index vector = how many nested for-loops + @param maxExclusive number of repetitions: every index variable rotates in range [0..maxExclusive) + */ + public Incrementer (int degree, final int maxExclusive) { + assert degree > 0 : "degree must be > 0, but "+degree; + assert maxExclusive > 0 : "maxExclusive must be > 0, but "+maxExclusive; + + indexes = new int[degree]; + maxForIndex = (index) -> maxExclusive; + init(); + }//new + + /** + * Permutation with repetition. Every index has its own max. + * @param maxExclusive all indexes have their own max and rotate in range [0..maxExclusive[index]) + */ + public Incrementer (final int[] maxExclusive) { + assert maxExclusive.length > 0 : "degree must be > 0, but "+maxExclusive.length; + + indexes = new int[maxExclusive.length]; + maxForIndex = (index) -> maxExclusive[index]; + init(); + }//new + + protected void init () {} + + public int degree () { + return indexes.length; + } + + public int indexAt (int indexIndex) { + return indexes[indexIndex]; + } + + public int maxAt (int indexIndex) { + return maxForIndex.applyAsInt(indexIndex); + } + + @Override public String toString () { + return "Incrementer"+ Arrays.toString(indexes); + } + + + /** + Increments index vector (array with indexes) by 1. + E.g. for base 2: 0,0,0→0,0,1→0,1,0 + Emulates one step in multi-nested-for loop. + + @return we "overflowed" the array size = all indexes have reached their max-1 + @see #forLoop(Predicate) + */ + public boolean incrementIndexVector () { + final int len = indexes.length; + + for (int i = 0; i < len; ) { + if (++indexes[i] < maxForIndex.applyAsInt(i)) { + return false; + + } else {// overflow + indexes[i] = 0;// e.g. [10]: 09 → 10 + i++; + } + } + return true; + } + + /** + * Permutation with repetition. + * @param loopBody receive this Incrementer with indexes and maxExclusive, must return boolean searchIsOver/found + * @return searchIsOver/found + */ + public boolean forLoop (Predicate loopBody) { + while (true) { + boolean found = loopBody.test(this); + if (found) { + return true; + } + boolean overflow = incrementIndexVector(); + if (overflow) { + return false; + } + } + } + }//Incrementer + +} \ No newline at end of file