1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* UT_reduce_backward.java is a much simpler version of this test
18 * case that exercises pragmas after the functions (backward
19 * reference), whereas this test case exercises the pragmas before
20 * the functions (forward reference).
21 */
22
23package com.android.rs.unittest;
24
25import android.content.Context;
26import android.renderscript.Allocation;
27import android.renderscript.Element;
28import android.renderscript.Float2;
29import android.renderscript.Int2;
30import android.renderscript.Int3;
31import android.renderscript.RenderScript;
32import android.renderscript.ScriptIntrinsicHistogram;
33import android.renderscript.Type;
34import android.util.Log;
35
36import java.util.ArrayList;
37import java.util.Arrays;
38import java.util.Random;
39
40import static junit.framework.Assert.assertEquals;
41import static junit.framework.Assert.assertTrue;
42
43public class UT_reduce extends UnitTest {
44    private static final String TAG = "reduce";
45
46    public UT_reduce(Context ctx) {
47        super("reduce", ctx);
48    }
49
50    private static class timing {
51        timing(long myJavaStart, long myJavaEnd, long myRsStart,
52               long myCopyStart, long myKernelStart, long myRsEnd,
53               Allocation... myInputs) {
54            javaStart = myJavaStart;
55            javaEnd = myJavaEnd;
56            rsStart = myRsStart;
57            copyStart = myCopyStart;
58            kernelStart = myKernelStart;
59            rsEnd = myRsEnd;
60
61            inputBytes = 0;
62            for (Allocation input : myInputs)
63                inputBytes += input.getBytesSize();
64
65            inputCells = (myInputs.length > 0) ? myInputs[0].getType().getCount() : 0;
66        }
67
68        timing(long myInputCells) {
69            inputCells = myInputCells;
70        }
71
72        private long javaStart = -1;
73        private long javaEnd = -1;
74        private long rsStart = -1;
75        private long copyStart = -1;
76        private long kernelStart = -1;
77        private long rsEnd = -1;
78        private long inputBytes = -1;
79        private long inputCells = -1;
80
81        public long javaTime() {
82            return javaEnd - javaStart;
83        }
84
85        public long rsTime() {
86            return rsEnd - rsStart;
87        }
88
89        public long kernelTime() {
90            return rsEnd - kernelStart;
91        }
92
93        public long overheadTime() {
94            return kernelStart - rsStart;
95        }
96
97        public long allocationTime() {
98            return copyStart - rsStart;
99        }
100
101        public long copyTime() {
102            return kernelStart - copyStart;
103        }
104
105        public static String string(long myJavaStart, long myJavaEnd, long myRsStart,
106                                    long myCopyStart, long myKernelStart, long myRsEnd,
107                                    Allocation... myInputs) {
108            return (new timing(myJavaStart, myJavaEnd, myRsStart,
109                    myCopyStart, myKernelStart, myRsEnd, myInputs)).string();
110        }
111
112        public static String string(long myInputCells) {
113            return (new timing(myInputCells)).string();
114        }
115
116        public String string() {
117            String result;
118            if (javaStart >= 0) {
119                result = "(java " + javaTime() + "ms, rs " + rsTime() + "ms = overhead " +
120                        overheadTime() + "ms (alloc " + allocationTime() + "ms + copy " +
121                        copyTime() + "ms) + kernel+get() " + kernelTime() + "ms)";
122                if (inputCells > 0)
123                    result += " ";
124            } else {
125                result = "";
126            }
127            if (inputCells > 0) {
128                result += "(" + fmt.format(inputCells) + " cells";
129                if (inputBytes > 0)
130                    result += ", " + fmt.format(inputBytes) + " bytes";
131                result += ")";
132            }
133            return result;
134        }
135
136        private static java.text.DecimalFormat fmt;
137
138        static {
139            fmt = new java.text.DecimalFormat("###,###");
140        }
141    }
142
143    private byte[] createInputArrayByte(int len, int seed) {
144        byte[] array = new byte[len];
145        (new Random(seed)).nextBytes(array);
146        return array;
147    }
148
149    private float[] createInputArrayFloat(int len, Random rand) {
150        float[] array = new float[len];
151        for (int i = 0; i < len; ++i) {
152            final float val = rand.nextFloat();
153            array[i] = rand.nextBoolean() ? val : -val;
154        }
155        return array;
156    }
157
158    private float[] createInputArrayFloat(int len, int seed) {
159        return createInputArrayFloat(len, new Random(seed));
160    }
161
162    private float[] createInputArrayFloatWithInfs(int len, int infs, int seed) {
163        Random rand = new Random(seed);
164        float[] array = createInputArrayFloat(len, rand);
165        for (int i = 0; i < infs; ++i)
166            array[rand.nextInt(len)] = (rand.nextBoolean() ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY);
167        return array;
168    }
169
170    private int[] createInputArrayInt(int len, int seed) {
171        Random rand = new Random(seed);
172        int[] array = new int[len];
173        for (int i = 0; i < len; ++i)
174            array[i] = rand.nextInt();
175        return array;
176    }
177
178    private int[] createInputArrayInt(int len, int seed, int eltRange) {
179        Random rand = new Random(seed);
180        int[] array = new int[len];
181        for (int i = 0; i < len; ++i)
182            array[i] = rand.nextInt(eltRange);
183        return array;
184    }
185
186    private long[] intArrayToLong(final int[] input) {
187        final long[] output = new long[input.length];
188
189        for (int i = 0; i < input.length; ++i)
190            output[i] = input[i];
191
192        return output;
193    }
194
195    private <T extends Number> boolean result(String testName, final timing t,
196                                              T javaResult, T rsResult) {
197        final boolean success = javaResult.equals(rsResult);
198        String status = (success ? "PASSED" : "FAILED");
199        if (success && (t != null))
200            status += " " + t.string();
201        Log.i(TAG, testName + ": java " + javaResult + ", rs " + rsResult + ": " + status);
202        return success;
203    }
204
205    private boolean result(String testName, final timing t,
206                           final float[] javaResult, final float[] rsResult) {
207        if (javaResult.length != rsResult.length) {
208            Log.i(TAG, testName + ": java length " + javaResult.length +
209                    ", rs length " + rsResult.length + ": FAILED");
210            return false;
211        }
212        for (int i = 0; i < javaResult.length; ++i) {
213            if (javaResult[i] != rsResult[i]) {
214                Log.i(TAG, testName + "[" + i + "]: java " + javaResult[i] +
215                        ", rs " + rsResult[i] + ": FAILED");
216                return false;
217            }
218        }
219        String status = "PASSED";
220        if (t != null)
221            status += " " + t.string();
222        Log.i(TAG, testName + ": " + status);
223        return true;
224    }
225
226    private boolean result(String testName, final timing t,
227                           final long[] javaResult, final long[] rsResult) {
228        if (javaResult.length != rsResult.length) {
229            Log.i(TAG, testName + ": java length " + javaResult.length +
230                    ", rs length " + rsResult.length + ": FAILED");
231            return false;
232        }
233        for (int i = 0; i < javaResult.length; ++i) {
234            if (javaResult[i] != rsResult[i]) {
235                Log.i(TAG, testName + "[" + i + "]: java " + javaResult[i] +
236                        ", rs " + rsResult[i] + ": FAILED");
237                return false;
238            }
239        }
240        String status = "PASSED";
241        if (t != null)
242            status += " " + t.string();
243        Log.i(TAG, testName + ": " + status);
244        return true;
245    }
246
247    private boolean result(String testName, final timing t,
248                           final int[] javaResult, final int[] rsResult) {
249        return result(testName, t, intArrayToLong(javaResult), intArrayToLong(rsResult));
250    }
251
252    private boolean result(String testName, final timing t, Int2 javaResult, Int2 rsResult) {
253        final boolean success = (javaResult.x == rsResult.x) && (javaResult.y == rsResult.y);
254        String status = (success ? "PASSED" : "FAILED");
255        if (success && (t != null))
256            status += " " + t.string();
257        Log.i(TAG,
258                testName +
259                        ": java (" + javaResult.x + ", " + javaResult.y + ")" +
260                        ", rs (" + rsResult.x + ", " + rsResult.y + ")" +
261                        ": " + status);
262        return success;
263    }
264
265    private boolean result(String testName, final timing t, Float2 javaResult, Float2 rsResult) {
266        final boolean success = (javaResult.x == rsResult.x) && (javaResult.y == rsResult.y);
267        String status = (success ? "PASSED" : "FAILED");
268        if (success && (t != null))
269            status += " " + t.string();
270        Log.i(TAG,
271                testName +
272                        ": java (" + javaResult.x + ", " + javaResult.y + ")" +
273                        ", rs (" + rsResult.x + ", " + rsResult.y + ")" +
274                        ": " + status);
275        return success;
276    }
277
278    ///////////////////////////////////////////////////////////////////
279
280    private int addint(int[] input) {
281        int result = 0;
282        for (int idx = 0; idx < input.length; ++idx)
283            result += input[idx];
284        return result;
285    }
286
287    private boolean addint1D_array(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
288        final int[] input = createInputArrayInt(size[0], seed, Integer.MAX_VALUE / size[0]);
289
290        final int javaResult = addint(input);
291        final int rsResult = s.reduce_addint(input).get();
292
293        return result("addint1D_array", new timing(size[0]), javaResult, rsResult);
294    }
295
296    private boolean addint1D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
297        final int[] inputArray = createInputArrayInt(size[0], seed, Integer.MAX_VALUE / size[0]);
298
299        final long javaTimeStart = java.lang.System.currentTimeMillis();
300        final int javaResult = addint(inputArray);
301        final long javaTimeEnd = java.lang.System.currentTimeMillis();
302
303        final long rsTimeStart = java.lang.System.currentTimeMillis();
304
305        Allocation inputAllocation = Allocation.createSized(RS, Element.I32(RS), inputArray.length);
306
307        final long copyTimeStart = java.lang.System.currentTimeMillis();
308
309        inputAllocation.copyFrom(inputArray);
310
311        final long kernelTimeStart = java.lang.System.currentTimeMillis();
312        final int rsResult = s.reduce_addint(inputAllocation).get();
313        final long rsTimeEnd = java.lang.System.currentTimeMillis();
314
315        final boolean success =
316                result("addint1D",
317                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
318                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
319                        javaResult, rsResult);
320        inputAllocation.destroy();
321        return success;
322    }
323
324    private boolean addint2D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
325        final int dimX = size[0];
326        final int dimY = size[1];
327
328        final int[] inputArray = createInputArrayInt(dimX * dimY, seed, Integer.MAX_VALUE / (dimX * dimY));
329
330        final long javaTimeStart = java.lang.System.currentTimeMillis();
331        final int javaResult = addint(inputArray);
332        final long javaTimeEnd = java.lang.System.currentTimeMillis();
333
334        final long rsTimeStart = java.lang.System.currentTimeMillis();
335
336        Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
337        typeBuilder.setX(dimX).setY(dimY);
338        Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
339
340        final long copyTimeStart = java.lang.System.currentTimeMillis();
341
342        inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
343
344        final long kernelTimeStart = java.lang.System.currentTimeMillis();
345        final int rsResult = s.reduce_addint(inputAllocation).get();
346        final long rsTimeEnd = java.lang.System.currentTimeMillis();
347
348        final boolean success =
349                result("addint2D",
350                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
351                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
352                        javaResult, rsResult);
353        inputAllocation.destroy();
354        return success;
355    }
356
357    private boolean addint3D(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
358        final int dimX = size[0];
359        final int dimY = size[1];
360        final int dimZ = size[2];
361
362        final int[] inputArray = createInputArrayInt(dimX * dimY * dimZ, seed, Integer.MAX_VALUE / (dimX * dimY * dimZ));
363
364        final long javaTimeStart = java.lang.System.currentTimeMillis();
365        final int javaResult = addint(inputArray);
366        final long javaTimeEnd = java.lang.System.currentTimeMillis();
367
368        final long rsTimeStart = java.lang.System.currentTimeMillis();
369
370        Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
371        typeBuilder.setX(dimX).setY(dimY).setZ(dimZ);
372        Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
373
374        final long copyTimeStart = java.lang.System.currentTimeMillis();
375
376        inputAllocation.copy3DRangeFrom(0, 0, 0, dimX, dimY, dimZ, inputArray);
377
378        final long kernelTimeStart = java.lang.System.currentTimeMillis();
379        final int rsResult = s.reduce_addint(inputAllocation).get();
380        final long rsTimeEnd = java.lang.System.currentTimeMillis();
381
382        final boolean success =
383                result("addint3D",
384                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
385                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
386                        javaResult, rsResult);
387        inputAllocation.destroy();
388        return success;
389    }
390
391    //-----------------------------------------------------------------
392
393    private boolean patternInterleavedReduce(RenderScript RS, ScriptC_reduce s) {
394        // Run two reduce operations without forcing completion between them.
395        // We want to ensure that the driver can handle this, and that
396        // temporary Allocations created to run the reduce operations survive
397        // until get().
398
399        boolean pass = true;
400
401        final int inputSize = (1 << 18);
402
403        final int[] input1 = createInputArrayInt(123, Integer.MAX_VALUE / inputSize);
404        final int[] input2 = createInputArrayInt(456, Integer.MAX_VALUE / inputSize);
405
406        final int javaResult1 = addint(input1);
407        final int javaResult2 = addint(input2);
408
409        final ScriptC_reduce.result_int rsResultFuture1 = s.reduce_addint(input1);
410        final ScriptC_reduce.result_int rsResultFuture2 = s.reduce_addint(input2);
411
412        pass &= result("patternInterleavedReduce (1)", new timing(inputSize),
413                javaResult1, rsResultFuture1.get());
414        pass &= result("patternInterleavedReduce (2)", new timing(inputSize),
415                javaResult2, rsResultFuture2.get());
416
417        return pass;
418    }
419
420    //-----------------------------------------------------------------
421
422    private int[] sillySumIntoDecArray(final int[] input) {
423        final int resultScalar = addint(input);
424        final int[] result = new int[4];
425        for (int i = 0; i < 4; ++i)
426            result[i] = resultScalar / (i + 1);
427        return result;
428    }
429
430    private int[] sillySumIntoIncArray(final int[] input) {
431        final int resultScalar = addint(input);
432        final int[] result = new int[4];
433        for (int i = 0; i < 4; ++i)
434            result[i] = resultScalar / (4 - i);
435        return result;
436    }
437
438    private boolean patternDuplicateAnonymousResult(RenderScript RS, ScriptC_reduce s) {
439        // Ensure that we can have two kernels with the same anonymous result type.
440
441        boolean pass = true;
442
443        final int inputSize = 1000;
444        final int[] input = createInputArrayInt(149, Integer.MAX_VALUE / inputSize);
445
446        final int[] javaResultDec = sillySumIntoDecArray(input);
447        final int[] rsResultDec = s.reduce_sillySumIntoDecArray(input).get();
448        pass &= result("patternDuplicateAnonymousResult (Dec)", new timing(inputSize),
449                javaResultDec, rsResultDec);
450
451        final int[] javaResultInc = sillySumIntoIncArray(input);
452        final int[] rsResultInc = s.reduce_sillySumIntoIncArray(input).get();
453        pass &= result("patternDuplicateAnonymousResult (Inc)", new timing(inputSize),
454                javaResultInc, rsResultInc);
455
456        return pass;
457    }
458
459    ///////////////////////////////////////////////////////////////////
460
461    private float findMinAbs(float[] input) {
462        float accum = input[0];
463        for (int idx = 1; idx < input.length; ++idx) {
464            final float val = input[idx];
465            if (Math.abs(val) < Math.abs(accum))
466                accum = val;
467        }
468        return accum;
469    }
470
471    static interface ReduceFindMinAbs {
472        float run(Allocation input);
473    }
474
475    private boolean findMinAbs(RenderScript RS, float[] inputArray, String testName, ReduceFindMinAbs reduction) {
476        final long javaTimeStart = java.lang.System.currentTimeMillis();
477        final float javaResult = findMinAbs(inputArray);
478        final long javaTimeEnd = java.lang.System.currentTimeMillis();
479
480        final long rsTimeStart = java.lang.System.currentTimeMillis();
481
482        Allocation inputAllocation = Allocation.createSized(RS, Element.F32(RS), inputArray.length);
483
484        final long copyTimeStart = java.lang.System.currentTimeMillis();
485
486        inputAllocation.copyFrom(inputArray);
487
488        final long kernelTimeStart = java.lang.System.currentTimeMillis();
489        final float rsResult = reduction.run(inputAllocation);
490        final long rsTimeEnd = java.lang.System.currentTimeMillis();
491
492        // Note that the Java and RenderScript algorithms are not
493        // guaranteed to find the same results -- but the results
494        // should have the same absolute value.
495
496        final boolean success =
497                result(testName,
498                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
499                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
500                        Math.abs(javaResult), Math.abs(rsResult));
501        inputAllocation.destroy();
502        return success;
503    }
504
505    private boolean findMinAbsBool(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
506        return findMinAbs(RS, createInputArrayFloat(size[0], seed), "findMinAbsBool",
507                (Allocation input) -> s.reduce_findMinAbsBool(input).get());
508    }
509
510    private boolean findMinAbsBoolInf(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
511        return findMinAbs(RS, createInputArrayFloatWithInfs(size[0], 1 + size[0] / 1000, seed), "findMinAbsBoolInf",
512                (Allocation input) -> s.reduce_findMinAbsBool(input).get());
513    }
514
515    private boolean findMinAbsNaN(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
516        return findMinAbs(RS, createInputArrayFloat(size[0], seed), "findMinAbsNaN",
517                (Allocation input) -> s.reduce_findMinAbsNaN(input).get());
518    }
519
520    private boolean findMinAbsNaNInf(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
521        return findMinAbs(RS, createInputArrayFloatWithInfs(size[0], 1 + size[0] / 1000, seed), "findMinAbsNaNInf",
522                (Allocation input) -> s.reduce_findMinAbsNaN(input).get());
523    }
524
525    ///////////////////////////////////////////////////////////////////
526
527    private Int2 findMinAndMax(float[] input) {
528        float minVal = Float.POSITIVE_INFINITY;
529        int minIdx = -1;
530        float maxVal = Float.NEGATIVE_INFINITY;
531        int maxIdx = -1;
532
533        for (int idx = 0; idx < input.length; ++idx) {
534            if ((minIdx < 0) || (input[idx] < minVal)) {
535                minVal = input[idx];
536                minIdx = idx;
537            }
538            if ((maxIdx < 0) || (input[idx] > maxVal)) {
539                maxVal = input[idx];
540                maxIdx = idx;
541            }
542        }
543
544        return new Int2(minIdx, maxIdx);
545    }
546
547    private boolean findMinAndMax_array(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
548        final float[] input = createInputArrayFloat(size[0], seed);
549
550        final Int2 javaResult = findMinAndMax(input);
551        final Int2 rsResult = s.reduce_findMinAndMax(input).get();
552
553        // Note that the Java and RenderScript algorithms are not
554        // guaranteed to find the same cells -- but they should
555        // find cells of the same value.
556        final Float2 javaVal = new Float2(input[javaResult.x], input[javaResult.y]);
557        final Float2 rsVal = new Float2(input[rsResult.x], input[rsResult.y]);
558
559        return result("findMinAndMax_array", new timing(size[0]), javaVal, rsVal);
560    }
561
562    private boolean findMinAndMax(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
563        final float[] inputArray = createInputArrayFloat(size[0], seed);
564
565        final long javaTimeStart = java.lang.System.currentTimeMillis();
566        final Int2 javaResult = findMinAndMax(inputArray);
567        final long javaTimeEnd = java.lang.System.currentTimeMillis();
568
569        final long rsTimeStart = java.lang.System.currentTimeMillis();
570
571        Allocation inputAllocation = Allocation.createSized(RS, Element.F32(RS), inputArray.length);
572
573        final long copyTimeStart = java.lang.System.currentTimeMillis();
574
575        inputAllocation.copyFrom(inputArray);
576
577        final long kernelTimeStart = java.lang.System.currentTimeMillis();
578        final Int2 rsResult = s.reduce_findMinAndMax(inputAllocation).get();
579        final long rsTimeEnd = java.lang.System.currentTimeMillis();
580
581        // Note that the Java and RenderScript algorithms are not
582        // guaranteed to find the same cells -- but they should
583        // find cells of the same value.
584        final Float2 javaVal = new Float2(inputArray[javaResult.x], inputArray[javaResult.y]);
585        final Float2 rsVal = new Float2(inputArray[rsResult.x], inputArray[rsResult.y]);
586
587        final boolean success =
588                result("findMinAndMax",
589                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
590                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
591                        javaVal, rsVal);
592        inputAllocation.destroy();
593        return success;
594    }
595
596    //-----------------------------------------------------------------
597
598    private boolean patternFindMinAndMaxInf(RenderScript RS, ScriptC_reduce s) {
599        // Run this kernel on an input consisting solely of a single infinity.
600
601        final float[] input = new float[1];
602        input[0] = Float.POSITIVE_INFINITY;
603
604        final Int2 javaResult = findMinAndMax(input);
605        final Int2 rsResult = s.reduce_findMinAndMax(input).get();
606
607        // Note that the Java and RenderScript algorithms are not
608        // guaranteed to find the same cells -- but they should
609        // find cells of the same value.
610        final Float2 javaVal = new Float2(input[javaResult.x], input[javaResult.y]);
611        final Float2 rsVal = new Float2(input[rsResult.x], input[rsResult.y]);
612
613        return result("patternFindMinAndMaxInf", new timing(1), javaVal, rsVal);
614    }
615
616    ///////////////////////////////////////////////////////////////////
617
618    // Both the input and the result are linearized representations of matSize*matSize matrices.
619    private float[] findMinMat(final float[] inputArray, final int matSize) {
620        final int matSizeSquared = matSize * matSize;
621
622        float[] result = new float[matSizeSquared];
623        for (int i = 0; i < matSizeSquared; ++i)
624            result[i] = Float.POSITIVE_INFINITY;
625
626        for (int i = 0; i < inputArray.length; ++i)
627            result[i % matSizeSquared] = Math.min(result[i % matSizeSquared], inputArray[i]);
628
629        return result;
630    }
631
632    static interface ReduceFindMinMat {
633        float[] run(Allocation input);
634    }
635
636    private boolean findMinMat(RenderScript RS, int seed, int[] inputSize,
637                               int matSize, Element matElement, ReduceFindMinMat reduction) {
638        final int length = inputSize[0];
639        final int matSizeSquared = matSize * matSize;
640
641        final float[] inputArray = createInputArrayFloat(matSizeSquared * length, seed);
642
643        final long javaTimeStart = java.lang.System.currentTimeMillis();
644        final float[] javaResult = findMinMat(inputArray, matSize);
645        final long javaTimeEnd = java.lang.System.currentTimeMillis();
646
647        final long rsTimeStart = java.lang.System.currentTimeMillis();
648
649        Allocation inputAllocation = Allocation.createSized(RS, matElement, length);
650
651        final long copyTimeStart = java.lang.System.currentTimeMillis();
652
653        inputAllocation.copyFromUnchecked(inputArray);
654
655        final long kernelTimeStart = java.lang.System.currentTimeMillis();
656        final float[] rsResult = reduction.run(inputAllocation);
657        final long rsTimeEnd = java.lang.System.currentTimeMillis();
658
659        final boolean success =
660                result("findMinMat" + matSize,
661                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
662                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
663                        javaResult, rsResult);
664        inputAllocation.destroy();
665        return success;
666    }
667
668    private boolean findMinMat2(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
669        return findMinMat(RS, seed, size, 2, Element.MATRIX_2X2(RS),
670                (Allocation input) -> s.reduce_findMinMat2(input).get());
671    }
672
673    private boolean findMinMat4(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
674        return findMinMat(RS, seed, size, 4, Element.MATRIX_4X4(RS),
675                (Allocation input) -> s.reduce_findMinMat4(input).get());
676    }
677
678    ///////////////////////////////////////////////////////////////////
679
680    private int fz(final int[] input) {
681        for (int i = 0; i < input.length; ++i)
682            if (input[i] == 0)
683                return i;
684        return -1;
685    }
686
687    private boolean fz_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
688        final int inputLen = size[0];
689        int[] input = createInputArrayInt(inputLen, seed + 0);
690        // just in case we got unlucky
691        input[(new Random(seed + 1)).nextInt(inputLen)] = 0;
692
693        final int rsResult = s.reduce_fz(input).get();
694
695        final boolean success = (input[rsResult] == 0);
696        Log.i(TAG,
697                "fz_array: input[" + rsResult + "] == " + input[rsResult] + ": " +
698                        (success ? "PASSED " + timing.string(size[0]) : "FAILED"));
699        return success;
700    }
701
702    private boolean fz(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
703        final int inputLen = size[0];
704        int[] inputArray = createInputArrayInt(inputLen, seed + 0);
705        // just in case we got unlucky
706        inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
707
708        final long javaTimeStart = java.lang.System.currentTimeMillis();
709        final int javaResult = fz(inputArray);
710        final long javaTimeEnd = java.lang.System.currentTimeMillis();
711
712        final long rsTimeStart = java.lang.System.currentTimeMillis();
713
714        Allocation inputAllocation = Allocation.createSized(RS, Element.I32(RS), inputArray.length);
715
716        final long copyTimeStart = java.lang.System.currentTimeMillis();
717
718        inputAllocation.copyFrom(inputArray);
719
720        final long kernelTimeStart = java.lang.System.currentTimeMillis();
721        final int rsResult = s.reduce_fz(inputAllocation).get();
722        final long rsTimeEnd = java.lang.System.currentTimeMillis();
723
724        final boolean success = (inputArray[rsResult] == 0);
725        String status = (success ? "PASSED" : "FAILED");
726        if (success)
727            status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
728                    copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
729        Log.i(TAG,
730                "fz: java input[" + javaResult + "] == " + inputArray[javaResult] +
731                        ", rs input[" + rsResult + "] == " + inputArray[javaResult] + ": " + status);
732        inputAllocation.destroy();
733        return success;
734    }
735
736    ///////////////////////////////////////////////////////////////////
737
738    private boolean fz2(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
739        final int dimX = size[0], dimY = size[1];
740        final int inputLen = dimX * dimY;
741
742        int[] inputArray = createInputArrayInt(inputLen, seed + 0);
743        // just in case we got unlucky
744        inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
745
746        final long javaTimeStart = java.lang.System.currentTimeMillis();
747        final int javaResultLinear = fz(inputArray);
748        final long javaTimeEnd = java.lang.System.currentTimeMillis();
749
750        final Int2 javaResult = new Int2(javaResultLinear % dimX, javaResultLinear / dimX);
751        final int javaCellVal = inputArray[javaResult.x + dimX * javaResult.y];
752
753        final long rsTimeStart = java.lang.System.currentTimeMillis();
754
755        Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
756        typeBuilder.setX(dimX).setY(dimY);
757        Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
758
759        final long copyTimeStart = java.lang.System.currentTimeMillis();
760
761        inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
762
763        final long kernelTimeStart = java.lang.System.currentTimeMillis();
764        final Int2 rsResult = s.reduce_fz2(inputAllocation).get();
765        final long rsTimeEnd = java.lang.System.currentTimeMillis();
766
767        final int rsCellVal = inputArray[rsResult.x + dimX * rsResult.y];
768        final boolean success = (rsCellVal == 0);
769        String status = (success ? "PASSED" : "FAILED");
770        if (success)
771            status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
772                    copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
773        Log.i(TAG,
774                "fz2: java input[" + javaResult.x + ", " + javaResult.y + "] == " + javaCellVal +
775                        ", rs input[" + rsResult.x + ", " + rsResult.y + "] == " + rsCellVal + ": " + status);
776        inputAllocation.destroy();
777        return success;
778    }
779
780    ///////////////////////////////////////////////////////////////////
781
782    private boolean fz3(RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
783        final int dimX = size[0], dimY = size[1], dimZ = size[2];
784        final int inputLen = dimX * dimY * dimZ;
785
786        int[] inputArray = createInputArrayInt(inputLen, seed + 0);
787        // just in case we got unlucky
788        inputArray[(new Random(seed + 1)).nextInt(inputLen)] = 0;
789
790        final long javaTimeStart = java.lang.System.currentTimeMillis();
791        final int javaResultLinear = fz(inputArray);
792        final long javaTimeEnd = java.lang.System.currentTimeMillis();
793
794        final Int3 javaResult = new Int3(
795                javaResultLinear % dimX,
796                (javaResultLinear / dimX) % dimY,
797                javaResultLinear / (dimX * dimY));
798        final int javaCellVal = inputArray[javaResult.x + dimX * javaResult.y + dimX * dimY * javaResult.z];
799
800        final long rsTimeStart = java.lang.System.currentTimeMillis();
801
802        Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS));
803        typeBuilder.setX(dimX).setY(dimY).setZ(dimZ);
804        Allocation inputAllocation = Allocation.createTyped(RS, typeBuilder.create());
805
806        final long copyTimeStart = java.lang.System.currentTimeMillis();
807
808        inputAllocation.copy3DRangeFrom(0, 0, 0, dimX, dimY, dimZ, inputArray);
809
810        final long kernelTimeStart = java.lang.System.currentTimeMillis();
811        final Int3 rsResult = s.reduce_fz3(inputAllocation).get();
812        final long rsTimeEnd = java.lang.System.currentTimeMillis();
813
814        final int rsCellVal = inputArray[rsResult.x + dimX * rsResult.y + dimX * dimY * rsResult.z];
815        final boolean success = (rsCellVal == 0);
816        String status = (success ? "PASSED" : "FAILED");
817        if (success)
818            status += " " + timing.string(javaTimeStart, javaTimeEnd, rsTimeStart,
819                    copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation);
820        Log.i(TAG,
821                "fz3: java input[" + javaResult.x + ", " + javaResult.y + ", " + javaResult.z + "] == " + javaCellVal +
822                        ", rs input[" + rsResult.x + ", " + rsResult.y + ", " + rsResult.z + "] == " + rsCellVal + ": " + status);
823        inputAllocation.destroy();
824        return success;
825    }
826
827    ///////////////////////////////////////////////////////////////////
828
829    private static final int histogramBucketCount = 256;
830
831    private long[] histogram(RenderScript RS, final byte[] inputArray) {
832        Allocation inputAllocation = Allocation.createSized(RS, Element.U8(RS), inputArray.length);
833        inputAllocation.copyFrom(inputArray);
834
835        Allocation outputAllocation = Allocation.createSized(RS, Element.U32(RS), histogramBucketCount);
836
837        ScriptIntrinsicHistogram scriptHsg = ScriptIntrinsicHistogram.create(RS, Element.U8(RS));
838        scriptHsg.setOutput(outputAllocation);
839        scriptHsg.forEach(inputAllocation);
840
841        int[] outputArrayMistyped = new int[histogramBucketCount];
842        outputAllocation.copyTo(outputArrayMistyped);
843
844        long[] outputArray = new long[histogramBucketCount];
845        for (int i = 0; i < histogramBucketCount; ++i)
846            outputArray[i] = outputArrayMistyped[i] & (long) 0xffffffff;
847
848        inputAllocation.destroy();
849        outputAllocation.destroy();
850
851        scriptHsg.destroy();
852        return outputArray;
853    }
854
855    private boolean histogram_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
856        final byte[] inputArray = createInputArrayByte(size[0], seed);
857
858        final long[] javaResult = histogram(RS, inputArray);
859        assertEquals("javaResult length", histogramBucketCount, javaResult.length);
860        final long[] rsResult = s.reduce_histogram(inputArray).get();
861        assertEquals("rsResult length", histogramBucketCount, rsResult.length);
862
863        return result("histogram_array", new timing(size[0]), javaResult, rsResult);
864    }
865
866    private boolean histogram(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
867        final byte[] inputArray = createInputArrayByte(size[0], seed);
868
869        final long javaTimeStart = java.lang.System.currentTimeMillis();
870        final long[] javaResult = histogram(RS, inputArray);
871        final long javaTimeEnd = java.lang.System.currentTimeMillis();
872        assertEquals("javaResult length", histogramBucketCount, javaResult.length);
873
874        final long rsTimeStart = java.lang.System.currentTimeMillis();
875
876        Allocation inputAllocation = Allocation.createSized(RS, Element.U8(RS), inputArray.length);
877
878        final long copyTimeStart = java.lang.System.currentTimeMillis();
879
880        inputAllocation.copyFrom(inputArray);
881
882        final long kernelTimeStart = java.lang.System.currentTimeMillis();
883        final long[] rsResult = s.reduce_histogram(inputAllocation).get();
884        final long rsTimeEnd = java.lang.System.currentTimeMillis();
885        assertEquals("rsResult length", histogramBucketCount, rsResult.length);
886
887        // NOTE: The "java time" is actually for the RenderScript histogram intrinsic
888        final boolean success =
889                result("histogram",
890                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart,
891                                copyTimeStart, kernelTimeStart, rsTimeEnd, inputAllocation),
892                        javaResult, rsResult);
893        inputAllocation.destroy();
894        return success;
895    }
896
897    //-----------------------------------------------------------------
898
899    private boolean patternRedundantGet(RenderScript RS, ScriptC_reduce s) {
900        // Ensure that get() can be called multiple times on the same
901        // result, and returns the same object each time.
902
903        boolean pass = true;
904
905        final int inputLength = 1 << 18;
906        final byte[] inputArray = createInputArrayByte(inputLength, 789);
907
908        final long[] javaResult = histogram(RS, inputArray);
909        assertEquals("javaResult length", histogramBucketCount, javaResult.length);
910
911        final ScriptC_reduce.resultArray256_uint rsResultFuture = s.reduce_histogram(inputArray);
912        final long[] rsResult1 = rsResultFuture.get();
913        assertEquals("rsResult1 length", histogramBucketCount, rsResult1.length);
914        pass &= result("patternRedundantGet (1)", new timing(inputLength), javaResult, rsResult1);
915
916        final long[] rsResult2 = rsResultFuture.get();
917        pass &= result("patternRedundantGet (2)", new timing(inputLength), javaResult, rsResult2);
918
919        final boolean success = (rsResult1 == rsResult2);
920        Log.i(TAG, "patternRedundantGet (object equality): " + (success ? "PASSED" : "FAILED"));
921        pass &= success;
922
923        return pass;
924    }
925
926    //-----------------------------------------------------------------
927
928    private Int2 mode(RenderScript RS, final byte[] inputArray) {
929        long[] hsg = histogram(RS, inputArray);
930
931        int modeIdx = 0;
932        for (int i = 1; i < hsg.length; ++i)
933            if (hsg[i] > hsg[modeIdx]) modeIdx = i;
934        return new Int2(modeIdx, (int) hsg[modeIdx]);
935    }
936
937    private boolean mode_array(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
938        final byte[] inputArray = createInputArrayByte(size[0], seed);
939
940        final Int2 javaResult = mode(RS, inputArray);
941        final Int2 rsResult = s.reduce_mode(inputArray).get();
942
943        return result("mode", new timing(size[0]), javaResult, rsResult);
944    }
945
946    ///////////////////////////////////////////////////////////////////
947
948    private long sumgcd(final int in1[], final int in2[]) {
949        assertEquals("sumgcd input lengths", in1.length, in2.length);
950
951        long sum = 0;
952        for (int i = 0; i < in1.length; ++i) {
953            int a = in1[i], b = in2[i];
954
955            while (b != 0) {
956                final int aNew = b;
957                final int bNew = a % b;
958
959                a = aNew;
960                b = bNew;
961            }
962
963            sum += a;
964        }
965        return sum;
966    }
967
968    private boolean sumgcd(RenderScript RS, ScriptC_reduce s, int seed, int size[]) {
969        final int len = size[0];
970
971        final int[] inputArrayA = createInputArrayInt(len, seed + 0);
972        final int[] inputArrayB = createInputArrayInt(len, seed + 1);
973
974        final long javaTimeStart = java.lang.System.currentTimeMillis();
975        final long javaResult = sumgcd(inputArrayA, inputArrayB);
976        final long javaTimeEnd = java.lang.System.currentTimeMillis();
977
978        final long rsTimeStart = java.lang.System.currentTimeMillis();
979
980        Allocation inputAllocationA = Allocation.createSized(RS, Element.I32(RS), len);
981        Allocation inputAllocationB = Allocation.createSized(RS, Element.I32(RS), len);
982
983        final long copyTimeStart = java.lang.System.currentTimeMillis();
984
985        inputAllocationA.copyFrom(inputArrayA);
986        inputAllocationB.copyFrom(inputArrayB);
987
988        final long kernelTimeStart = java.lang.System.currentTimeMillis();
989        final long rsResult = s.reduce_sumgcd(inputAllocationA, inputAllocationB).get();
990        final long rsTimeEnd = java.lang.System.currentTimeMillis();
991
992        final boolean success =
993                result("sumgcd",
994                        new timing(javaTimeStart, javaTimeEnd, rsTimeStart, copyTimeStart, kernelTimeStart, rsTimeEnd,
995                                inputAllocationA, inputAllocationB),
996                        javaResult, rsResult);
997        inputAllocationA.destroy();
998        inputAllocationB.destroy();
999        return success;
1000    }
1001
1002    ///////////////////////////////////////////////////////////////////
1003
1004    // Return an array of sparse integer values from 0 to maxVal inclusive.
1005    // The array consists of all values k*sparseness (k a nonnegative integer)
1006    // that are less than maxVal, and maxVal itself.  For example, if maxVal
1007    // is 20 and sparseness is 6, then the result is { 0, 6, 12, 18, 20 };
1008    // and if maxVal is 20 and sparseness is 10, then the result is { 0, 10, 20 }.
1009    //
1010    // The elements of the array are sorted in increasing order.
1011    //
1012    // maxVal     -- must be nonnegative
1013    // sparseness -- must be positive
1014    private static int[] computeSizePoints(int maxVal, int sparseness) {
1015        assertTrue((maxVal >= 0) && (sparseness > 0));
1016
1017        final boolean maxValIsExtra = ((maxVal % sparseness) != 0);
1018        int[] result = new int[1 + maxVal / sparseness + (maxValIsExtra ? 1 : 0)];
1019
1020        for (int i = 0; i * sparseness <= maxVal; ++i)
1021            result[i] = i * sparseness;
1022        if (maxValIsExtra)
1023            result[result.length - 1] = maxVal;
1024
1025        return result;
1026    }
1027
1028    private static final int maxSeedsPerTest = 10;
1029
1030    static interface Test {
1031        // A test execution is characterized by two properties: A seed
1032        // and a size.
1033        //
1034        // The seed is used for generating pseudorandom input data.
1035        // Ideally, we use different seeds for different tests and for
1036        // different executions of the same test at different sizes.
1037        // A test with multiple blocks of input data (i.e., for a
1038        // reduction with multiple inputs) may want multiple seeds; it
1039        // may use the seeds seed..seed+maxSeedsPerTest-1.
1040        //
1041        // The size indicates the amount of input data.  It is the number
1042        // of cells in a particular dimension of the iteration space.
1043        boolean run(RenderScript RS, ScriptC_reduce s, int seed, int[] size);
1044    }
1045
1046    static class TestDescription {
1047        public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize,
1048                               int myLog2MaxSize, int mySparseness) {
1049            testName = myTestName;
1050            test = myTest;
1051            seed = mySeed;
1052            defSize = myDefSize;
1053            log2MaxSize = myLog2MaxSize;
1054            sparseness = mySparseness;
1055        }
1056
1057        public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize, int myLog2MaxSize) {
1058            testName = myTestName;
1059            test = myTest;
1060            seed = mySeed;
1061            defSize = myDefSize;
1062            log2MaxSize = myLog2MaxSize;
1063            sparseness = 1;
1064        }
1065
1066        public TestDescription(String myTestName, Test myTest, int mySeed, int[] myDefSize) {
1067            testName = myTestName;
1068            test = myTest;
1069            seed = mySeed;
1070            defSize = myDefSize;
1071            log2MaxSize = -1;
1072            sparseness = 1;
1073        }
1074
1075        public final String testName;
1076
1077        public final Test test;
1078
1079        // When executing the test, scale this up by maxSeedsPerTest.
1080        public final int seed;
1081
1082        // If we're only going to run the test once, what size should
1083        // we use?  The length of the array is the number of
1084        // dimensions of the input data.
1085        public final int[] defSize;
1086
1087        // If we're going to run the test over a range of sizes, what
1088        // is the maximum size to use?  (This constrains the number of
1089        // cells of the input data, not the number of cells ALONG A
1090        // PARTICULAR DIMENSION of the input data.)
1091        public final int log2MaxSize;
1092
1093        // If we're going to run the test "exhaustively" over a range
1094        // of sizes, what is the size of a step through the range?
1095        //
1096        // For 1D, must be 1.
1097        public final int sparseness;
1098    }
1099
1100    private boolean run(TestDescription td, RenderScript RS, ScriptC_reduce s, int seed, int[] size) {
1101        String arrayContent = "";
1102        for (int i = 0; i < size.length; ++i) {
1103            if (i != 0)
1104                arrayContent += ", ";
1105            arrayContent += size[i];
1106        }
1107        Log.i(TAG, "Running " + td.testName + "(seed = " + seed + ", size[] = {" + arrayContent + "})");
1108        return td.test.run(RS, s, seed, size);
1109    }
1110
1111    private final TestDescription[] correctnessTests = {
1112            // alloc and array variants of the same test will use the same
1113            // seed, in case results need to be compared.
1114
1115            new TestDescription("addint1D", this::addint1D, 0, new int[]{100000}, 20),
1116            new TestDescription("addint1D_array", this::addint1D_array, 0, new int[]{100000}, 20),
1117            new TestDescription("addint2D", this::addint2D, 1, new int[]{450, 225}, 20, 5),
1118            new TestDescription("addint3D", this::addint3D, 2, new int[]{37, 48, 49}, 20, 7),
1119
1120            // Bool and NaN variants of the same test will use the same
1121            // seed, in case results need to be compared.
1122            new TestDescription("findMinAbsBool", this::findMinAbsBool, 3, new int[]{100000}, 20),
1123            new TestDescription("findMinAbsNaN", this::findMinAbsNaN, 3, new int[]{100000}, 20),
1124            new TestDescription("findMinAbsBoolInf", this::findMinAbsBoolInf, 4, new int[]{100000}, 20),
1125            new TestDescription("findMinAbsNaNInf", this::findMinAbsNaNInf, 4, new int[]{100000}, 20),
1126
1127            new TestDescription("findMinAndMax", this::findMinAndMax, 5, new int[]{100000}, 20),
1128            new TestDescription("findMinAndMax_array", this::findMinAndMax_array, 5, new int[]{100000}, 20),
1129            new TestDescription("findMinMat2", this::findMinMat2, 6, new int[]{25000}, 17),
1130            new TestDescription("findMinMat4", this::findMinMat4, 7, new int[]{10000}, 15),
1131            new TestDescription("fz", this::fz, 8, new int[]{100000}, 20),
1132            new TestDescription("fz_array", this::fz_array, 8, new int[]{100000}, 20),
1133            new TestDescription("fz2", this::fz2, 9, new int[]{225, 450}, 20, 5),
1134            new TestDescription("fz3", this::fz3, 10, new int[]{59, 48, 37}, 20, 7),
1135            new TestDescription("histogram", this::histogram, 11, new int[]{100000}, 20),
1136            new TestDescription("histogram_array", this::histogram_array, 11, new int[]{100000}, 20),
1137            // might want to add: new TestDescription("mode", this::mode, 12, new int[]{100000}, 20),
1138            new TestDescription("mode_array", this::mode_array, 12, new int[]{100000}, 20),
1139            new TestDescription("sumgcd", this::sumgcd, 13, new int[]{1 << 16}, 20)
1140    };
1141
1142    private boolean runCorrectnessQuick(RenderScript RS, ScriptC_reduce s) {
1143        boolean pass = true;
1144
1145        for (TestDescription td : correctnessTests) {
1146            pass &= run(td, RS, s, maxSeedsPerTest * td.seed, td.defSize);
1147        }
1148
1149        return pass;
1150    }
1151
1152    // NOTE: Each test execution gets maxSeedsPerTest, and there are
1153    // up to 3 + 5*log2MaxSize test executions in the full (as opposed
1154    // to quick) correctness run of a particular test description, and
1155    // we need an additional seed for pseudorandom size generation.
1156    // Assuming log2MaxSize does not exceed 32, then it should be
1157    // sufficient to reserve 1 + (3+5*32)*maxSeedsPerTest seeds per
1158    // TestDescription.
1159    //
1160    // See runCorrectness1D().
1161    private static final int seedsPerTestDescriptionCorrectness1D = 1 + (3 + 5 * 32) * maxSeedsPerTest;
1162
1163    // NOTE: Each test execution gets maxSeedsPerTest, and there are
1164    // about 11*((log2MaxSize+1)**2) test executions in the full (as
1165    // opposed to quick) correctness run of a particular test
1166    // description, and we need a seed for pseudorandom size
1167    // generation.  Assuming log2MaxSize does not exceed 32, then it
1168    // should be sufficient to reserve 1 + 11*1089*maxSeedsPerTest
1169    // seeds per TestDescription.
1170    //
1171    // See runCorrectness2D().
1172    private static final int seedsPerTestDescriptionCorrectness2D = 1 + (11 * 1089) * maxSeedsPerTest;
1173
1174    // NOTE: Each test execution gets maxSeedsPerTest, and there are
1175    // about 27*((log2MaxSize+1)**3) + 6*((log2MaxSize+1)**2) test
1176    // executions in the full (as opposed to quick) correctness run of
1177    // a particular test description, and we need a seed for (c).
1178    // Assuming log2MaxSize does not exceed 32, then it should
1179    // be sufficient to reserve 1 + (27*(33**3) + 6*(33**2))*maxSeedsPerTest
1180    // seeds per TestDescription, which can be simplified upwards to
1181    // 1 + (28*(33**3))*maxSeedsPerTest seeds per TestDescription.
1182    private static final int seedsPerTestDescriptionCorrectness3D = 1 + (28 * 35937) * maxSeedsPerTest;
1183
1184    // Each test execution gets a certain number of seeds, and a full
1185    // (as opposed to quick) correctness run of a particular
1186    // TestDescription consists of some number of executions (each of
1187    // which needs up to maxSeedsPerTest) and may require some
1188    // additional seeds.
1189    private static final int seedsPerTestDescriptionCorrectness =
1190            Math.max(seedsPerTestDescriptionCorrectness1D,
1191                    Math.max(seedsPerTestDescriptionCorrectness2D,
1192                            seedsPerTestDescriptionCorrectness3D));
1193
1194    private boolean runCorrectness(RenderScript RS, ScriptC_reduce s) {
1195        boolean pass = true;
1196
1197        for (TestDescription td : correctnessTests) {
1198            switch (td.defSize.length) {
1199                case 1:
1200                    pass &= runCorrectness1D(td, RS, s);
1201                    break;
1202                case 2:
1203                    pass &= runCorrectness2D(td, RS, s);
1204                    break;
1205                case 3:
1206                    pass &= runCorrectness3D(td, RS, s);
1207                    break;
1208                default:
1209                    assertTrue("unexpected defSize.length " + td.defSize.length, false);
1210                    pass &= false;
1211                    break;
1212            }
1213        }
1214
1215        return pass;
1216    }
1217
1218    private boolean runCorrectness1D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
1219        assertEquals(1, td.sparseness);
1220        final int log2MaxSize = td.log2MaxSize;
1221        assertTrue(log2MaxSize >= 0);
1222
1223        boolean pass = true;
1224
1225        // We will execute the test with the following sizes:
1226        // (a) Each power of 2 from zero (2**0) up to log2MaxSize (2**log2MaxSize)
1227        // (b) Each size from (a) +/-1
1228        // (c) 2 random sizes between each pair of adjacent points in (a)
1229        int[] testSizes = new int[
1230            /* a */ (1 + log2MaxSize) +
1231            /* b */ 2 * (1 + log2MaxSize) +
1232            /* c */ 2 * log2MaxSize];
1233        // See seedsPerTestDescriptionCorrectness1D
1234
1235        final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
1236
1237        int nextTestIdx = 0;
1238
1239        // Fill in (a) and (b)
1240        for (int i = 0; i <= log2MaxSize; ++i) {
1241            final int pwrOf2 = 1 << i;
1242            testSizes[nextTestIdx++] = pwrOf2;      /* a */
1243            testSizes[nextTestIdx++] = pwrOf2 - 1;  /* b */
1244            testSizes[nextTestIdx++] = pwrOf2 + 1;  /* b */
1245        }
1246
1247        // Fill in (c)
1248        Random r = new Random(seedForPickingTestSizes);
1249        for (int i = 0; i < log2MaxSize; ++i) {
1250            final int lo = (1 << i) + 1;
1251            final int hi = 1 << (i + 1);
1252
1253            if (lo < hi) {
1254                for (int j = 0; j < 2; ++j) {
1255                    testSizes[nextTestIdx++] = r.nextInt(hi - lo) + lo;
1256                }
1257            }
1258        }
1259
1260        Arrays.sort(testSizes);
1261
1262        int[] lastTestSizeArg = new int[]{-1};
1263        for (int i = 0; i < testSizes.length; ++i) {
1264            if ((testSizes[i] > 0) && (testSizes[i] != lastTestSizeArg[0])) {
1265                lastTestSizeArg[0] = testSizes[i];
1266                final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
1267                pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
1268            }
1269        }
1270
1271        return pass;
1272    }
1273
1274    private boolean runCorrectness2D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
1275        final int log2MaxSize = td.log2MaxSize, maxSize = 1 << log2MaxSize, sparseness = td.sparseness;
1276        assertTrue((log2MaxSize >= 0) && (sparseness >= 1));
1277
1278        boolean pass = true;
1279
1280        final int[] sizePoints = computeSizePoints(log2MaxSize, sparseness);
1281
1282        // We will execute the test with the following sizes:
1283        // (a) Each dimension at a power of 2 from sizePoints[]
1284        ///    such that the sum of the exponents does not exceed
1285        //     log2MaxSize
1286        // (b) Each size from (a) with one or both dimensions +/-1,
1287        //     except where this would exceed 2**log2MaxSize
1288        // (c) Approximately 2*(sizePoints.length**2) random sizes
1289        ArrayList<int[]> testSizesList = new ArrayList<int[]>();
1290        // See seedsPerTestDescriptionCorrectness2D
1291
1292        final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
1293
1294        // Fill in (a) and (b)
1295        for (int i : sizePoints) {
1296            final int iPwrOf2 = 1 << i;
1297            for (int iDelta = -1; iDelta <= 1; ++iDelta) {
1298                final int iSize = iPwrOf2 + iDelta;
1299                for (int j : sizePoints) {
1300                    final int jPwrOf2 = 1 << j;
1301                    for (int jDelta = -1; jDelta <= 1; ++jDelta) {
1302                        final int jSize = jPwrOf2 + jDelta;
1303                        if ((long) iSize * (long) jSize <= maxSize)
1304                            testSizesList.add(new int[]{iSize, jSize});
1305                    }
1306                }
1307            }
1308        }
1309
1310        // Fill in (c)
1311        Random r = new Random(seedForPickingTestSizes);
1312        for (int i : sizePoints) {
1313            for (int j : sizePoints) {
1314                final int size0 = 1 + r.nextInt(1 << i);
1315                final int size1 = 1 + r.nextInt(maxSize / size0);
1316
1317                testSizesList.add(new int[]{size0, size1});
1318                testSizesList.add(new int[]{size1, size0});
1319            }
1320        }
1321
1322        int[][] testSizes = testSizesList.toArray(new int[0][]);
1323        Arrays.sort(testSizes,
1324                (a, b) -> {
1325                    final int comp0 = ((Integer) a[0]).compareTo(b[0]);
1326                    return (comp0 != 0 ? comp0 : ((Integer) a[1]).compareTo(b[1]));
1327                });
1328
1329        int[] lastTestSizeArg = null;
1330        for (int i = 0; i < testSizes.length; ++i) {
1331            if ((testSizes[i][0] <= 0) || (testSizes[i][1] <= 0))
1332                continue;
1333            if ((lastTestSizeArg != null) &&
1334                    (testSizes[i][0] == lastTestSizeArg[0]) &&
1335                    (testSizes[i][1] == lastTestSizeArg[1]))
1336                continue;
1337            lastTestSizeArg = testSizes[i];
1338            final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
1339            pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
1340        }
1341
1342        return pass;
1343    }
1344
1345    private boolean runCorrectness3D(TestDescription td, RenderScript RS, ScriptC_reduce s) {
1346        final int log2MaxSize = td.log2MaxSize, maxSize = 1 << log2MaxSize, sparseness = td.sparseness;
1347        assertTrue((log2MaxSize >= 0) && (sparseness >= 1));
1348
1349        boolean pass = true;
1350
1351        final int[] sizePoints = computeSizePoints(log2MaxSize, sparseness);
1352
1353        // We will execute the test with the following sizes:
1354        // (a) Each dimension at a power of 2 from sizePoints[]
1355        ///    such that the sum of the exponents does not exceed
1356        //     log2MaxSize
1357        // (b) Each size from (a) with one or both dimensions +/-1,
1358        //     except where this would exceed 2**log2MaxSize
1359        // (c) Approximately 6*(sizePoints.length**2) random sizes
1360        ArrayList<int[]> testSizesList = new ArrayList<int[]>();
1361        // See seedsPerTestDescriptionCorrectness3D
1362
1363        final int seedForPickingTestSizes = td.seed * seedsPerTestDescriptionCorrectness;
1364
1365        // Fill in (a) and (b)
1366        for (int i : sizePoints) {
1367            final int iPwrOf2 = 1 << i;
1368            for (int iDelta = -1; iDelta <= 1; ++iDelta) {
1369                final int iSize = iPwrOf2 + iDelta;
1370                for (int j : sizePoints) {
1371                    final int jPwrOf2 = 1 << j;
1372                    for (int jDelta = -1; jDelta <= 1; ++jDelta) {
1373                        final int jSize = jPwrOf2 + jDelta;
1374                        for (int k : sizePoints) {
1375                            final int kPwrOf2 = 1 << k;
1376                            for (int kDelta = -1; kDelta <= 1; ++kDelta) {
1377                                final int kSize = kPwrOf2 + kDelta;
1378                                if ((long) iSize * (long) jSize * (long) kSize <= maxSize)
1379                                    testSizesList.add(new int[]{iSize, jSize, kSize});
1380                            }
1381                        }
1382                    }
1383                }
1384            }
1385        }
1386
1387        // Fill in (c)
1388        Random r = new Random(seedForPickingTestSizes);
1389        for (int i : sizePoints) {
1390            for (int j : sizePoints) {
1391                final int size0 = 1 + r.nextInt(1 << i);
1392                final int size1 = 1 + r.nextInt(Math.min(1 << j, maxSize / size0));
1393                final int size2 = 1 + r.nextInt(maxSize / (size0 * size1));
1394
1395                testSizesList.add(new int[]{size0, size1, size2});
1396                testSizesList.add(new int[]{size0, size2, size1});
1397                testSizesList.add(new int[]{size1, size0, size2});
1398                testSizesList.add(new int[]{size1, size2, size0});
1399                testSizesList.add(new int[]{size2, size0, size1});
1400                testSizesList.add(new int[]{size2, size1, size0});
1401            }
1402        }
1403
1404        int[][] testSizes = testSizesList.toArray(new int[0][]);
1405        Arrays.sort(testSizes,
1406                (a, b) -> {
1407                    int comp = ((Integer) a[0]).compareTo(b[0]);
1408                    if (comp == 0)
1409                        comp = ((Integer) a[1]).compareTo(b[1]);
1410                    if (comp == 0)
1411                        comp = ((Integer) a[2]).compareTo(b[2]);
1412                    return comp;
1413                });
1414
1415        int[] lastTestSizeArg = null;
1416        for (int i = 0; i < testSizes.length; ++i) {
1417            if ((testSizes[i][0] <= 0) || (testSizes[i][1] <= 0) || (testSizes[i][2] <= 0))
1418                continue;
1419            if ((lastTestSizeArg != null) &&
1420                    (testSizes[i][0] == lastTestSizeArg[0]) &&
1421                    (testSizes[i][1] == lastTestSizeArg[1]) &&
1422                    (testSizes[i][2] == lastTestSizeArg[2]))
1423                continue;
1424
1425            // Apply Z-dimension limiting.
1426            //
1427            // The Z dimension is always handled specially by GPU
1428            // drivers, and a high value for this dimension can have
1429            // serious performance implications.  For example, Cuda
1430            // and OpenCL encourage Z to be the smallest dimension.
1431            if (testSizes[i][2] > 1024)
1432                continue;
1433
1434            lastTestSizeArg = testSizes[i];
1435            final int seedForTestExecution = seedForPickingTestSizes + 1 + i * maxSeedsPerTest;
1436            pass &= run(td, RS, s, seedForTestExecution, lastTestSizeArg);
1437        }
1438
1439        return pass;
1440    }
1441
1442    private final TestDescription[] performanceTests = {
1443            new TestDescription("addint1D", this::addint1D, 0, new int[]{100000 << 10}),
1444            new TestDescription("addint2D", this::addint2D, 1, new int[]{450 << 5, 225 << 5}),
1445            new TestDescription("addint3D", this::addint3D, 2, new int[]{37 << 3, 48 << 3, 49 << 3}),
1446            new TestDescription("findMinAndMax", this::findMinAndMax, 3, new int[]{100000 << 9}),
1447            new TestDescription("fz", this::fz, 4, new int[]{100000 << 10}),
1448            new TestDescription("fz2", this::fz2, 5, new int[]{225 << 5, 450 << 5}),
1449            new TestDescription("fz3", this::fz3, 6, new int[]{59 << 3, 48 << 3, 37 << 3}),
1450            new TestDescription("histogram", this::histogram, 7, new int[]{100000 << 10}),
1451            // might want to add: new TestDescription("mode", this::mode, 8, new int[]{100000}),
1452            new TestDescription("sumgcd", this::sumgcd, 9, new int[]{1 << 21})
1453    };
1454
1455    private boolean runPerformanceQuick(RenderScript RS, ScriptC_reduce s) {
1456        boolean pass = true;
1457
1458        for (TestDescription td : performanceTests) {
1459            pass &= run(td, RS, s, maxSeedsPerTest * td.seed, td.defSize);
1460        }
1461
1462        return pass;
1463    }
1464
1465    private boolean runCorrectnessPatterns(RenderScript RS, ScriptC_reduce s) {
1466        // Test some very specific usage patterns.
1467        boolean pass = true;
1468
1469        pass &= patternDuplicateAnonymousResult(RS, s);
1470        pass &= patternFindMinAndMaxInf(RS, s);
1471        pass &= patternInterleavedReduce(RS, s);
1472        pass &= patternRedundantGet(RS, s);
1473
1474        return pass;
1475    }
1476
1477    public void run() {
1478        RenderScript pRS = createRenderScript(false);
1479        ScriptC_reduce s = new ScriptC_reduce(pRS);
1480        s.set_negInf(Float.NEGATIVE_INFINITY);
1481        s.set_posInf(Float.POSITIVE_INFINITY);
1482
1483        boolean pass = true;
1484
1485        pass &= runCorrectnessPatterns(pRS, s);
1486        pass &= runCorrectnessQuick(pRS, s);
1487        pass &= runCorrectness(pRS, s);
1488        // pass &= runPerformanceQuick(pRS, s);
1489
1490        pRS.finish();
1491        s.destroy();
1492        pRS.destroy();
1493
1494        Log.i(TAG, pass ? "PASSED" : "FAILED");
1495        if (pass)
1496            passTest();
1497        else
1498            failTest();
1499    }
1500}
1501
1502// TODO: Add machinery for easily running fuller (i.e., non-sparse) testing.
1503