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