xref: /llvm-project/flang/unittests/Runtime/Reduction.cpp (revision fc51c7f0cc1abf1679100d71d103fe5d943f580b)
1 //===-- flang/unittests/Runtime/Reductions.cpp ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "flang/Runtime/reduction.h"
10 #include "gtest/gtest.h"
11 #include "tools.h"
12 #include "flang/Common/float128.h"
13 #include "flang/Runtime/allocatable.h"
14 #include "flang/Runtime/cpp-type.h"
15 #include "flang/Runtime/descriptor.h"
16 #include "flang/Runtime/reduce.h"
17 #include "flang/Runtime/type-code.h"
18 #include <cstdint>
19 #include <cstring>
20 #include <string>
21 #include <vector>
22 
23 using namespace Fortran::runtime;
24 using Fortran::common::TypeCategory;
25 
26 TEST(Reductions, Int4Ops) {
27   auto array{MakeArray<TypeCategory::Integer, 4>(
28       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
29   std::int32_t sum{RTNAME(SumInteger4)(*array, __FILE__, __LINE__)};
30   EXPECT_EQ(sum, 21) << sum;
31   std::int32_t all{RTNAME(IAll4)(*array, __FILE__, __LINE__)};
32   EXPECT_EQ(all, 0) << all;
33   std::int32_t any{RTNAME(IAny4)(*array, __FILE__, __LINE__)};
34   EXPECT_EQ(any, 7) << any;
35   std::int32_t eor{RTNAME(IParity4)(*array, __FILE__, __LINE__)};
36   EXPECT_EQ(eor, 7) << eor;
37 }
38 
39 TEST(Reductions, DimMaskProductInt4) {
40   std::vector<int> shape{2, 3};
41   auto array{MakeArray<TypeCategory::Integer, 4>(
42       shape, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
43   auto mask{MakeArray<TypeCategory::Logical, 1>(
44       shape, std::vector<bool>{true, false, false, true, true, true})};
45   StaticDescriptor<maxRank, true> statDesc;
46   Descriptor &prod{statDesc.descriptor()};
47   RTNAME(ProductDim)(prod, *array, 1, __FILE__, __LINE__, &*mask);
48   EXPECT_EQ(prod.rank(), 1);
49   EXPECT_EQ(prod.GetDimension(0).LowerBound(), 1);
50   EXPECT_EQ(prod.GetDimension(0).Extent(), 3);
51   EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(0), 1);
52   EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(1), 4);
53   EXPECT_EQ(*prod.ZeroBasedIndexedElement<std::int32_t>(2), 30);
54   EXPECT_EQ(RTNAME(SumInteger4)(prod, __FILE__, __LINE__), 35);
55   prod.Destroy();
56 }
57 
58 TEST(Reductions, DoubleMaxMinNorm2) {
59   std::vector<int> shape{3, 4, 2}; // rows, columns, planes
60   //   0  -3   6  -9     12 -15  18 -21
61   //  -1   4  -7  10    -13  16 -19  22
62   //   2  -5   8 -11     14 -17  20  22   <- note last two are equal to test
63   //   BACK=
64   std::vector<double> rawData{0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12,
65       -13, 14, -15, 16, -17, 18, -19, 20, -21, 22, 22};
66   auto array{MakeArray<TypeCategory::Real, 8>(shape, rawData)};
67   EXPECT_EQ(RTNAME(MaxvalReal8)(*array, __FILE__, __LINE__), 22.0);
68   EXPECT_EQ(RTNAME(MinvalReal8)(*array, __FILE__, __LINE__), -21.0);
69   double naiveNorm2{0};
70   for (auto x : rawData) {
71     naiveNorm2 += x * x;
72   }
73   naiveNorm2 = std::sqrt(naiveNorm2);
74   double norm2Error{
75       std::abs(naiveNorm2 - RTNAME(Norm2_8)(*array, __FILE__, __LINE__))};
76   EXPECT_LE(norm2Error, 0.000001 * naiveNorm2);
77   StaticDescriptor<2, true> statDesc;
78   Descriptor &loc{statDesc.descriptor()};
79   RTNAME(MaxlocReal8)
80   (loc, *array, /*KIND=*/8, __FILE__, __LINE__, /*MASK=*/nullptr,
81       /*BACK=*/false);
82   EXPECT_EQ(loc.rank(), 1);
83   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
84   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
85   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
86   EXPECT_EQ(
87       *array->Element<double>(loc.ZeroBasedIndexedElement<SubscriptValue>(0)),
88       22.0);
89   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(0), 2);
90   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(1), 4);
91   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(2), 2);
92   loc.Destroy();
93   RTNAME(MaxlocReal8)
94   (loc, *array, /*KIND=*/8, __FILE__, __LINE__, /*MASK=*/nullptr,
95       /*BACK=*/true);
96   EXPECT_EQ(loc.rank(), 1);
97   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
98   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
99   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
100   EXPECT_EQ(
101       *array->Element<double>(loc.ZeroBasedIndexedElement<SubscriptValue>(0)),
102       22.0);
103   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(0), 3);
104   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(1), 4);
105   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int64_t>(2), 2);
106   loc.Destroy();
107   RTNAME(MinlocDim)
108   (loc, *array, /*KIND=*/2, /*DIM=*/1, __FILE__, __LINE__, /*MASK=*/nullptr,
109       /*BACK=*/false);
110   EXPECT_EQ(loc.rank(), 2);
111   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 2}.raw()));
112   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
113   EXPECT_EQ(loc.GetDimension(0).Extent(), 4);
114   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
115   EXPECT_EQ(loc.GetDimension(1).Extent(), 2);
116   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(0), 2); // -1
117   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(1), 3); // -5
118   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(2), 2); // -2
119   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(3), 3); // -11
120   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(4), 2); // -13
121   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(5), 3); // -17
122   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(6), 2); // -19
123   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(7), 1); // -21
124   loc.Destroy();
125   auto mask{MakeArray<TypeCategory::Logical, 1>(shape,
126       std::vector<bool>{false, false, false, false, false, true, false, true,
127           false, false, true, true, true, false, false, true, false, true, true,
128           true, false, true, true, true})};
129   RTNAME(MaxlocDim)
130   (loc, *array, /*KIND=*/2, /*DIM=*/3, __FILE__, __LINE__, /*MASK=*/&*mask,
131       false);
132   EXPECT_EQ(loc.rank(), 2);
133   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 2}.raw()));
134   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
135   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
136   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
137   EXPECT_EQ(loc.GetDimension(1).Extent(), 4);
138   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(0), 2); // 12
139   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(1), 0);
140   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(2), 0);
141   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(3), 2); // -15
142   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(4), 0);
143   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(5), 1); // -5
144   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(6), 2); // 18
145   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(7), 1); // -7
146   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(8), 0);
147   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(9), 2); // -21
148   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(10), 2); // 22
149   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int16_t>(11), 2); // 22
150   loc.Destroy();
151   // Test scalar result for MaxlocDim, MinlocDim, MaxvalDim, MinvalDim.
152   // A scalar result occurs when you have a rank 1 array and dim == 1.
153   std::vector<int> shape1{24};
154   auto array1{MakeArray<TypeCategory::Real, 8>(shape1, rawData)};
155   StaticDescriptor<2, true> statDesc0[1];
156   Descriptor &scalarResult{statDesc0[0].descriptor()};
157   RTNAME(MaxlocDim)
158   (scalarResult, *array1, /*KIND=*/2, /*DIM=*/1, __FILE__, __LINE__,
159       /*MASK=*/nullptr, /*BACK=*/false);
160   EXPECT_EQ(scalarResult.rank(), 0);
161   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int16_t>(0), 23);
162   scalarResult.Destroy();
163 
164   // Test .FALSE. scalar MASK argument
165   auto falseMask{MakeArray<TypeCategory::Logical, 4>(
166       std::vector<int>{}, std::vector<std::int32_t>{0})};
167   RTNAME(MaxlocDim)
168   (loc, *array, /*KIND=*/4, /*DIM=*/2, __FILE__, __LINE__,
169       /*MASK=*/&*falseMask, /*BACK=*/false);
170   EXPECT_EQ(loc.rank(), 2);
171   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
172   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
173   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
174   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
175   EXPECT_EQ(loc.GetDimension(1).Extent(), 2);
176   for (int i{0}; i < 6; ++i) {
177     EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(0), 0);
178   }
179   loc.Destroy();
180 
181   // Test .TRUE. scalar MASK argument
182   auto trueMask{MakeArray<TypeCategory::Logical, 4>(
183       std::vector<int>{}, std::vector<std::int32_t>{1})};
184   RTNAME(MaxlocDim)
185   (loc, *array, /*KIND=*/4, /*DIM=*/2, __FILE__, __LINE__,
186       /*MASK=*/&*trueMask, /*BACK=*/false);
187   EXPECT_EQ(loc.rank(), 2);
188   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
189   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
190   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
191   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
192   EXPECT_EQ(loc.GetDimension(1).Extent(), 2);
193   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(0), 3);
194   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(1), 4);
195   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(2), 3);
196   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(3), 3);
197   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(4), 4);
198   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(5), 4);
199   loc.Destroy();
200 
201   RTNAME(MinlocDim)
202   (scalarResult, *array1, /*KIND=*/2, /*DIM=*/1, __FILE__, __LINE__,
203       /*MASK=*/nullptr, /*BACK=*/true);
204   EXPECT_EQ(scalarResult.rank(), 0);
205   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int16_t>(0), 22);
206   scalarResult.Destroy();
207 
208   // Test .FALSE. scalar MASK argument
209   RTNAME(MinlocDim)
210   (loc, *array, /*KIND=*/4, /*DIM=*/2, __FILE__, __LINE__,
211       /*MASK=*/&*falseMask, /*BACK=*/false);
212   EXPECT_EQ(loc.rank(), 2);
213   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
214   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
215   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
216   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
217   EXPECT_EQ(loc.GetDimension(1).Extent(), 2);
218   for (int i{0}; i < 6; ++i) {
219     EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(0), 0);
220   }
221   loc.Destroy();
222 
223   // Test .TRUE. scalar MASK argument
224   RTNAME(MinlocDim)
225   (loc, *array, /*KIND=*/4, /*DIM=*/2, __FILE__, __LINE__,
226       /*MASK=*/&*trueMask, /*BACK=*/false);
227   EXPECT_EQ(loc.rank(), 2);
228   EXPECT_EQ(loc.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
229   EXPECT_EQ(loc.GetDimension(0).LowerBound(), 1);
230   EXPECT_EQ(loc.GetDimension(0).Extent(), 3);
231   EXPECT_EQ(loc.GetDimension(1).LowerBound(), 1);
232   EXPECT_EQ(loc.GetDimension(1).Extent(), 2);
233   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(0), 4);
234   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(1), 3);
235   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(2), 4);
236   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(3), 4);
237   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(4), 3);
238   EXPECT_EQ(*loc.ZeroBasedIndexedElement<std::int32_t>(5), 2);
239   loc.Destroy();
240 
241   RTNAME(MaxvalDim)
242   (scalarResult, *array1, /*DIM=*/1, __FILE__, __LINE__, /*MASK=*/nullptr);
243   EXPECT_EQ(scalarResult.rank(), 0);
244   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<double>(0), 22.0);
245   scalarResult.Destroy();
246   RTNAME(MinvalDim)
247   (scalarResult, *array1, /*DIM=*/1, __FILE__, __LINE__, /*MASK=*/nullptr);
248   EXPECT_EQ(scalarResult.rank(), 0);
249   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<double>(0), -21.0);
250   scalarResult.Destroy();
251 }
252 
253 TEST(Reductions, Character) {
254   std::vector<int> shape{2, 3};
255   auto array{MakeArray<TypeCategory::Character, 1>(shape,
256       std::vector<std::string>{"abc", "def", "ghi", "jkl", "mno", "abc"}, 3)};
257   StaticDescriptor<1, true> statDesc[2];
258   Descriptor &res{statDesc[0].descriptor()};
259   RTNAME(MaxvalCharacter)(res, *array, __FILE__, __LINE__);
260   EXPECT_EQ(res.rank(), 0);
261   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Character, 1}.raw()));
262   EXPECT_EQ(std::memcmp(res.OffsetElement<char>(), "mno", 3), 0);
263   res.Destroy();
264   RTNAME(MinvalCharacter)(res, *array, __FILE__, __LINE__);
265   EXPECT_EQ(res.rank(), 0);
266   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Character, 1}.raw()));
267   EXPECT_EQ(std::memcmp(res.OffsetElement<char>(), "abc", 3), 0);
268   res.Destroy();
269   RTNAME(MaxlocCharacter)
270   (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr,
271       /*BACK=*/false);
272   EXPECT_EQ(res.rank(), 1);
273   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
274   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
275   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
276   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
277   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
278   res.Destroy();
279   auto mask{MakeArray<TypeCategory::Logical, 1>(
280       shape, std::vector<bool>{false, true, false, true, false, true})};
281   RTNAME(MaxlocCharacter)
282   (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/&*mask,
283       /*BACK=*/false);
284   EXPECT_EQ(res.rank(), 1);
285   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
286   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
287   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
288   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
289   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2);
290   res.Destroy();
291   RTNAME(MinlocCharacter)
292   (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr,
293       /*BACK=*/false);
294   EXPECT_EQ(res.rank(), 1);
295   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
296   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
297   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
298   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
299   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
300   res.Destroy();
301   RTNAME(MinlocCharacter)
302   (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/nullptr,
303       /*BACK=*/true);
304   EXPECT_EQ(res.rank(), 1);
305   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
306   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
307   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
308   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
309   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
310   res.Destroy();
311   RTNAME(MinlocCharacter)
312   (res, *array, /*KIND=*/4, __FILE__, __LINE__, /*MASK=*/&*mask,
313       /*BACK=*/true);
314   EXPECT_EQ(res.rank(), 1);
315   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
316   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
317   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
318   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
319   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
320   res.Destroy();
321   static const char targetChar[]{"abc"};
322   Descriptor &target{statDesc[1].descriptor()};
323   target.Establish(1, std::strlen(targetChar),
324       const_cast<void *>(static_cast<const void *>(&targetChar)), 0, nullptr,
325       CFI_attribute_pointer);
326   RTNAME(Findloc)
327   (res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr,
328       /*BACK=*/false);
329   EXPECT_EQ(res.rank(), 1);
330   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
331   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
332   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
333   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
334   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
335   res.Destroy();
336   RTNAME(Findloc)
337   (res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr, /*BACK=*/true);
338   EXPECT_EQ(res.rank(), 1);
339   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
340   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
341   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
342   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
343   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 3);
344   res.Destroy();
345 }
346 
347 TEST(Reductions, Logical) {
348   std::vector<int> shape{2, 2};
349   auto array{MakeArray<TypeCategory::Logical, 4>(
350       shape, std::vector<std::int32_t>{false, false, true, true})};
351   ASSERT_EQ(array->ElementBytes(), std::size_t{4});
352   EXPECT_EQ(RTNAME(All)(*array, __FILE__, __LINE__), false);
353   EXPECT_EQ(RTNAME(Any)(*array, __FILE__, __LINE__), true);
354   EXPECT_EQ(RTNAME(Parity)(*array, __FILE__, __LINE__), false);
355   EXPECT_EQ(RTNAME(Count)(*array, __FILE__, __LINE__), 2);
356   StaticDescriptor<2, true> statDesc[2];
357   Descriptor &res{statDesc[0].descriptor()};
358   RTNAME(AllDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__);
359   EXPECT_EQ(res.rank(), 1);
360   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
361   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
362   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
363   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
364   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
365   res.Destroy();
366   RTNAME(AllDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__);
367   EXPECT_EQ(res.rank(), 1);
368   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
369   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
370   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
371   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
372   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 0);
373   res.Destroy();
374   // Test scalar result for AllDim.
375   // A scalar result occurs when you have a rank 1 array.
376   std::vector<int> shape1{4};
377   auto array1{MakeArray<TypeCategory::Logical, 4>(
378       shape1, std::vector<std::int32_t>{false, false, true, true})};
379   StaticDescriptor<1, true> statDesc0[1];
380   Descriptor &scalarResult{statDesc0[0].descriptor()};
381   RTNAME(AllDim)(scalarResult, *array1, /*DIM=*/1, __FILE__, __LINE__);
382   EXPECT_EQ(scalarResult.rank(), 0);
383   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int32_t>(0), 0);
384   scalarResult.Destroy();
385   RTNAME(AnyDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__);
386   EXPECT_EQ(res.rank(), 1);
387   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
388   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
389   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
390   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
391   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
392   res.Destroy();
393   RTNAME(AnyDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__);
394   EXPECT_EQ(res.rank(), 1);
395   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
396   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
397   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
398   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
399   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
400   res.Destroy();
401   // Test scalar result for AnyDim.
402   // A scalar result occurs when you have a rank 1 array.
403   RTNAME(AnyDim)(scalarResult, *array1, /*DIM=*/1, __FILE__, __LINE__);
404   EXPECT_EQ(scalarResult.rank(), 0);
405   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int32_t>(0), 1);
406   scalarResult.Destroy();
407   RTNAME(ParityDim)(res, *array, /*DIM=*/1, __FILE__, __LINE__);
408   EXPECT_EQ(res.rank(), 1);
409   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
410   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
411   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
412   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
413   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 0);
414   res.Destroy();
415   RTNAME(ParityDim)(res, *array, /*DIM=*/2, __FILE__, __LINE__);
416   EXPECT_EQ(res.rank(), 1);
417   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Logical, 4}.raw()));
418   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
419   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
420   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
421   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
422   res.Destroy();
423   // Test scalar result for ParityDim.
424   // A scalar result occurs when you have a rank 1 array.
425   RTNAME(ParityDim)(scalarResult, *array1, /*DIM=*/1, __FILE__, __LINE__);
426   EXPECT_EQ(scalarResult.rank(), 0);
427   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int32_t>(0), 0);
428   scalarResult.Destroy();
429   RTNAME(CountDim)(res, *array, /*DIM=*/1, /*KIND=*/4, __FILE__, __LINE__);
430   EXPECT_EQ(res.rank(), 1);
431   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
432   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
433   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
434   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 0);
435   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2);
436   res.Destroy();
437   RTNAME(CountDim)(res, *array, /*DIM=*/2, /*KIND=*/8, __FILE__, __LINE__);
438   EXPECT_EQ(res.rank(), 1);
439   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
440   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
441   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
442   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(0), 1);
443   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int64_t>(1), 1);
444   res.Destroy();
445   // Test scalar result for CountDim.
446   // A scalar result occurs when you have a rank 1 array and dim == 1.
447   RTNAME(CountDim)
448   (scalarResult, *array1, /*DIM=*/1, /*KIND=*/8, __FILE__, __LINE__);
449   EXPECT_EQ(scalarResult.rank(), 0);
450   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<std::int64_t>(0), 2);
451   scalarResult.Destroy();
452   bool boolValue{false};
453   Descriptor &target{statDesc[1].descriptor()};
454   target.Establish(TypeCategory::Logical, 1, static_cast<void *>(&boolValue), 0,
455       nullptr, CFI_attribute_pointer);
456   RTNAME(Findloc)
457   (res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr,
458       /*BACK=*/false);
459   EXPECT_EQ(res.rank(), 1);
460   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
461   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
462   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
463   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 1);
464   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 1);
465   res.Destroy();
466   boolValue = true;
467   RTNAME(Findloc)
468   (res, *array, target, /*KIND=*/4, __FILE__, __LINE__, nullptr, /*BACK=*/true);
469   EXPECT_EQ(res.rank(), 1);
470   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 4}.raw()));
471   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
472   EXPECT_EQ(res.GetDimension(0).Extent(), 2);
473   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(0), 2);
474   EXPECT_EQ(*res.ZeroBasedIndexedElement<std::int32_t>(1), 2);
475   res.Destroy();
476 }
477 
478 TEST(Reductions, FindlocNumeric) {
479   std::vector<int> shape{2, 3};
480   auto realArray{MakeArray<TypeCategory::Real, 8>(shape,
481       std::vector<double>{0.0, -0.0, 1.0, 3.14,
482           std::numeric_limits<double>::quiet_NaN(),
483           std::numeric_limits<double>::infinity()})};
484   ASSERT_EQ(realArray->ElementBytes(), sizeof(double));
485   StaticDescriptor<2, true> statDesc[2];
486   Descriptor &res{statDesc[0].descriptor()};
487   // Find the first zero
488   Descriptor &target{statDesc[1].descriptor()};
489   double value{0.0};
490   target.Establish(TypeCategory::Real, 8, static_cast<void *>(&value), 0,
491       nullptr, CFI_attribute_pointer);
492   RTNAME(Findloc)
493   (res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
494   EXPECT_EQ(res.rank(), 1);
495   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
496   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
497   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
498   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 1);
499   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
500   res.Destroy();
501   // Find last zero (even though it's negative)
502   RTNAME(Findloc)
503   (res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/true);
504   EXPECT_EQ(res.rank(), 1);
505   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
506   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
507   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
508   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
509   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
510   res.Destroy();
511   // Find the +Inf
512   value = std::numeric_limits<double>::infinity();
513   RTNAME(Findloc)
514   (res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
515   EXPECT_EQ(res.rank(), 1);
516   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
517   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
518   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
519   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
520   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 3);
521   res.Destroy();
522   // Ensure that we can't find a NaN
523   value = std::numeric_limits<double>::quiet_NaN();
524   RTNAME(Findloc)
525   (res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
526   EXPECT_EQ(res.rank(), 1);
527   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
528   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
529   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
530   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 0);
531   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 0);
532   res.Destroy();
533   // Find a value of a distinct type
534   int intValue{1};
535   target.Establish(TypeCategory::Integer, 4, static_cast<void *>(&intValue), 0,
536       nullptr, CFI_attribute_pointer);
537   RTNAME(Findloc)
538   (res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
539   EXPECT_EQ(res.rank(), 1);
540   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
541   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
542   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
543   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 1);
544   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 2);
545   res.Destroy();
546   // Partial reductions
547   value = 1.0;
548   target.Establish(TypeCategory::Real, 8, static_cast<void *>(&value), 0,
549       nullptr, CFI_attribute_pointer);
550   RTNAME(FindlocDim)
551   (res, *realArray, target, 8, /*DIM=*/1, __FILE__, __LINE__, nullptr,
552       /*BACK=*/false);
553   EXPECT_EQ(res.rank(), 1);
554   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
555   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
556   EXPECT_EQ(res.GetDimension(0).UpperBound(), 3);
557   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 0);
558   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
559   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(2), 0);
560   res.Destroy();
561   RTNAME(FindlocDim)
562   (res, *realArray, target, 8, /*DIM=*/2, __FILE__, __LINE__, nullptr,
563       /*BACK=*/true);
564   EXPECT_EQ(res.rank(), 1);
565   EXPECT_EQ(res.type().raw(), (TypeCode{TypeCategory::Integer, 8}.raw()));
566   EXPECT_EQ(res.GetDimension(0).LowerBound(), 1);
567   EXPECT_EQ(res.GetDimension(0).UpperBound(), 2);
568   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(0), 2);
569   EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 0);
570   res.Destroy();
571   // Test scalar result for FindlocDim.
572   // A scalar result occurs when you have a rank 1 array, value, and dim == 1.
573   std::vector<int> shape1{6};
574   auto realArray1{MakeArray<TypeCategory::Real, 8>(shape1,
575       std::vector<double>{0.0, -0.0, 1.0, 3.14,
576           std::numeric_limits<double>::quiet_NaN(),
577           std::numeric_limits<double>::infinity()})};
578   StaticDescriptor<1, true> statDesc0[1];
579   Descriptor &scalarResult{statDesc0[0].descriptor()};
580   RTNAME(FindlocDim)
581   (scalarResult, *realArray1, target, 8, /*DIM=*/1, __FILE__, __LINE__, nullptr,
582       /*BACK=*/false);
583   EXPECT_EQ(scalarResult.rank(), 0);
584   EXPECT_EQ(*scalarResult.ZeroBasedIndexedElement<SubscriptValue>(0), 3);
585   scalarResult.Destroy();
586 }
587 
588 TEST(Reductions, DotProduct) {
589   auto realVector{MakeArray<TypeCategory::Real, 8>(
590       std::vector<int>{4}, std::vector<double>{0.0, -0.0, 1.0, -2.0})};
591   EXPECT_EQ(
592       RTNAME(DotProductReal8)(*realVector, *realVector, __FILE__, __LINE__),
593       5.0);
594   auto complexVector{MakeArray<TypeCategory::Complex, 4>(std::vector<int>{4},
595       std::vector<std::complex<float>>{
596           {0.0}, {-0.0, -0.0}, {1.0, -2.0}, {-2.0, 4.0}})};
597   std::complex<double> result8;
598   RTNAME(CppDotProductComplex8)
599   (result8, *realVector, *complexVector, __FILE__, __LINE__);
600   EXPECT_EQ(result8, (std::complex<double>{5.0, -10.0}));
601   RTNAME(CppDotProductComplex8)
602   (result8, *complexVector, *realVector, __FILE__, __LINE__);
603   EXPECT_EQ(result8, (std::complex<double>{5.0, 10.0}));
604   std::complex<float> result4;
605   RTNAME(CppDotProductComplex4)
606   (result4, *complexVector, *complexVector, __FILE__, __LINE__);
607   EXPECT_EQ(result4, (std::complex<float>{25.0, 0.0}));
608   auto logicalVector1{MakeArray<TypeCategory::Logical, 1>(
609       std::vector<int>{4}, std::vector<bool>{false, false, true, true})};
610   EXPECT_TRUE(RTNAME(DotProductLogical)(
611       *logicalVector1, *logicalVector1, __FILE__, __LINE__));
612   auto logicalVector2{MakeArray<TypeCategory::Logical, 1>(
613       std::vector<int>{4}, std::vector<bool>{true, true, false, false})};
614   EXPECT_TRUE(RTNAME(DotProductLogical)(
615       *logicalVector2, *logicalVector2, __FILE__, __LINE__));
616   EXPECT_FALSE(RTNAME(DotProductLogical)(
617       *logicalVector1, *logicalVector2, __FILE__, __LINE__));
618   EXPECT_FALSE(RTNAME(DotProductLogical)(
619       *logicalVector2, *logicalVector1, __FILE__, __LINE__));
620 }
621 
622 #if HAS_LDBL128 || HAS_FLOAT128
623 TEST(Reductions, ExtremaReal16) {
624   // The identity value for Min/Maxval for REAL(16) was mistakenly
625   // set to 0.0.
626   using ElemType = CppTypeFor<TypeCategory::Real, 16>;
627   std::vector<int> shape{3};
628   //   1.0  2.0  3.0
629   std::vector<ElemType> rawMinData{1.0, 2.0, 3.0};
630   auto minArray{MakeArray<TypeCategory::Real, 16>(shape, rawMinData)};
631   EXPECT_EQ(RTNAME(MinvalReal16)(*minArray, __FILE__, __LINE__), 1.0);
632   //   -1.0  -2.0  -3.0
633   std::vector<ElemType> rawMaxData{-1.0, -2.0, -3.0};
634   auto maxArray{MakeArray<TypeCategory::Real, 16>(shape, rawMaxData)};
635   EXPECT_EQ(RTNAME(MaxvalReal16)(*maxArray, __FILE__, __LINE__), -1.0);
636 }
637 #endif // HAS_LDBL128 || HAS_FLOAT128
638 
639 static std::int32_t IAdd(const std::int32_t *x, const std::int32_t *y) {
640   return *x + *y;
641 }
642 
643 static std::int32_t IMultiply(const std::int32_t *x, const std::int32_t *y) {
644   return *x * *y;
645 }
646 
647 TEST(Reductions, ReduceInt4) {
648   auto intVector{MakeArray<TypeCategory::Integer, 4>(
649       std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
650   EXPECT_EQ(
651       RTNAME(ReduceInteger4Ref)(*intVector, IAdd, __FILE__, __LINE__), 10);
652   EXPECT_EQ(
653       RTNAME(ReduceInteger4Ref)(*intVector, IMultiply, __FILE__, __LINE__), 24);
654 }
655 TEST(Reductions, ReduceInt4Dim) {
656   auto intMatrix{MakeArray<TypeCategory::Integer, 4>(
657       std::vector<int>{2, 2}, std::vector<std::int32_t>{1, 2, 3, 4})};
658   StaticDescriptor<2, true> statDesc;
659   Descriptor &sums{statDesc.descriptor()};
660   RTNAME(ReduceInteger4DimRef)(sums, *intMatrix, IAdd, __FILE__, __LINE__, 1);
661   EXPECT_EQ(sums.rank(), 1);
662   EXPECT_EQ(sums.GetDimension(0).LowerBound(), 1);
663   EXPECT_EQ(sums.GetDimension(0).Extent(), 2);
664   EXPECT_EQ(*sums.ZeroBasedIndexedElement<std::int32_t>(0), 3);
665   EXPECT_EQ(*sums.ZeroBasedIndexedElement<std::int32_t>(1), 7);
666   sums.Destroy();
667   RTNAME(ReduceInteger4DimRef)(sums, *intMatrix, IAdd, __FILE__, __LINE__, 2);
668   EXPECT_EQ(sums.rank(), 1);
669   EXPECT_EQ(sums.GetDimension(0).LowerBound(), 1);
670   EXPECT_EQ(sums.GetDimension(0).Extent(), 2);
671   EXPECT_EQ(*sums.ZeroBasedIndexedElement<std::int32_t>(0), 4);
672   EXPECT_EQ(*sums.ZeroBasedIndexedElement<std::int32_t>(1), 6);
673   sums.Destroy();
674 }
675