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