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