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