xref: /llvm-project/flang/unittests/Runtime/Transformational.cpp (revision fc51c7f0cc1abf1679100d71d103fe5d943f580b)
1 //===-- flang/unittests/Runtime/Transformational.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/transformational.h"
10 #include "gtest/gtest.h"
11 #include "tools.h"
12 #include "flang/Common/float128.h"
13 #include "flang/Runtime/type-code.h"
14 #include <vector>
15 
16 using namespace Fortran::runtime;
17 using Fortran::common::TypeCategory;
18 
19 template <int KIND>
20 using BesselFuncType = std::function<void(Descriptor &, int32_t, int32_t,
21     CppTypeFor<TypeCategory::Real, KIND>, CppTypeFor<TypeCategory::Real, KIND>,
22     CppTypeFor<TypeCategory::Real, KIND>, const char *, int)>;
23 
24 template <int KIND>
25 using BesselX0FuncType =
26     std::function<void(Descriptor &, int32_t, int32_t, const char *, int)>;
27 
28 template <int KIND>
29 constexpr CppTypeFor<TypeCategory::Real, KIND>
30     besselEpsilon = CppTypeFor<TypeCategory::Real, KIND>(1e-4);
31 
32 template <int KIND>
33 static void testBesselJn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
34     CppTypeFor<TypeCategory::Real, KIND> x,
35     const std::vector<CppTypeFor<TypeCategory::Real, KIND>> &expected) {
36   StaticDescriptor desc;
37   Descriptor &result{desc.descriptor()};
38   unsigned len = expected.size();
39 
40   CppTypeFor<TypeCategory::Real, KIND> anc0 = len > 0 ? expected[len - 1] : 0.0;
41   CppTypeFor<TypeCategory::Real, KIND> anc1 = len > 1 ? expected[len - 2] : 0.0;
42 
43   rtFunc(result, n1, n2, x, anc0, anc1, __FILE__, __LINE__);
44 
45   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
46   EXPECT_EQ(result.rank(), 1);
47   EXPECT_EQ(
48       result.ElementBytes(), sizeof(CppTypeFor<TypeCategory::Real, KIND>));
49   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
50   EXPECT_EQ(result.GetDimension(0).Extent(), len);
51 
52   for (size_t j{0}; j < len; ++j) {
53     EXPECT_NEAR(
54         (*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
55             j)),
56         expected[j], besselEpsilon<KIND>);
57   }
58 }
59 
60 template <int KIND>
61 static void testBesselJnX0(
62     BesselX0FuncType<KIND> rtFunc, int32_t n1, int32_t n2) {
63   StaticDescriptor desc;
64   Descriptor &result{desc.descriptor()};
65 
66   rtFunc(result, n1, n2, __FILE__, __LINE__);
67 
68   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
69   EXPECT_EQ(result.rank(), 1);
70   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
71   EXPECT_EQ(result.GetDimension(0).Extent(), n2 >= n1 ? n2 - n1 + 1 : 0);
72 
73   if (n2 < n1) {
74     return;
75   }
76 
77   EXPECT_NEAR(
78       (*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
79           0)),
80       (n1 == 0) ? 1.0 : 0.0, 1e-5);
81 
82   for (int j{1}; j < (n2 - n1 + 1); ++j) {
83     EXPECT_NEAR(
84         (*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
85             j)),
86         0.0, besselEpsilon<KIND>);
87   }
88 }
89 
90 template <int KIND> static void testBesselJn(BesselFuncType<KIND> rtFunc) {
91   testBesselJn<KIND>(rtFunc, 1, 0, 1.0, {});
92   testBesselJn<KIND>(rtFunc, 0, 0, 1.0, {0.765197694});
93   testBesselJn<KIND>(rtFunc, 0, 1, 1.0, {0.765197694, 0.440050572});
94   testBesselJn<KIND>(
95       rtFunc, 0, 2, 1.0, {0.765197694, 0.440050572, 0.114903487});
96   testBesselJn<KIND>(rtFunc, 1, 5, 5.0,
97       {-0.327579111, 0.046565145, 0.364831239, 0.391232371, 0.261140555});
98 }
99 
100 template <int KIND> static void testBesselJnX0(BesselX0FuncType<KIND> rtFunc) {
101   testBesselJnX0<KIND>(rtFunc, 1, 0);
102   testBesselJnX0<KIND>(rtFunc, 0, 0);
103   testBesselJnX0<KIND>(rtFunc, 1, 1);
104   testBesselJnX0<KIND>(rtFunc, 0, 3);
105   testBesselJnX0<KIND>(rtFunc, 1, 4);
106 }
107 
108 static void testBesselJn() {
109   testBesselJn<4>(RTNAME(BesselJn_4));
110   testBesselJn<8>(RTNAME(BesselJn_8));
111 #if HAS_FLOAT80
112   testBesselJn<10>(RTNAME(BesselJn_10));
113 #endif
114 #if HAS_LDBL128 || HAS_FLOAT128
115   testBesselJn<16>(RTNAME(BesselJn_16));
116 #endif
117 
118   testBesselJnX0<4>(RTNAME(BesselJnX0_4));
119   testBesselJnX0<8>(RTNAME(BesselJnX0_8));
120 #if HAS_FLOAT80
121   testBesselJnX0<10>(RTNAME(BesselJnX0_10));
122 #endif
123 #if HAS_LDBL128 || HAS_FLOAT128
124   testBesselJnX0<16>(RTNAME(BesselJnX0_16));
125 #endif
126 }
127 
128 TEST(Transformational, BesselJn) { testBesselJn(); }
129 
130 template <int KIND>
131 static void testBesselYn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
132     CppTypeFor<TypeCategory::Real, KIND> x,
133     const std::vector<CppTypeFor<TypeCategory::Real, KIND>> &expected) {
134   StaticDescriptor desc;
135   Descriptor &result{desc.descriptor()};
136   unsigned len = expected.size();
137 
138   CppTypeFor<TypeCategory::Real, KIND> anc0 = len > 0 ? expected[0] : 0.0;
139   CppTypeFor<TypeCategory::Real, KIND> anc1 = len > 1 ? expected[1] : 0.0;
140 
141   rtFunc(result, n1, n2, x, anc0, anc1, __FILE__, __LINE__);
142 
143   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
144   EXPECT_EQ(result.rank(), 1);
145   EXPECT_EQ(
146       result.ElementBytes(), sizeof(CppTypeFor<TypeCategory::Real, KIND>));
147   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
148   EXPECT_EQ(result.GetDimension(0).Extent(), len);
149 
150   for (size_t j{0}; j < len; ++j) {
151     EXPECT_NEAR(
152         (*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
153             j)),
154         expected[j], besselEpsilon<KIND>);
155   }
156 }
157 
158 template <int KIND>
159 static void testBesselYnX0(
160     BesselX0FuncType<KIND> rtFunc, int32_t n1, int32_t n2) {
161   StaticDescriptor<2> desc;
162   Descriptor &result{desc.descriptor()};
163 
164   rtFunc(result, n1, n2, __FILE__, __LINE__);
165 
166   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
167   EXPECT_EQ(result.rank(), 1);
168   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
169   EXPECT_EQ(result.GetDimension(0).Extent(), n2 >= n1 ? n2 - n1 + 1 : 0);
170 
171   if (n2 < n1) {
172     return;
173   }
174 
175   for (int j{0}; j < (n2 - n1 + 1); ++j) {
176     EXPECT_EQ(
177         (*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
178             j)),
179         (-std::numeric_limits<
180             CppTypeFor<TypeCategory::Real, KIND>>::infinity()));
181   }
182 }
183 
184 template <int KIND> static void testBesselYn(BesselFuncType<KIND> rtFunc) {
185   testBesselYn<KIND>(rtFunc, 1, 0, 1.0, {});
186   testBesselYn<KIND>(rtFunc, 0, 0, 1.0, {0.08825695});
187   testBesselYn<KIND>(rtFunc, 0, 1, 1.0, {0.08825695, -0.7812128});
188   testBesselYn<KIND>(rtFunc, 0, 2, 1.0, {0.0882569555, -0.7812128, -1.6506826});
189   testBesselYn<KIND>(rtFunc, 1, 5, 1.0,
190       {-0.7812128, -1.6506826, -5.8215175, -33.278423, -260.40588});
191 }
192 
193 template <int KIND> static void testBesselYnX0(BesselX0FuncType<KIND> rtFunc) {
194   testBesselYnX0<KIND>(rtFunc, 1, 0);
195   testBesselYnX0<KIND>(rtFunc, 0, 0);
196   testBesselYnX0<KIND>(rtFunc, 1, 1);
197   testBesselYnX0<KIND>(rtFunc, 0, 3);
198   testBesselYnX0<KIND>(rtFunc, 1, 4);
199 }
200 
201 static void testBesselYn() {
202   testBesselYn<4>(RTNAME(BesselYn_4));
203   testBesselYn<8>(RTNAME(BesselYn_8));
204 #if HAS_FLOAT80
205   testBesselYn<10>(RTNAME(BesselYn_10));
206 #endif
207 #if HAS_LDBL128 || HAS_FLOAT128
208   testBesselYn<16>(RTNAME(BesselYn_16));
209 #endif
210 
211   testBesselYnX0<4>(RTNAME(BesselYnX0_4));
212   testBesselYnX0<8>(RTNAME(BesselYnX0_8));
213 #if HAS_FLOAT80
214   testBesselYnX0<10>(RTNAME(BesselYnX0_10));
215 #endif
216 #if HAS_LDBL128 || HAS_FLOAT128
217   testBesselYnX0<16>(RTNAME(BesselYnX0_16));
218 #endif
219 }
220 
221 TEST(Transformational, BesselYn) { testBesselYn(); }
222 
223 TEST(Transformational, Shifts) {
224   // ARRAY  1 3 5
225   //        2 4 6
226   auto array{MakeArray<TypeCategory::Integer, 4>(
227       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
228   array->GetDimension(0).SetLowerBound(0); // shouldn't matter
229   array->GetDimension(1).SetLowerBound(-1);
230   StaticDescriptor<2, true> statDesc;
231   Descriptor &result{statDesc.descriptor()};
232 
233   auto shift3{MakeArray<TypeCategory::Integer, 8>(
234       std::vector<int>{3}, std::vector<std::int64_t>{1, -1, 2})};
235   RTNAME(Cshift)(result, *array, *shift3, 1, __FILE__, __LINE__);
236   EXPECT_EQ(result.type(), array->type());
237   EXPECT_EQ(result.rank(), 2);
238   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
239   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
240   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
241   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
242   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
243   static std::int32_t cshiftExpect1[6]{2, 1, 4, 3, 5, 6};
244   for (int j{0}; j < 6; ++j) {
245     EXPECT_EQ(
246         *result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect1[j]);
247   }
248   result.Destroy();
249 
250   auto shift2{MakeArray<TypeCategory::Integer, 1>(
251       std::vector<int>{2}, std::vector<std::int8_t>{1, -1})};
252   shift2->GetDimension(0).SetLowerBound(-1); // shouldn't matter
253   RTNAME(Cshift)(result, *array, *shift2, 2, __FILE__, __LINE__);
254   EXPECT_EQ(result.type(), array->type());
255   EXPECT_EQ(result.rank(), 2);
256   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
257   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
258   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
259   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
260   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
261   static std::int32_t cshiftExpect2[6]{3, 6, 5, 2, 1, 4};
262   for (int j{0}; j < 6; ++j) {
263     EXPECT_EQ(
264         *result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect2[j]);
265   }
266   result.Destroy();
267 
268   // VECTOR  1 3 5 2 4 6
269   auto vector{MakeArray<TypeCategory::Integer, 4>(
270       std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
271   vector->GetDimension(0).SetLowerBound(0);
272   StaticDescriptor<1, true> vectorDesc;
273   Descriptor &vectorResult{vectorDesc.descriptor()};
274 
275   RTNAME(CshiftVector)(vectorResult, *vector, 2, __FILE__, __LINE__);
276   EXPECT_EQ(vectorResult.type(), array->type());
277   EXPECT_EQ(vectorResult.rank(), 1);
278   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
279   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
280   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
281   static std::int32_t cshiftExpect3[6]{3, 4, 5, 6, 1, 2};
282   for (int j{0}; j < 6; ++j) {
283     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
284         cshiftExpect3[j]);
285   }
286   vectorResult.Destroy();
287 
288   RTNAME(CshiftVector)(vectorResult, *vector, -2, __FILE__, __LINE__);
289   EXPECT_EQ(vectorResult.type(), array->type());
290   EXPECT_EQ(vectorResult.rank(), 1);
291   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
292   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
293   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
294   static std::int32_t cshiftExpect4[6]{5, 6, 1, 2, 3, 4};
295   for (int j{0}; j < 6; ++j) {
296     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
297         cshiftExpect4[j]);
298   }
299   vectorResult.Destroy();
300 
301   // VECTOR  1 3 5 2 4 6 WITH non zero lower bound in a negative cshift.
302   auto vectorWithLowerBounds{MakeArray<TypeCategory::Integer, 4>(
303       std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
304   vectorWithLowerBounds->GetDimension(0).SetLowerBound(2);
305 
306   RTNAME(CshiftVector)
307   (vectorResult, *vectorWithLowerBounds, -2, __FILE__, __LINE__);
308   EXPECT_EQ(vectorResult.type(), array->type());
309   EXPECT_EQ(vectorResult.rank(), 1);
310   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
311   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
312   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
313   static std::int32_t cshiftExpect5[6]{5, 6, 1, 2, 3, 4};
314   for (int j{0}; j < 6; ++j) {
315     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
316         cshiftExpect5[j]);
317   }
318   vectorResult.Destroy();
319 
320   auto boundary{MakeArray<TypeCategory::Integer, 4>(
321       std::vector<int>{3}, std::vector<std::int32_t>{-1, -2, -3})};
322   boundary->GetDimension(0).SetLowerBound(9); // shouldn't matter
323   RTNAME(Eoshift)(result, *array, *shift3, &*boundary, 1, __FILE__, __LINE__);
324   EXPECT_EQ(result.type(), array->type());
325   EXPECT_EQ(result.rank(), 2);
326   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
327   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
328   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
329   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
330   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
331   static std::int32_t eoshiftExpect1[6]{2, -1, -2, 3, -3, -3};
332   for (int j{0}; j < 6; ++j) {
333     EXPECT_EQ(
334         *result.ZeroBasedIndexedElement<std::int32_t>(j), eoshiftExpect1[j]);
335   }
336   result.Destroy();
337 
338   // VECTOR EOSHIFT
339   StaticDescriptor<0> boundaryDescriptor;
340   Descriptor vectorBoundary{boundaryDescriptor.descriptor()};
341   std::int32_t boundaryValue{343};
342   vectorBoundary.Establish(TypeCategory::Integer, 4,
343       const_cast<void *>(reinterpret_cast<const void *>(&boundaryValue)), 0);
344   RTNAME(EoshiftVector)
345   (vectorResult, *vector, 2, &vectorBoundary, __FILE__, __LINE__);
346   EXPECT_EQ(vectorResult.type(), array->type());
347   EXPECT_EQ(vectorResult.rank(), 1);
348   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
349   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
350   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
351   static std::int32_t eoshiftVectorExpect[6]{3, 4, 5, 6, 343, 343};
352   for (int j{0}; j < 6; ++j) {
353     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
354         eoshiftVectorExpect[j]);
355   }
356   vectorResult.Destroy();
357 
358   // VECTOR EOSHIFT on input with non zero lower bounds
359   RTNAME(EoshiftVector)
360   (vectorResult, *vectorWithLowerBounds, -2, &vectorBoundary, __FILE__,
361       __LINE__);
362   EXPECT_EQ(vectorResult.type(), array->type());
363   EXPECT_EQ(vectorResult.rank(), 1);
364   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
365   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
366   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
367   static std::int32_t eoshiftVectorExpect2[6]{343, 343, 1, 2, 3, 4};
368   for (int j{0}; j < 6; ++j) {
369     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
370         eoshiftVectorExpect2[j]);
371   }
372   vectorResult.Destroy();
373 }
374 
375 TEST(Transformational, Pack) {
376   // ARRAY  1 3 5
377   //        2 4 6
378   auto array{MakeArray<TypeCategory::Integer, 4>(
379       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
380   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
381   array->GetDimension(1).SetLowerBound(-1);
382   auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
383       std::vector<std::uint8_t>{false, true, true, false, false, true})};
384   mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
385   mask->GetDimension(1).SetLowerBound(2);
386   StaticDescriptor<maxRank, true> statDesc;
387   Descriptor &result{statDesc.descriptor()};
388 
389   RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__);
390   EXPECT_EQ(result.type(), array->type());
391   EXPECT_EQ(result.rank(), 1);
392   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
393   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
394   static std::int32_t packExpect1[3]{2, 3, 6};
395   for (int j{0}; j < 3; ++j) {
396     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect1[j])
397         << " at " << j;
398   }
399   result.Destroy();
400 
401   auto vector{MakeArray<TypeCategory::Integer, 4>(
402       std::vector<int>{5}, std::vector<std::int32_t>{-1, -2, -3, -4, -5})};
403   RTNAME(Pack)(result, *array, *mask, &*vector, __FILE__, __LINE__);
404   EXPECT_EQ(result.type(), array->type());
405   EXPECT_EQ(result.rank(), 1);
406   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
407   EXPECT_EQ(result.GetDimension(0).Extent(), 5);
408   static std::int32_t packExpect2[5]{2, 3, 6, -4, -5};
409   for (int j{0}; j < 5; ++j) {
410     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect2[j])
411         << " at " << j;
412   }
413   result.Destroy();
414 }
415 
416 TEST(Transformational, Spread) {
417   auto array{MakeArray<TypeCategory::Integer, 4>(
418       std::vector<int>{3}, std::vector<std::int32_t>{1, 2, 3})};
419   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
420   StaticDescriptor<2, true> statDesc;
421   Descriptor &result{statDesc.descriptor()};
422 
423   RTNAME(Spread)(result, *array, 1, 2, __FILE__, __LINE__);
424   EXPECT_EQ(result.type(), array->type());
425   EXPECT_EQ(result.rank(), 2);
426   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
427   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
428   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
429   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
430   for (int j{0}; j < 6; ++j) {
431     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j / 2);
432   }
433   result.Destroy();
434 
435   RTNAME(Spread)(result, *array, 2, 2, __FILE__, __LINE__);
436   EXPECT_EQ(result.type(), array->type());
437   EXPECT_EQ(result.rank(), 2);
438   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
439   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
440   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
441   EXPECT_EQ(result.GetDimension(1).Extent(), 2);
442   for (int j{0}; j < 6; ++j) {
443     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j % 3);
444   }
445   result.Destroy();
446 
447   auto scalar{MakeArray<TypeCategory::Integer, 4>(
448       std::vector<int>{}, std::vector<std::int32_t>{1})};
449   RTNAME(Spread)(result, *scalar, 1, 2, __FILE__, __LINE__);
450   EXPECT_EQ(result.type(), array->type());
451   EXPECT_EQ(result.rank(), 1);
452   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
453   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
454   for (int j{0}; j < 2; ++j) {
455     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1);
456   }
457   result.Destroy();
458 }
459 
460 TEST(Transformational, Transpose) {
461   // ARRAY  1 3 5
462   //        2 4 6
463   auto array{MakeArray<TypeCategory::Integer, 4>(
464       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
465   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
466   array->GetDimension(1).SetLowerBound(-6);
467   StaticDescriptor<2, true> statDesc;
468   Descriptor &result{statDesc.descriptor()};
469   RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
470   EXPECT_EQ(result.type(), array->type());
471   EXPECT_EQ(result.rank(), 2);
472   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
473   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
474   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
475   EXPECT_EQ(result.GetDimension(1).Extent(), 2);
476   static std::int32_t expect[6]{1, 3, 5, 2, 4, 6};
477   for (int j{0}; j < 6; ++j) {
478     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
479   }
480   result.Destroy();
481 }
482 
483 TEST(Transformational, Unpack) {
484   auto vector{MakeArray<TypeCategory::Integer, 4>(
485       std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
486   vector->GetDimension(0).SetLowerBound(2); // shouldn't matter
487   auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
488       std::vector<std::uint8_t>{false, true, true, false, false, true})};
489   mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
490   mask->GetDimension(1).SetLowerBound(2);
491   auto field{MakeArray<TypeCategory::Integer, 4>(std::vector<int>{2, 3},
492       std::vector<std::int32_t>{-1, -2, -3, -4, -5, -6})};
493   field->GetDimension(0).SetLowerBound(-1); // shouldn't matter
494   StaticDescriptor<2, true> statDesc;
495   Descriptor &result{statDesc.descriptor()};
496   RTNAME(Unpack)(result, *vector, *mask, *field, __FILE__, __LINE__);
497   EXPECT_EQ(result.type(), vector->type());
498   EXPECT_EQ(result.rank(), 2);
499   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
500   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
501   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
502   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
503   static std::int32_t expect[6]{-1, 1, 2, -4, -5, 3};
504   for (int j{0}; j < 6; ++j) {
505     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
506   }
507   result.Destroy();
508 
509   // Test for scalar value of the "field" argument
510   auto scalarField{MakeArray<TypeCategory::Integer, 4>(
511       std::vector<int>{}, std::vector<std::int32_t>{343})};
512   RTNAME(Unpack)(result, *vector, *mask, *scalarField, __FILE__, __LINE__);
513   EXPECT_EQ(result.rank(), 2);
514   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
515   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
516   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
517   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
518   static std::int32_t scalarExpect[6]{343, 1, 2, 343, 343, 3};
519   for (int j{0}; j < 6; ++j) {
520     EXPECT_EQ(
521         *result.ZeroBasedIndexedElement<std::int32_t>(j), scalarExpect[j]);
522   }
523   result.Destroy();
524 }
525 
526 #if HAS_FLOAT80
527 // Make sure the destination descriptor is created by the runtime
528 // with proper element size, when REAL*10 maps to 'long double'.
529 #define Real10CppType long double
530 TEST(Transformational, TransposeReal10) {
531   // ARRAY  1 3 5
532   //        2 4 6
533   auto array{MakeArray<TypeCategory::Real, 10>(std::vector<int>{2, 3},
534       std::vector<Real10CppType>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0},
535       sizeof(Real10CppType))};
536   StaticDescriptor<2, true> statDesc;
537   Descriptor &result{statDesc.descriptor()};
538   RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
539   EXPECT_EQ(result.ElementBytes(), sizeof(Real10CppType));
540   EXPECT_EQ(result.type(), array->type());
541   EXPECT_EQ(result.rank(), 2);
542   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
543   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
544   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
545   EXPECT_EQ(result.GetDimension(1).Extent(), 2);
546   static Real10CppType expect[6]{1.0, 3.0, 5.0, 2.0, 4.0, 6.0};
547   for (int j{0}; j < 6; ++j) {
548     EXPECT_EQ(*result.ZeroBasedIndexedElement<Real10CppType>(j), expect[j]);
549   }
550   result.Destroy();
551 }
552 #endif
553