001package stdlib;
002import java.util.function.*;
003
004public class DoublingTest {
005        private static double time;
006        private static long ops;
007        /**
008         * During a doubling test, the provided code may call {@code incOps} and/or {@code addOps}.
009         * If either is called, then the number of operations so recorded will be reported by {@code run}.
010         */
011        public static void incOps () { ops++; }
012        /**
013         * During a doubling test, the provided code may call {@code incOps} and/or {@code addOps}.
014         * If either is called, then the number of operations so recorded will be reported by {@code run}.
015         */
016        public static void addOps (int count) {
017                if (count < 1) throw new IllegalArgumentException ();
018                ops += count;
019        }
020        /**
021         * See fully parameterized {@code run} for a description.
022         * In this abbreviated form, {@code numTestsPerValue} is 1.
023         * {@code setup} does nothing, therefore no value is provided to {@code timed}.
024         */
025        public static void run (int initialValue, int numValues, Consumer<Integer> timed) {
026                        run (initialValue, numValues, 1, x -> {}, timed);
027        }
028        /**
029         * See fully parameterized {@code run} for a description.
030         * In this abbreviated form, {@code numTestsPerValue} is 1.
031         * No value is passed from {@code setup} to {@code timed}.
032         */
033        public static void run (int initialValue, int numValues, Consumer<Integer> setup, Consumer<Integer> timed) {
034                run (initialValue, numValues, 1, setup, timed);
035        }
036        /**
037         * See fully parameterized {@code run} for a description.
038         * In this abbreviated form, no value is passed from {@code setup} to {@code timed}.
039         */
040        public static void run (int initialValue, int numValues, int numTestsPerValue, Consumer<Integer> setup, Consumer<Integer> timed) {
041                run (initialValue, numValues, numTestsPerValue, (Integer x) -> { setup.accept (x); return null; }, (x,t) -> timed.accept (x));
042        }
043        /**
044         * See fully parameterized {@code run} for a description.
045         * In this abbreviated form, {@code numTestsPerValue} is 1.
046         */
047        public static <T> void run (int initialValue, int numValues, Function<Integer,T> setup, BiConsumer<Integer,T> timed) {
048                run (initialValue, numValues, 1, setup, timed);
049        }
050        /**
051         * See fully parameterized {@code run} for a description.
052         * In this abbreviated form, the integer value is not passed to {@code timed}.
053         */
054        public static <T> void run (int initialValue, int numValues, int numTestsPerValue, Function<Integer,T> setup, Consumer<T> timed) {
055                run (initialValue, numValues, numTestsPerValue, setup, (x,t) -> timed.accept (t));
056        }
057        /**
058         * See fully parameterized {@code run} for a description.
059         * In this abbreviated form, {@code numTestsPerValue} is 1.
060         * The integer value is not passed to {@code timed}.
061         */
062        public static <T> void run (int initialValue, int numValues, Function<Integer,T> setup, Consumer<T> timed) {
063                run (initialValue, numValues, 1, setup, timed);
064        }
065        /**
066         * Run the function {@code timed} with increasing large values for the first integer parameter (N).
067         * Running time is reported to the console.
068         * Initial value of N is given by {@code initialValue}.
069         * N doubles each time the test is run.
070         * The number of different values for N is given by {@code numValues}.
071         * If {@code numTestsPerValue} is larger than one, then the test will be run multiple times for each N, and the average time reported.
072         * The {@code setup} function is called before {@code timed}, and the result (of type T) is provided to {@code timed} as the second parameter.
073         * {@code setup} is called once before each call to {@code timed}.
074         */
075        public static <T> void run (int initialValue, int numValues, int numTestsPerValue, Function<Integer,T> setup, BiConsumer<Integer,T> timed) {
076                int value = initialValue;
077                runOnce (value, numTestsPerValue, setup, timed);
078                if (ops != 0) {
079                        System.out.format("N=%,13d, ops=%,13d,             seconds=%5.3f\n", value, ops/numTestsPerValue, time/numTestsPerValue);
080                } else {
081                        System.out.format("N=%,13d, seconds=%5.3f\n", value, time/numTestsPerValue);
082                }
083                double prevOps = ops;
084                double prevTime = time;
085                for (int i=1; i<numValues; i++) {
086                        value *= 2;
087                        runOnce (value, numTestsPerValue, setup, timed);
088                        if (ops != 0) {
089                                System.out.format("N=%,13d, ops=%,13d ratio=%5.1f, seconds=%5.3f ratio=%5.3f\n", value, ops/numTestsPerValue, ops/prevOps, time/numTestsPerValue, time/prevTime);
090                        } else {
091                                System.out.format("N=%,13d, seconds=%5.3f ratio=%5.3f\n", value, time/numTestsPerValue, time/prevTime);
092                        }
093                        prevOps = ops;
094                        prevTime = time;
095                }
096        }
097        private static <T> void runOnce (int value, int numTestsPerValue, Function<Integer,T> setup, BiConsumer<Integer,T> timed) {
098                ops = 0;
099                time = 0;
100                for (int testNum = 0; testNum < numTestsPerValue; testNum++) {
101                        T setupResult = setup.apply (value);
102                        Stopwatch sw = new Stopwatch ();
103                        timed.accept (value, setupResult);
104                        time += sw.elapsedTime ();
105                }
106        }
107
108        // Now repeat everything with Long, rather than Integer
109
110        /**
111         * See fully parameterized {@code runLong} for a description.
112         * In this abbreviated form, {@code numTestsPerValue} is 1.
113         * {@code setup} does nothing, therefore no value is provided to {@code timed}.
114         */
115        public static void runLong (long initialValue, int numValues, Consumer<Long> timed) {
116                runLong (initialValue, numValues, 1, x -> {}, timed);
117        }
118        /**
119         * See fully parameterized {@code runLong} for a description.
120         * In this abbreviated form, {@code numTestsPerValue} is 1.
121         * No value is passed from {@code setup} to {@code timed}.
122         */
123        public static void runLong (long initialValue, int numValues, Consumer<Long> setup, Consumer<Long> timed) {
124                runLong (initialValue, numValues, 1, setup, timed);
125        }
126        /**
127         * See fully parameterized {@code runLong} for a description.
128         * In this abbreviated form, no value is passed from {@code setup} to {@code timed}.
129         */
130        public static void runLong (long initialValue, int numValues, int numTestsPerValue, Consumer<Long> setup, Consumer<Long> timed) {
131                runLong (initialValue, numValues, numTestsPerValue, (Long x) -> { setup.accept (x); return null; }, (x,t) -> timed.accept (x));
132        }
133        /**
134         * See fully parameterized {@code runLong} for a description.
135         * In this abbreviated form, {@code numTestsPerValue} is 1.
136         */
137        public static <T> void runLong (long initialValue, int numValues, Function<Long,T> setup, BiConsumer<Long,T> timed) {
138                runLong (initialValue, numValues, 1, setup, timed);
139        }
140        /**
141         * See fully parameterized {@code runLong} for a description.
142         * In this abbreviated form, the integer value is not passed to {@code timed}.
143         */
144        public static <T> void runLong (long initialValue, int numValues, int numTestsPerValue, Function<Long,T> setup, Consumer<T> timed) {
145                runLong (initialValue, numValues, numTestsPerValue, setup, (x,t) -> timed.accept (t));
146        }
147        /**
148         * See fully parameterized {@code runLong} for a description.
149         * In this abbreviated form, {@code numTestsPerValue} is 1.
150         * The integer value is not passed to {@code timed}.
151         */
152        public static <T> void runLong (long initialValue, int numValues, Function<Long,T> setup, Consumer<T> timed) {
153                runLong (initialValue, numValues, 1, setup, timed);
154        }
155        /**
156         * Run the function {@code timed} with increasing large values for the first integer parameter (N).
157         * Running time is reported to the console.
158         * Initial value of N is given by {@code initialValue}.
159         * N doubles each time the test is runLong.
160         * The number of different values for N is given by {@code numValues}.
161         * If {@code numTestsPerValue} is larger than one, then the test will be runLong multiple times for each N, and the average time reported.
162         * The {@code setup} function is called before {@code timed}, and the result (of type T) is provided to {@code timed} as the second parameter.
163         * {@code setup} is called once before each call to {@code timed}.
164         */
165        public static <T> void runLong (long initialValue, int numValues, int numTestsPerValue, Function<Long,T> setup, BiConsumer<Long,T> timed) {
166                long value = initialValue;
167                runOnce (value, numTestsPerValue, setup, timed);
168                if (ops != 0) {
169                        System.out.format("N=%,13d, ops=%,13d,             seconds=%5.3f\n", value, ops/numTestsPerValue, time/numTestsPerValue);
170                } else {
171                        System.out.format("N=%,13d, seconds=%5.3f\n", value, time/numTestsPerValue);
172                }
173                double prevOps = ops;
174                double prevTime = time;
175                for (int i=1; i<numValues; i++) {
176                        value *= 2;
177                        runOnce (value, numTestsPerValue, setup, timed);
178                        if (ops != 0) {
179                                System.out.format("N=%,13d, ops=%,13d ratio=%5.1f, seconds=%5.3f ratio=%5.3f\n", value, ops/numTestsPerValue, ops/prevOps, time/numTestsPerValue, time/prevTime);
180                        } else {
181                                System.out.format("N=%,13d, seconds=%5.3f ratio=%5.3f\n", value, time/numTestsPerValue, time/prevTime);
182                        }
183                        prevOps = ops;
184                        prevTime = time;
185                }
186        }
187        private static <T> void runOnce (long value, int numTestsPerValue, Function<Long,T> setup, BiConsumer<Long,T> timed) {
188                ops = 0;
189                time = 0;
190                for (int testNum = 0; testNum < numTestsPerValue; testNum++) {
191                        T setupResult = setup.apply (value);
192                        Stopwatch sw = new Stopwatch ();
193                        timed.accept (value, setupResult);
194                        time += sw.elapsedTime ();
195                }
196        }
197}