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/Runtime/type-code.h"
13 
14 using namespace Fortran::runtime;
15 using Fortran::common::TypeCategory;
16 
TEST(Transformational,Shifts)17 TEST(Transformational, Shifts) {
18   // ARRAY  1 3 5
19   //        2 4 6
20   auto array{MakeArray<TypeCategory::Integer, 4>(
21       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
22   array->GetDimension(0).SetLowerBound(0); // shouldn't matter
23   array->GetDimension(1).SetLowerBound(-1);
24   StaticDescriptor<2, true> statDesc;
25   Descriptor &result{statDesc.descriptor()};
26 
27   auto shift3{MakeArray<TypeCategory::Integer, 8>(
28       std::vector<int>{3}, std::vector<std::int64_t>{1, -1, 2})};
29   RTNAME(Cshift)(result, *array, *shift3, 1, __FILE__, __LINE__);
30   EXPECT_EQ(result.type(), array->type());
31   EXPECT_EQ(result.rank(), 2);
32   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
33   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
34   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
35   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
36   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
37   static std::int32_t cshiftExpect1[6]{2, 1, 4, 3, 5, 6};
38   for (int j{0}; j < 6; ++j) {
39     EXPECT_EQ(
40         *result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect1[j]);
41   }
42   result.Destroy();
43 
44   auto shift2{MakeArray<TypeCategory::Integer, 1>(
45       std::vector<int>{2}, std::vector<std::int8_t>{1, -1})};
46   shift2->GetDimension(0).SetLowerBound(-1); // shouldn't matter
47   shift2->GetDimension(1).SetLowerBound(2);
48   RTNAME(Cshift)(result, *array, *shift2, 2, __FILE__, __LINE__);
49   EXPECT_EQ(result.type(), array->type());
50   EXPECT_EQ(result.rank(), 2);
51   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
52   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
53   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
54   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
55   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
56   static std::int32_t cshiftExpect2[6]{3, 6, 5, 2, 1, 4};
57   for (int j{0}; j < 6; ++j) {
58     EXPECT_EQ(
59         *result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect2[j]);
60   }
61   result.Destroy();
62 
63   // VECTOR  1 3 5 2 4 6
64   auto vector{MakeArray<TypeCategory::Integer, 4>(
65       std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
66   vector->GetDimension(0).SetLowerBound(0);
67   StaticDescriptor<1, true> vectorDesc;
68   Descriptor &vectorResult{vectorDesc.descriptor()};
69 
70   RTNAME(CshiftVector)(vectorResult, *vector, 2, __FILE__, __LINE__);
71   EXPECT_EQ(vectorResult.type(), array->type());
72   EXPECT_EQ(vectorResult.rank(), 1);
73   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
74   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
75   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
76   static std::int32_t cshiftExpect3[6]{3, 4, 5, 6, 1, 2};
77   for (int j{0}; j < 6; ++j) {
78     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
79         cshiftExpect3[j]);
80   }
81   vectorResult.Destroy();
82 
83   RTNAME(CshiftVector)(vectorResult, *vector, -2, __FILE__, __LINE__);
84   EXPECT_EQ(vectorResult.type(), array->type());
85   EXPECT_EQ(vectorResult.rank(), 1);
86   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
87   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
88   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
89   static std::int32_t cshiftExpect4[6]{5, 6, 1, 2, 3, 4};
90   for (int j{0}; j < 6; ++j) {
91     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
92         cshiftExpect4[j]);
93   }
94   vectorResult.Destroy();
95 
96   // VECTOR  1 3 5 2 4 6 WITH non zero lower bound in a negative cshift.
97   auto vectorWithLowerBounds{MakeArray<TypeCategory::Integer, 4>(
98       std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
99   vectorWithLowerBounds->GetDimension(0).SetLowerBound(2);
100 
101   RTNAME(CshiftVector)
102   (vectorResult, *vectorWithLowerBounds, -2, __FILE__, __LINE__);
103   EXPECT_EQ(vectorResult.type(), array->type());
104   EXPECT_EQ(vectorResult.rank(), 1);
105   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
106   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
107   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
108   static std::int32_t cshiftExpect5[6]{5, 6, 1, 2, 3, 4};
109   for (int j{0}; j < 6; ++j) {
110     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
111         cshiftExpect5[j]);
112   }
113   vectorResult.Destroy();
114 
115   auto boundary{MakeArray<TypeCategory::Integer, 4>(
116       std::vector<int>{3}, std::vector<std::int32_t>{-1, -2, -3})};
117   boundary->GetDimension(0).SetLowerBound(9); // shouldn't matter
118   RTNAME(Eoshift)(result, *array, *shift3, &*boundary, 1, __FILE__, __LINE__);
119   EXPECT_EQ(result.type(), array->type());
120   EXPECT_EQ(result.rank(), 2);
121   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
122   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
123   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
124   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
125   EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
126   static std::int32_t eoshiftExpect1[6]{2, -1, -2, 3, -3, -3};
127   for (int j{0}; j < 6; ++j) {
128     EXPECT_EQ(
129         *result.ZeroBasedIndexedElement<std::int32_t>(j), eoshiftExpect1[j]);
130   }
131   result.Destroy();
132 
133   // VECTOR EOSHIFT
134   StaticDescriptor<0> boundaryDescriptor;
135   Descriptor vectorBoundary{boundaryDescriptor.descriptor()};
136   std::int32_t boundaryValue{343};
137   vectorBoundary.Establish(TypeCategory::Integer, 4,
138       const_cast<void *>(reinterpret_cast<const void *>(&boundaryValue)), 0);
139   RTNAME(EoshiftVector)
140   (vectorResult, *vector, 2, &vectorBoundary, __FILE__, __LINE__);
141   EXPECT_EQ(vectorResult.type(), array->type());
142   EXPECT_EQ(vectorResult.rank(), 1);
143   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
144   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
145   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
146   static std::int32_t eoshiftVectorExpect[6]{3, 4, 5, 6, 343, 343};
147   for (int j{0}; j < 6; ++j) {
148     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
149         eoshiftVectorExpect[j]);
150   }
151   vectorResult.Destroy();
152 
153   // VECTOR EOSHIFT on input with non zero lower bounds
154   RTNAME(EoshiftVector)
155   (vectorResult, *vectorWithLowerBounds, -2, &vectorBoundary, __FILE__,
156       __LINE__);
157   EXPECT_EQ(vectorResult.type(), array->type());
158   EXPECT_EQ(vectorResult.rank(), 1);
159   EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
160   EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
161   EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
162   static std::int32_t eoshiftVectorExpect2[6]{343, 343, 1, 2, 3, 4};
163   for (int j{0}; j < 6; ++j) {
164     EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
165         eoshiftVectorExpect2[j]);
166   }
167   vectorResult.Destroy();
168 }
169 
TEST(Transformational,Pack)170 TEST(Transformational, Pack) {
171   // ARRAY  1 3 5
172   //        2 4 6
173   auto array{MakeArray<TypeCategory::Integer, 4>(
174       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
175   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
176   array->GetDimension(1).SetLowerBound(-1);
177   auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
178       std::vector<std::uint8_t>{false, true, true, false, false, true})};
179   mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
180   mask->GetDimension(1).SetLowerBound(2);
181   StaticDescriptor<1, true> statDesc;
182   Descriptor &result{statDesc.descriptor()};
183 
184   RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__);
185   EXPECT_EQ(result.type(), array->type());
186   EXPECT_EQ(result.rank(), 1);
187   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
188   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
189   static std::int32_t packExpect1[3]{2, 3, 6};
190   for (int j{0}; j < 3; ++j) {
191     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect1[j])
192         << " at " << j;
193   }
194   result.Destroy();
195 
196   auto vector{MakeArray<TypeCategory::Integer, 4>(
197       std::vector<int>{5}, std::vector<std::int32_t>{-1, -2, -3, -4, -5})};
198   RTNAME(Pack)(result, *array, *mask, &*vector, __FILE__, __LINE__);
199   EXPECT_EQ(result.type(), array->type());
200   EXPECT_EQ(result.rank(), 1);
201   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
202   EXPECT_EQ(result.GetDimension(0).Extent(), 5);
203   static std::int32_t packExpect2[5]{2, 3, 6, -4, -5};
204   for (int j{0}; j < 5; ++j) {
205     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect2[j])
206         << " at " << j;
207   }
208   result.Destroy();
209 }
210 
TEST(Transformational,Spread)211 TEST(Transformational, Spread) {
212   auto array{MakeArray<TypeCategory::Integer, 4>(
213       std::vector<int>{3}, std::vector<std::int32_t>{1, 2, 3})};
214   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
215   StaticDescriptor<2, true> statDesc;
216   Descriptor &result{statDesc.descriptor()};
217 
218   RTNAME(Spread)(result, *array, 1, 2, __FILE__, __LINE__);
219   EXPECT_EQ(result.type(), array->type());
220   EXPECT_EQ(result.rank(), 2);
221   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
222   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
223   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
224   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
225   for (int j{0}; j < 6; ++j) {
226     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j / 2);
227   }
228   result.Destroy();
229 
230   RTNAME(Spread)(result, *array, 2, 2, __FILE__, __LINE__);
231   EXPECT_EQ(result.type(), array->type());
232   EXPECT_EQ(result.rank(), 2);
233   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
234   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
235   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
236   EXPECT_EQ(result.GetDimension(1).Extent(), 2);
237   for (int j{0}; j < 6; ++j) {
238     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j % 3);
239   }
240   result.Destroy();
241 
242   auto scalar{MakeArray<TypeCategory::Integer, 4>(
243       std::vector<int>{}, std::vector<std::int32_t>{1})};
244   RTNAME(Spread)(result, *scalar, 1, 2, __FILE__, __LINE__);
245   EXPECT_EQ(result.type(), array->type());
246   EXPECT_EQ(result.rank(), 1);
247   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
248   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
249   for (int j{0}; j < 2; ++j) {
250     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1);
251   }
252   result.Destroy();
253 }
254 
TEST(Transformational,Transpose)255 TEST(Transformational, Transpose) {
256   // ARRAY  1 3 5
257   //        2 4 6
258   auto array{MakeArray<TypeCategory::Integer, 4>(
259       std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
260   array->GetDimension(0).SetLowerBound(2); // shouldn't matter
261   array->GetDimension(1).SetLowerBound(-6);
262   StaticDescriptor<2, true> statDesc;
263   Descriptor &result{statDesc.descriptor()};
264   RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
265   EXPECT_EQ(result.type(), array->type());
266   EXPECT_EQ(result.rank(), 2);
267   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
268   EXPECT_EQ(result.GetDimension(0).Extent(), 3);
269   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
270   EXPECT_EQ(result.GetDimension(1).Extent(), 2);
271   static std::int32_t expect[6]{1, 3, 5, 2, 4, 6};
272   for (int j{0}; j < 6; ++j) {
273     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
274   }
275   result.Destroy();
276 }
277 
TEST(Transformational,Unpack)278 TEST(Transformational, Unpack) {
279   auto vector{MakeArray<TypeCategory::Integer, 4>(
280       std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
281   vector->GetDimension(0).SetLowerBound(2); // shouldn't matter
282   auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
283       std::vector<std::uint8_t>{false, true, true, false, false, true})};
284   mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
285   mask->GetDimension(1).SetLowerBound(2);
286   auto field{MakeArray<TypeCategory::Integer, 4>(std::vector<int>{2, 3},
287       std::vector<std::int32_t>{-1, -2, -3, -4, -5, -6})};
288   field->GetDimension(0).SetLowerBound(-1); // shouldn't matter
289   StaticDescriptor<2, true> statDesc;
290   Descriptor &result{statDesc.descriptor()};
291   RTNAME(Unpack)(result, *vector, *mask, *field, __FILE__, __LINE__);
292   EXPECT_EQ(result.type(), vector->type());
293   EXPECT_EQ(result.rank(), 2);
294   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
295   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
296   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
297   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
298   static std::int32_t expect[6]{-1, 1, 2, -4, -5, 3};
299   for (int j{0}; j < 6; ++j) {
300     EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
301   }
302   result.Destroy();
303 
304   // Test for scalar value of the "field" argument
305   auto scalarField{MakeArray<TypeCategory::Integer, 4>(
306       std::vector<int>{}, std::vector<std::int32_t>{343})};
307   RTNAME(Unpack)(result, *vector, *mask, *scalarField, __FILE__, __LINE__);
308   EXPECT_EQ(result.rank(), 2);
309   EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
310   EXPECT_EQ(result.GetDimension(0).Extent(), 2);
311   EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
312   EXPECT_EQ(result.GetDimension(1).Extent(), 3);
313   static std::int32_t scalarExpect[6]{343, 1, 2, 343, 343, 3};
314   for (int j{0}; j < 6; ++j) {
315     EXPECT_EQ(
316         *result.ZeroBasedIndexedElement<std::int32_t>(j), scalarExpect[j]);
317   }
318   result.Destroy();
319 }
320