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