1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "shared.rsh"
18
19// Has the same kernels as reduce_backward.rs, plus some others.
20//
21// This test case places the pragmas before the functions (forward
22// reference), and the other test case places the pragmas after the
23// functions (backward reference).
24
25float negInf, posInf;
26
27static bool IsNaN(float v) {
28  // a NaN (and only a NaN) compares unequal to everything
29  return v != v;
30}
31
32/////////////////////////////////////////////////////////////////////////
33
34#pragma rs reduce(addint) \
35  accumulator(aiAccum)
36
37static void aiAccum(int *accum, int val) { *accum += val; }
38
39/////////////////////////////////////////////////////////////////////////
40
41// These kernels find an input value of minimum absolute value.
42//
43// If the input domain consists of all non-NaN values (including
44// infinities), we cannot pick an initializer from the input domain,
45// because there are two different members of the domain with maximum
46// absolute value -- positive and negative infinity.  Instead, we need
47// to pick some other distinguished initializer, and explicitly check
48// for and handle an accumulator with this distinguished value.
49//
50// The two kernels represent the distinguished value differently.
51
52//.......................................................................
53
54// The kernel findMinAbsNaN uses an initializer from outside the input
55// domain that is nonetheless representable as a float -- NaN.
56
57#pragma rs reduce(findMinAbsNaN) \
58  initializer(fMinAbsNaNInit) accumulator(fMinAbsNaNAccumulator) combiner(fMinAbsNaNCombiner)
59
60static void fMinAbsNaNInit(float *accum) {
61  *accum = nan(0);
62}
63
64static void fMinAbsNaNAccumulator(float *accum, float val) {
65  if (IsNaN(*accum) || (fabs(val) < fabs(*accum)))
66    *accum = val;
67}
68
69static void fMinAbsNaNCombiner(float *accum, const float *other) {
70  if (!IsNaN(*other))
71    fMinAbsNaNAccumulator(accum, *other);
72}
73
74//.......................................................................
75
76// The kernel findMinAbsBool represents its accumulator as a struct
77// with two fields -- a bool field to indicate whether or not the
78// accumulator has the distinguished initial value, and a float field
79// for a non-initial value.
80
81typedef struct FindMinAbsBoolAccumType {
82  // set to true by initializer function;
83  // set to false by accumulator function
84  bool onlyInitialized;
85  // only valid when onlyInitialized is false
86  float val;
87} FindMinAbsBoolAccumType;
88
89#pragma rs reduce(findMinAbsBool) \
90  initializer(fMinAbsBoolInit) accumulator(fMinAbsBoolAccumulator) combiner(fMinAbsBoolCombiner) \
91  outconverter(fMinAbsBoolOut)
92
93static void fMinAbsBoolInit(FindMinAbsBoolAccumType *accum) {
94  accum->onlyInitialized = true;
95}
96
97static void fMinAbsBoolAccumulator(FindMinAbsBoolAccumType *accum, float val) {
98  if (accum->onlyInitialized || (fabs(val) < fabs(accum->val)))
99    accum->val = val;
100  accum->onlyInitialized = false;
101}
102
103static void fMinAbsBoolCombiner(FindMinAbsBoolAccumType *accum, const FindMinAbsBoolAccumType *other) {
104  if (!other->onlyInitialized)
105    fMinAbsBoolAccumulator(accum, other->val);
106}
107
108static void fMinAbsBoolOut(float *out, const FindMinAbsBoolAccumType *accum) {
109  *out = accum->val;
110}
111
112/////////////////////////////////////////////////////////////////////////
113
114#pragma rs reduce(findMinAndMax) \
115  initializer(fMMInit) accumulator(fMMAccumulator) \
116  combiner(fMMCombiner) outconverter(fMMOutConverter)
117
118typedef struct {
119  float val;
120  int idx;
121} IndexedVal;
122
123typedef struct {
124  IndexedVal min, max;
125} MinAndMax;
126
127static void fMMInit(MinAndMax *accum) {
128  accum->min.val = posInf;
129  accum->min.idx = -1;
130  accum->max.val = negInf;
131  accum->max.idx = -1;
132}
133
134static void fMMAccumulator(MinAndMax *accum, float in, int x) {
135  IndexedVal me;
136  me.val = in;
137  me.idx = x;
138
139  if (me.val <= accum->min.val)
140    accum->min = me;
141  if (me.val >= accum->max.val)
142    accum->max = me;
143}
144
145static void fMMCombiner(MinAndMax *accum,
146                        const MinAndMax *val) {
147  if ((accum->min.idx < 0) || (val->min.val < accum->min.val))
148    accum->min = val->min;
149  if ((accum->max.idx < 0) || (val->max.val > accum->max.val))
150    accum->max = val->max;
151}
152
153static void fMMOutConverter(int2 *result,
154                            const MinAndMax *val) {
155  result->x = val->min.idx;
156  result->y = val->max.idx;
157}
158
159/////////////////////////////////////////////////////////////////////////
160
161#pragma rs reduce(fz) \
162  initializer(fzInit) \
163  accumulator(fzAccum) combiner(fzCombine)
164
165static void fzInit(int *accumIdx) { *accumIdx = -1; }
166
167static void fzAccum(int *accumIdx,
168                    int inVal, int x /* special arg */) {
169  if (inVal==0) *accumIdx = x;
170}
171
172static void fzCombine(int *accumIdx, const int *accumIdx2) {
173  if (*accumIdx2 >= 0) *accumIdx = *accumIdx2;
174}
175
176/////////////////////////////////////////////////////////////////////////
177
178#pragma rs reduce(fz2) \
179  initializer(fz2Init) \
180  accumulator(fz2Accum) combiner(fz2Combine)
181
182static void fz2Init(int2 *accum) { accum->x = accum->y = -1; }
183
184static void fz2Accum(int2 *accum,
185                     int inVal,
186                     int x /* special arg */,
187                     int y /* special arg */) {
188  if (inVal==0) {
189    accum->x = x;
190    accum->y = y;
191  }
192}
193
194static void fz2Combine(int2 *accum, const int2 *accum2) {
195  if (accum2->x >= 0) *accum = *accum2;
196}
197
198/////////////////////////////////////////////////////////////////////////
199
200#pragma rs reduce(fz3) \
201  initializer(fz3Init) \
202  accumulator(fz3Accum) combiner(fz3Combine)
203
204static void fz3Init(int3 *accum) { accum->x = accum->y = accum->z = -1; }
205
206static void fz3Accum(int3 *accum,
207                     int inVal,
208                     int x /* special arg */,
209                     int y /* special arg */,
210                     int z /* special arg */) {
211  if (inVal==0) {
212    accum->x = x;
213    accum->y = y;
214    accum->z = z;
215  }
216}
217
218static void fz3Combine(int3 *accum, const int3 *accum2) {
219  if (accum2->x >= 0) *accum = *accum2;
220}
221
222/////////////////////////////////////////////////////////////////////////
223
224#pragma rs reduce(histogram) \
225  accumulator(hsgAccum) combiner(hsgCombine)
226
227#define BUCKETS 256
228typedef uint32_t Histogram[BUCKETS];
229
230static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; }
231
232static void hsgCombine(Histogram *accum, const Histogram *addend) {
233  for (int i = 0; i < BUCKETS; ++i)
234    (*accum)[i] += (*addend)[i];
235}
236
237#pragma rs reduce(mode) \
238  accumulator(hsgAccum) combiner(hsgCombine) \
239  outconverter(modeOutConvert)
240
241static void modeOutConvert(int2 *result, const Histogram *h) {
242  uint32_t mode = 0;
243  for (int i = 1; i < BUCKETS; ++i)
244    if ((*h)[i] > (*h)[mode]) mode = i;
245  result->x = mode;
246  result->y = (*h)[mode];
247}
248
249/////////////////////////////////////////////////////////////////////////
250
251#pragma rs reduce(sumgcd) accumulator(sgAccum) combiner(sgCombine)
252
253static int gcd(int a, int b) {
254  while (b != 0) {
255    const int aNew = b;
256    const int bNew = a % b;
257
258    a = aNew;
259    b = bNew;
260  }
261  return a;
262}
263
264static void sgAccum(long *accum, int a, int b) {
265  *accum += gcd(a, b);
266}
267
268static void sgCombine(long *accum, const long *other) { *accum += *other; }
269
270/////////////////////////////////////////////////////////////////////////
271
272// These two kernels have anonymous result types that are equivalent.
273// slang doesn't common them (i.e., each gets its own RSExportType);
274// so Java reflection must guard against this to avoid creating two
275// copies of the text that defines the reflected class resultArray4_int.
276
277#pragma rs reduce(sillySumIntoDecArray) accumulator(aiAccum) outconverter(outSillySumIntoDecArray)
278static void outSillySumIntoDecArray(int (*out)[4], const int *accumDatum) {
279  for (int i = 0; i < 4; ++i)
280    (*out)[i] = (*accumDatum)/(i+1);
281}
282
283#pragma rs reduce(sillySumIntoIncArray) accumulator(aiAccum) outconverter(outSillySumIntoIncArray)
284static void outSillySumIntoIncArray(int (*out)[4], const int *accumDatum) {
285  for (int i = 0; i < 4; ++i)
286    (*out)[i] = (*accumDatum)/(4-i);
287}
288
289/////////////////////////////////////////////////////////////////////////
290
291// finds min values (not their locations) from matrix input
292
293// tests matrix input and matrix accumulator
294
295// also tests calling conventions for two different composite types
296// rs_matrix2x2: 32-bit coerces this to an int array
297//               64-bit coerces this to float array
298// rs_matrix4x4: 64-bit passes this by reference
299
300//.......................................................................
301
302#pragma rs reduce(findMinMat2) \
303  initializer(fMinMat2Init) accumulator(fMinMat2Accumulator) \
304  outconverter(fMinMat2OutConverter)
305
306static void fMinMat2Init(rs_matrix2x2 *accum) {
307  for (int i = 0; i < 2; ++i)
308    for (int j = 0; j < 2; ++j)
309      rsMatrixSet(accum, i, j, posInf);
310}
311
312static void fMinMat2Accumulator(rs_matrix2x2 *accum, rs_matrix2x2 val) {
313  for (int i = 0; i < 2; ++i) {
314    for (int j = 0; j < 2; ++j) {
315      const float accumElt = rsMatrixGet(accum, i, j);
316      const float valElt = rsMatrixGet(&val, i, j);
317      if (valElt < accumElt)
318        rsMatrixSet(accum, i, j, valElt);
319    }
320  }
321}
322
323// reduction does not support matrix result, so use array instead
324static void fMinMat2OutConverter(float (*result)[4],  const rs_matrix2x2 *accum) {
325  for (int i = 0; i < 4; ++i)
326    (*result)[i] = accum->m[i];
327}
328
329//.......................................................................
330
331#pragma rs reduce(findMinMat4) \
332  initializer(fMinMat4Init) accumulator(fMinMat4Accumulator) \
333  outconverter(fMinMat4OutConverter)
334
335static void fMinMat4Init(rs_matrix4x4 *accum) {
336  for (int i = 0; i < 4; ++i)
337    for (int j = 0; j < 4; ++j)
338      rsMatrixSet(accum, i, j, posInf);
339}
340
341static void fMinMat4Accumulator(rs_matrix4x4 *accum, rs_matrix4x4 val) {
342  for (int i = 0; i < 4; ++i) {
343    for (int j = 0; j < 4; ++j) {
344      const float accumElt = rsMatrixGet(accum, i, j);
345      const float valElt = rsMatrixGet(&val, i, j);
346      if (valElt < accumElt)
347        rsMatrixSet(accum, i, j, valElt);
348    }
349  }
350}
351
352// reduction does not support matrix result, so use array instead
353static void fMinMat4OutConverter(float (*result)[16],  const rs_matrix4x4 *accum) {
354  for (int i = 0; i < 16; ++i)
355    (*result)[i] = accum->m[i];
356}
357