| 1 | //===-- unittests/Runtime/Transformational.cpp ------------------*- C++ -*-===// |
| 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 "tools.h" |
| 11 | #include "gtest/gtest.h" |
| 12 | #include "flang-rt/runtime/type-code.h" |
| 13 | #include "flang/Common/float128.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 | |
| 554 | TEST(Transformational, ShallowCopy) { |
| 555 | auto charArray{MakeArray<TypeCategory::Character, 1>(std::vector<int>{2, 3}, |
| 556 | std::vector<std::string>{"ab" , "cd" , "ef" , "gh" , "ij" , "kl" }, 2)}; |
| 557 | charArray->GetDimension(0).SetBounds(-1, 0); |
| 558 | charArray->GetDimension(1).SetBounds(3, 5); |
| 559 | StaticDescriptor<2> staticCharResult; |
| 560 | Descriptor &charResult{staticCharResult.descriptor()}; |
| 561 | |
| 562 | // Test allocating ShallowCopy. |
| 563 | RTNAME(ShallowCopy)(charResult, *charArray); |
| 564 | ASSERT_TRUE(charResult.IsAllocated()); |
| 565 | ASSERT_TRUE(charResult.IsContiguous()); |
| 566 | ASSERT_EQ(charResult.type(), charArray->type()); |
| 567 | ASSERT_EQ(charResult.ElementBytes(), 2u); |
| 568 | EXPECT_EQ(charResult.GetDimension(0).LowerBound(), 1); |
| 569 | EXPECT_EQ(charResult.GetDimension(0).Extent(), 2); |
| 570 | EXPECT_EQ(charResult.GetDimension(1).LowerBound(), 1); |
| 571 | EXPECT_EQ(charResult.GetDimension(1).Extent(), 3); |
| 572 | std::string expectedCharResult{"abcdefghijkl" }; |
| 573 | EXPECT_EQ(std::memcmp(charResult.OffsetElement<char>(0), |
| 574 | expectedCharResult.data(), expectedCharResult.length()), |
| 575 | 0); |
| 576 | |
| 577 | // Test ShallowCopyDirect with pre-allocated result. |
| 578 | char *allocatedPtr = charResult.OffsetElement<char>(0); |
| 579 | std::memset( |
| 580 | s: charResult.OffsetElement<char>(0), c: 'x', n: expectedCharResult.length()); |
| 581 | // Set new lower bounds for charResult. |
| 582 | charResult.GetDimension(0).SetBounds(-2, -1); |
| 583 | charResult.GetDimension(1).SetBounds(2, 4); |
| 584 | RTNAME(ShallowCopyDirect)(charResult, *charArray); |
| 585 | ASSERT_TRUE(charResult.IsAllocated()); |
| 586 | ASSERT_TRUE(charResult.IsContiguous()); |
| 587 | ASSERT_EQ(charResult.type(), charArray->type()); |
| 588 | ASSERT_EQ(charResult.ElementBytes(), 2u); |
| 589 | EXPECT_EQ(charResult.GetDimension(0).LowerBound(), -2); |
| 590 | EXPECT_EQ(charResult.GetDimension(0).Extent(), 2); |
| 591 | EXPECT_EQ(charResult.GetDimension(1).LowerBound(), 2); |
| 592 | EXPECT_EQ(charResult.GetDimension(1).Extent(), 3); |
| 593 | // Test that the result was not re-allocated. |
| 594 | EXPECT_EQ(allocatedPtr, charResult.OffsetElement<char>(0)); |
| 595 | EXPECT_EQ(std::memcmp(charResult.OffsetElement<char>(0), |
| 596 | expectedCharResult.data(), expectedCharResult.length()), |
| 597 | 0); |
| 598 | charResult.Destroy(); |
| 599 | |
| 600 | auto intScalar{MakeArray<TypeCategory::Integer, 4>( |
| 601 | std::vector<int>{}, std::vector<std::int32_t>{-1})}; |
| 602 | StaticDescriptor<0> staticIntResult; |
| 603 | Descriptor &intResult{staticIntResult.descriptor()}; |
| 604 | RTNAME(ShallowCopy)(intResult, *intScalar); |
| 605 | ASSERT_TRUE(intResult.IsAllocated()); |
| 606 | ASSERT_EQ(intResult.rank(), 0); |
| 607 | ASSERT_EQ(*intResult.ZeroBasedIndexedElement<std::int32_t>(0), -1); |
| 608 | *intResult.ZeroBasedIndexedElement<std::int32_t>(0) = 0; |
| 609 | allocatedPtr = intResult.OffsetElement<char>(0); |
| 610 | RTNAME(ShallowCopyDirect)(intResult, *intScalar); |
| 611 | ASSERT_TRUE(intResult.IsAllocated()); |
| 612 | ASSERT_EQ(intResult.rank(), 0); |
| 613 | ASSERT_EQ(*intResult.ZeroBasedIndexedElement<std::int32_t>(0), -1); |
| 614 | EXPECT_EQ(allocatedPtr, intResult.OffsetElement<char>(0)); |
| 615 | intResult.Destroy(); |
| 616 | } |
| 617 | |