1 //===-- runtime/reduction.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 // Implements ALL, ANY, COUNT, FINDLOC, IPARITY, MAXLOC, MAXVAL, MINLOC, MINVAL, 10 // PARITY, PRODUCT, and SUM for all required operand types and shapes and, 11 // for FINDLOC, MAXLOC, & MINLOC, kinds of results. 12 // 13 // * Real and complex SUM reductions attempt to reduce floating-point 14 // cancellation on intermediate results by adding up partial sums 15 // for positive and negative elements independently. 16 // * Partial reductions (i.e., those with DIM= arguments that are not 17 // required to be 1 by the rank of the argument) return arrays that 18 // are dynamically allocated in a caller-supplied descriptor. 19 // * Total reductions (i.e., no DIM= argument) with FINDLOC, MAXLOC, & MINLOC 20 // return integer vectors of some kind, not scalars; a caller-supplied 21 // descriptor is used 22 // * Character-valued reductions (MAXVAL & MINVAL) return arbitrary 23 // length results, dynamically allocated in a caller-supplied descriptor 24 25 #include "reduction.h" 26 #include "character.h" 27 #include "cpp-type.h" 28 #include "terminator.h" 29 #include "tools.h" 30 #include "flang/Common/long-double.h" 31 #include <cinttypes> 32 #include <complex> 33 #include <limits> 34 #include <type_traits> 35 36 namespace Fortran::runtime { 37 38 // Generic reduction templates 39 40 // Reductions are implemented with *accumulators*, which are instances of 41 // classes that incrementally build up the result (or an element thereof) during 42 // a traversal of the unmasked elements of an array. Each accumulator class 43 // supports a constructor (which captures a reference to the array), an 44 // AccumulateAt() member function that applies supplied subscripts to the 45 // array and does something with a scalar element, and a GetResult() 46 // member function that copies a final result into its destination. 47 48 // Total reduction of the array argument to a scalar (or to a vector in the 49 // cases of FINDLOC, MAXLOC, & MINLOC). These are the cases without DIM= or 50 // cases where the argument has rank 1 and DIM=, if present, must be 1. 51 template <typename TYPE, typename ACCUMULATOR> 52 inline void DoTotalReduction(const Descriptor &x, int dim, 53 const Descriptor *mask, ACCUMULATOR &accumulator, const char *intrinsic, 54 Terminator &terminator) { 55 if (dim < 0 || dim > 1) { 56 terminator.Crash( 57 "%s: bad DIM=%d for argument with rank %d", intrinsic, dim, x.rank()); 58 } 59 SubscriptValue xAt[maxRank]; 60 x.GetLowerBounds(xAt); 61 if (mask) { 62 CheckConformability(x, *mask, terminator, intrinsic, "ARRAY", "MASK"); 63 SubscriptValue maskAt[maxRank]; 64 mask->GetLowerBounds(maskAt); 65 if (mask->rank() > 0) { 66 for (auto elements{x.Elements()}; elements--; 67 x.IncrementSubscripts(xAt), mask->IncrementSubscripts(maskAt)) { 68 if (IsLogicalElementTrue(*mask, maskAt)) { 69 accumulator.template AccumulateAt<TYPE>(xAt); 70 } 71 } 72 return; 73 } else if (!IsLogicalElementTrue(*mask, maskAt)) { 74 // scalar MASK=.FALSE.: return identity value 75 return; 76 } 77 } 78 // No MASK=, or scalar MASK=.TRUE. 79 for (auto elements{x.Elements()}; elements--; x.IncrementSubscripts(xAt)) { 80 if (!accumulator.template AccumulateAt<TYPE>(xAt)) { 81 break; // cut short, result is known 82 } 83 } 84 } 85 86 template <TypeCategory CAT, int KIND, typename ACCUMULATOR> 87 inline CppTypeFor<CAT, KIND> GetTotalReduction(const Descriptor &x, 88 const char *source, int line, int dim, const Descriptor *mask, 89 ACCUMULATOR &&accumulator, const char *intrinsic) { 90 Terminator terminator{source, line}; 91 RUNTIME_CHECK(terminator, TypeCode(CAT, KIND) == x.type()); 92 using CppType = CppTypeFor<CAT, KIND>; 93 DoTotalReduction<CppType>(x, dim, mask, accumulator, intrinsic, terminator); 94 CppType result; 95 #ifdef _MSC_VER // work around MSVC spurious error 96 accumulator.GetResult(&result); 97 #else 98 accumulator.template GetResult(&result); 99 #endif 100 return result; 101 } 102 103 // For reductions on a dimension, e.g. SUM(array,DIM=2) where the shape 104 // of the array is [2,3,5], the shape of the result is [2,5] and 105 // result(j,k) = SUM(array(j,:,k)), possibly modified if the array has 106 // lower bounds other than one. This utility subroutine creates an 107 // array of subscripts [j,_,k] for result subscripts [j,k] so that the 108 // elemets of array(j,:,k) can be reduced. 109 inline void GetExpandedSubscripts(SubscriptValue at[], 110 const Descriptor &descriptor, int zeroBasedDim, 111 const SubscriptValue from[]) { 112 descriptor.GetLowerBounds(at); 113 int rank{descriptor.rank()}; 114 int j{0}; 115 for (; j < zeroBasedDim; ++j) { 116 at[j] += from[j] - 1 /*lower bound*/; 117 } 118 for (++j; j < rank; ++j) { 119 at[j] += from[j - 1] - 1; 120 } 121 } 122 123 template <typename TYPE, typename ACCUMULATOR> 124 inline void ReduceDimToScalar(const Descriptor &x, int zeroBasedDim, 125 SubscriptValue subscripts[], TYPE *result, ACCUMULATOR &accumulator) { 126 SubscriptValue xAt[maxRank]; 127 GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts); 128 const auto &dim{x.GetDimension(zeroBasedDim)}; 129 SubscriptValue at{dim.LowerBound()}; 130 for (auto n{dim.Extent()}; n-- > 0; ++at) { 131 xAt[zeroBasedDim] = at; 132 if (!accumulator.template AccumulateAt<TYPE>(xAt)) { 133 break; 134 } 135 } 136 #ifdef _MSC_VER // work around MSVC spurious error 137 accumulator.GetResult(result, zeroBasedDim); 138 #else 139 accumulator.template GetResult(result, zeroBasedDim); 140 #endif 141 } 142 143 template <typename TYPE, typename ACCUMULATOR> 144 inline void ReduceDimMaskToScalar(const Descriptor &x, int zeroBasedDim, 145 SubscriptValue subscripts[], const Descriptor &mask, TYPE *result, 146 ACCUMULATOR &accumulator) { 147 SubscriptValue xAt[maxRank], maskAt[maxRank]; 148 GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts); 149 GetExpandedSubscripts(maskAt, mask, zeroBasedDim, subscripts); 150 const auto &xDim{x.GetDimension(zeroBasedDim)}; 151 SubscriptValue xPos{xDim.LowerBound()}; 152 const auto &maskDim{mask.GetDimension(zeroBasedDim)}; 153 SubscriptValue maskPos{maskDim.LowerBound()}; 154 for (auto n{x.GetDimension(zeroBasedDim).Extent()}; n-- > 0; 155 ++xPos, ++maskPos) { 156 maskAt[zeroBasedDim] = maskPos; 157 if (IsLogicalElementTrue(mask, maskAt)) { 158 xAt[zeroBasedDim] = xPos; 159 if (!accumulator.template AccumulateAt<TYPE>(xAt)) { 160 break; 161 } 162 } 163 } 164 #ifdef _MSC_VER // work around MSVC spurious error 165 accumulator.GetResult(result, zeroBasedDim); 166 #else 167 accumulator.template GetResult(result, zeroBasedDim); 168 #endif 169 } 170 171 // Utility: establishes & allocates the result array for a partial 172 // reduction (i.e., one with DIM=). 173 static void CreatePartialReductionResult(Descriptor &result, 174 const Descriptor &x, int dim, Terminator &terminator, const char *intrinsic, 175 TypeCode typeCode) { 176 int xRank{x.rank()}; 177 if (dim < 1 || dim > xRank) { 178 terminator.Crash("%s: bad DIM=%d for rank %d", intrinsic, dim, xRank); 179 } 180 int zeroBasedDim{dim - 1}; 181 SubscriptValue resultExtent[maxRank]; 182 for (int j{0}; j < zeroBasedDim; ++j) { 183 resultExtent[j] = x.GetDimension(j).Extent(); 184 } 185 for (int j{zeroBasedDim + 1}; j < xRank; ++j) { 186 resultExtent[j - 1] = x.GetDimension(j).Extent(); 187 } 188 result.Establish(typeCode, x.ElementBytes(), nullptr, xRank - 1, resultExtent, 189 CFI_attribute_allocatable); 190 for (int j{0}; j + 1 < xRank; ++j) { 191 result.GetDimension(j).SetBounds(1, resultExtent[j]); 192 } 193 if (int stat{result.Allocate()}) { 194 terminator.Crash( 195 "%s: could not allocate memory for result; STAT=%d", intrinsic, stat); 196 } 197 } 198 199 // Partial reductions with DIM= 200 201 template <typename ACCUMULATOR, TypeCategory CAT, int KIND> 202 inline void PartialReduction(Descriptor &result, const Descriptor &x, int dim, 203 const Descriptor *mask, Terminator &terminator, const char *intrinsic, 204 ACCUMULATOR &accumulator) { 205 CreatePartialReductionResult( 206 result, x, dim, terminator, intrinsic, TypeCode{CAT, KIND}); 207 SubscriptValue at[maxRank]; 208 result.GetLowerBounds(at); 209 INTERNAL_CHECK(at[0] == 1); 210 using CppType = CppTypeFor<CAT, KIND>; 211 if (mask) { 212 CheckConformability(x, *mask, terminator, intrinsic, "ARRAY", "MASK"); 213 SubscriptValue maskAt[maxRank]; // contents unused 214 if (mask->rank() > 0) { 215 for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { 216 accumulator.Reinitialize(); 217 ReduceDimMaskToScalar<CppType, ACCUMULATOR>( 218 x, dim - 1, at, *mask, result.Element<CppType>(at), accumulator); 219 } 220 return; 221 } else if (!IsLogicalElementTrue(*mask, maskAt)) { 222 // scalar MASK=.FALSE. 223 accumulator.Reinitialize(); 224 for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { 225 accumulator.GetResult(result.Element<CppType>(at)); 226 } 227 return; 228 } 229 } 230 // No MASK= or scalar MASK=.TRUE. 231 for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { 232 accumulator.Reinitialize(); 233 ReduceDimToScalar<CppType, ACCUMULATOR>( 234 x, dim - 1, at, result.Element<CppType>(at), accumulator); 235 } 236 } 237 238 template <template <typename> class ACCUM> 239 struct PartialIntegerReductionHelper { 240 template <int KIND> struct Functor { 241 static constexpr int Intermediate{ 242 std::max(KIND, 4)}; // use at least "int" for intermediate results 243 void operator()(Descriptor &result, const Descriptor &x, int dim, 244 const Descriptor *mask, Terminator &terminator, 245 const char *intrinsic) const { 246 using Accumulator = 247 ACCUM<CppTypeFor<TypeCategory::Integer, Intermediate>>; 248 Accumulator accumulator{x}; 249 PartialReduction<Accumulator, TypeCategory::Integer, KIND>( 250 result, x, dim, mask, terminator, intrinsic, accumulator); 251 } 252 }; 253 }; 254 255 template <template <typename> class INTEGER_ACCUM> 256 inline void PartialIntegerReduction(Descriptor &result, const Descriptor &x, 257 int dim, int kind, const Descriptor *mask, const char *intrinsic, 258 Terminator &terminator) { 259 ApplyIntegerKind< 260 PartialIntegerReductionHelper<INTEGER_ACCUM>::template Functor, void>( 261 kind, terminator, result, x, dim, mask, terminator, intrinsic); 262 } 263 264 template <TypeCategory CAT, template <typename> class ACCUM> 265 struct PartialFloatingReductionHelper { 266 template <int KIND> struct Functor { 267 static constexpr int Intermediate{ 268 std::max(KIND, 8)}; // use at least "double" for intermediate results 269 void operator()(Descriptor &result, const Descriptor &x, int dim, 270 const Descriptor *mask, Terminator &terminator, 271 const char *intrinsic) const { 272 using Accumulator = ACCUM<CppTypeFor<TypeCategory::Real, Intermediate>>; 273 Accumulator accumulator{x}; 274 PartialReduction<Accumulator, CAT, KIND>( 275 result, x, dim, mask, terminator, intrinsic, accumulator); 276 } 277 }; 278 }; 279 280 template <template <typename> class INTEGER_ACCUM, 281 template <typename> class REAL_ACCUM, 282 template <typename> class COMPLEX_ACCUM> 283 inline void TypedPartialNumericReduction(Descriptor &result, 284 const Descriptor &x, int dim, const char *source, int line, 285 const Descriptor *mask, const char *intrinsic) { 286 Terminator terminator{source, line}; 287 auto catKind{x.type().GetCategoryAndKind()}; 288 RUNTIME_CHECK(terminator, catKind.has_value()); 289 switch (catKind->first) { 290 case TypeCategory::Integer: 291 PartialIntegerReduction<INTEGER_ACCUM>( 292 result, x, dim, catKind->second, mask, intrinsic, terminator); 293 break; 294 case TypeCategory::Real: 295 ApplyFloatingPointKind<PartialFloatingReductionHelper<TypeCategory::Real, 296 REAL_ACCUM>::template Functor, 297 void>(catKind->second, terminator, result, x, dim, mask, terminator, 298 intrinsic); 299 break; 300 case TypeCategory::Complex: 301 ApplyFloatingPointKind<PartialFloatingReductionHelper<TypeCategory::Complex, 302 COMPLEX_ACCUM>::template Functor, 303 void>(catKind->second, terminator, result, x, dim, mask, terminator, 304 intrinsic); 305 break; 306 default: 307 terminator.Crash("%s: invalid type code %d", intrinsic, x.type().raw()); 308 } 309 } 310 311 // SUM() 312 313 template <typename INTERMEDIATE> class IntegerSumAccumulator { 314 public: 315 explicit IntegerSumAccumulator(const Descriptor &array) : array_{array} {} 316 void Reinitialize() { sum_ = 0; } 317 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 318 *p = static_cast<A>(sum_); 319 } 320 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 321 sum_ += *array_.Element<A>(at); 322 return true; 323 } 324 325 private: 326 const Descriptor &array_; 327 INTERMEDIATE sum_{0}; 328 }; 329 330 template <typename INTERMEDIATE> class RealSumAccumulator { 331 public: 332 explicit RealSumAccumulator(const Descriptor &array) : array_{array} {} 333 void Reinitialize() { positives_ = negatives_ = inOrder_ = 0; } 334 template <typename A> A Result() const { 335 auto sum{static_cast<A>(positives_ + negatives_)}; 336 return std::isfinite(sum) ? sum : static_cast<A>(inOrder_); 337 } 338 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 339 *p = Result<A>(); 340 } 341 template <typename A> bool Accumulate(A x) { 342 // Accumulate the nonnegative and negative elements independently 343 // to reduce cancellation; also record an in-order sum for use 344 // in case of overflow. 345 if (x >= 0) { 346 positives_ += x; 347 } else { 348 negatives_ += x; 349 } 350 inOrder_ += x; 351 return true; 352 } 353 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 354 return Accumulate(*array_.Element<A>(at)); 355 } 356 357 private: 358 const Descriptor &array_; 359 INTERMEDIATE positives_{0.0}, negatives_{0.0}, inOrder_{0.0}; 360 }; 361 362 template <typename PART> class ComplexSumAccumulator { 363 public: 364 explicit ComplexSumAccumulator(const Descriptor &array) : array_{array} {} 365 void Reinitialize() { 366 reals_.Reinitialize(); 367 imaginaries_.Reinitialize(); 368 } 369 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 370 using ResultPart = typename A::value_type; 371 *p = {reals_.template Result<ResultPart>(), 372 imaginaries_.template Result<ResultPart>()}; 373 } 374 template <typename A> bool Accumulate(const A &z) { 375 reals_.Accumulate(z.real()); 376 imaginaries_.Accumulate(z.imag()); 377 return true; 378 } 379 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 380 return Accumulate(*array_.Element<A>(at)); 381 } 382 383 private: 384 const Descriptor &array_; 385 RealSumAccumulator<PART> reals_{array_}, imaginaries_{array_}; 386 }; 387 388 extern "C" { 389 CppTypeFor<TypeCategory::Integer, 1> RTNAME(SumInteger1)(const Descriptor &x, 390 const char *source, int line, int dim, const Descriptor *mask) { 391 return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask, 392 IntegerSumAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "SUM"); 393 } 394 CppTypeFor<TypeCategory::Integer, 2> RTNAME(SumInteger2)(const Descriptor &x, 395 const char *source, int line, int dim, const Descriptor *mask) { 396 return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask, 397 IntegerSumAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "SUM"); 398 } 399 CppTypeFor<TypeCategory::Integer, 4> RTNAME(SumInteger4)(const Descriptor &x, 400 const char *source, int line, int dim, const Descriptor *mask) { 401 return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask, 402 IntegerSumAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, "SUM"); 403 } 404 CppTypeFor<TypeCategory::Integer, 8> RTNAME(SumInteger8)(const Descriptor &x, 405 const char *source, int line, int dim, const Descriptor *mask) { 406 return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask, 407 IntegerSumAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, "SUM"); 408 } 409 #ifdef __SIZEOF_INT128__ 410 CppTypeFor<TypeCategory::Integer, 16> RTNAME(SumInteger16)(const Descriptor &x, 411 const char *source, int line, int dim, const Descriptor *mask) { 412 return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim, 413 mask, IntegerSumAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x}, 414 "SUM"); 415 } 416 #endif 417 418 // TODO: real/complex(2 & 3) 419 CppTypeFor<TypeCategory::Real, 4> RTNAME(SumReal4)(const Descriptor &x, 420 const char *source, int line, int dim, const Descriptor *mask) { 421 return GetTotalReduction<TypeCategory::Real, 4>( 422 x, source, line, dim, mask, RealSumAccumulator<double>{x}, "SUM"); 423 } 424 CppTypeFor<TypeCategory::Real, 8> RTNAME(SumReal8)(const Descriptor &x, 425 const char *source, int line, int dim, const Descriptor *mask) { 426 return GetTotalReduction<TypeCategory::Real, 8>( 427 x, source, line, dim, mask, RealSumAccumulator<double>{x}, "SUM"); 428 } 429 #if LONG_DOUBLE == 80 430 CppTypeFor<TypeCategory::Real, 10> RTNAME(SumReal10)(const Descriptor &x, 431 const char *source, int line, int dim, const Descriptor *mask) { 432 return GetTotalReduction<TypeCategory::Real, 10>( 433 x, source, line, dim, mask, RealSumAccumulator<long double>{x}, "SUM"); 434 } 435 #elif LONG_DOUBLE == 128 436 CppTypeFor<TypeCategory::Real, 16> RTNAME(SumReal16)(const Descriptor &x, 437 const char *source, int line, int dim, const Descriptor *mask) { 438 return GetTotalReduction<TypeCategory::Real, 16>( 439 x, source, line, dim, mask, RealSumAccumulator<long double>{x}, "SUM"); 440 } 441 #endif 442 443 void RTNAME(CppSumComplex4)(CppTypeFor<TypeCategory::Complex, 4> &result, 444 const Descriptor &x, const char *source, int line, int dim, 445 const Descriptor *mask) { 446 result = GetTotalReduction<TypeCategory::Complex, 4>( 447 x, source, line, dim, mask, ComplexSumAccumulator<double>{x}, "SUM"); 448 } 449 void RTNAME(CppSumComplex8)(CppTypeFor<TypeCategory::Complex, 8> &result, 450 const Descriptor &x, const char *source, int line, int dim, 451 const Descriptor *mask) { 452 result = GetTotalReduction<TypeCategory::Complex, 8>( 453 x, source, line, dim, mask, ComplexSumAccumulator<double>{x}, "SUM"); 454 } 455 #if LONG_DOUBLE == 80 456 void RTNAME(CppSumComplex10)(CppTypeFor<TypeCategory::Complex, 10> &result, 457 const Descriptor &x, const char *source, int line, int dim, 458 const Descriptor *mask) { 459 result = GetTotalReduction<TypeCategory::Complex, 10>( 460 x, source, line, dim, mask, ComplexSumAccumulator<long double>{x}, "SUM"); 461 } 462 #elif LONG_DOUBLE == 128 463 void RTNAME(CppSumComplex16)(CppTypeFor<TypeCategory::Complex, 16> &result, 464 const Descriptor &x, const char *source, int line, int dim, 465 const Descriptor *mask) { 466 result = GetTotalReduction<TypeCategory::Complex, 16>( 467 x, source, line, dim, mask, ComplexSumAccumulator<long double>{x}, "SUM"); 468 } 469 #endif 470 471 void RTNAME(SumDim)(Descriptor &result, const Descriptor &x, int dim, 472 const char *source, int line, const Descriptor *mask) { 473 TypedPartialNumericReduction<IntegerSumAccumulator, RealSumAccumulator, 474 ComplexSumAccumulator>(result, x, dim, source, line, mask, "SUM"); 475 } 476 } // extern "C" 477 478 // PRODUCT() 479 480 template <typename INTERMEDIATE> class NonComplexProductAccumulator { 481 public: 482 explicit NonComplexProductAccumulator(const Descriptor &array) 483 : array_{array} {} 484 void Reinitialize() { product_ = 1; } 485 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 486 *p = static_cast<A>(product_); 487 } 488 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 489 product_ *= *array_.Element<A>(at); 490 return product_ != 0; 491 } 492 493 private: 494 const Descriptor &array_; 495 INTERMEDIATE product_{1}; 496 }; 497 498 template <typename PART> class ComplexProductAccumulator { 499 public: 500 explicit ComplexProductAccumulator(const Descriptor &array) : array_{array} {} 501 void Reinitialize() { product_ = std::complex<PART>{1, 0}; } 502 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 503 using ResultPart = typename A::value_type; 504 *p = {static_cast<ResultPart>(product_.real()), 505 static_cast<ResultPart>(product_.imag())}; 506 } 507 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 508 product_ *= *array_.Element<A>(at); 509 return true; 510 } 511 512 private: 513 const Descriptor &array_; 514 std::complex<PART> product_{1, 0}; 515 }; 516 517 extern "C" { 518 CppTypeFor<TypeCategory::Integer, 1> RTNAME(ProductInteger1)( 519 const Descriptor &x, const char *source, int line, int dim, 520 const Descriptor *mask) { 521 return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask, 522 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 523 "PRODUCT"); 524 } 525 CppTypeFor<TypeCategory::Integer, 2> RTNAME(ProductInteger2)( 526 const Descriptor &x, const char *source, int line, int dim, 527 const Descriptor *mask) { 528 return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask, 529 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 530 "PRODUCT"); 531 } 532 CppTypeFor<TypeCategory::Integer, 4> RTNAME(ProductInteger4)( 533 const Descriptor &x, const char *source, int line, int dim, 534 const Descriptor *mask) { 535 return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask, 536 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 537 "PRODUCT"); 538 } 539 CppTypeFor<TypeCategory::Integer, 8> RTNAME(ProductInteger8)( 540 const Descriptor &x, const char *source, int line, int dim, 541 const Descriptor *mask) { 542 return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask, 543 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, 544 "PRODUCT"); 545 } 546 #ifdef __SIZEOF_INT128__ 547 CppTypeFor<TypeCategory::Integer, 16> RTNAME(ProductInteger16)( 548 const Descriptor &x, const char *source, int line, int dim, 549 const Descriptor *mask) { 550 return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim, 551 mask, 552 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x}, 553 "PRODUCT"); 554 } 555 #endif 556 557 // TODO: real/complex(2 & 3) 558 CppTypeFor<TypeCategory::Real, 4> RTNAME(ProductReal4)(const Descriptor &x, 559 const char *source, int line, int dim, const Descriptor *mask) { 560 return GetTotalReduction<TypeCategory::Real, 4>(x, source, line, dim, mask, 561 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, 562 "PRODUCT"); 563 } 564 CppTypeFor<TypeCategory::Real, 8> RTNAME(ProductReal8)(const Descriptor &x, 565 const char *source, int line, int dim, const Descriptor *mask) { 566 return GetTotalReduction<TypeCategory::Real, 8>(x, source, line, dim, mask, 567 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, 568 "PRODUCT"); 569 } 570 #if LONG_DOUBLE == 80 571 CppTypeFor<TypeCategory::Real, 10> RTNAME(ProductReal10)(const Descriptor &x, 572 const char *source, int line, int dim, const Descriptor *mask) { 573 return GetTotalReduction<TypeCategory::Real, 10>(x, source, line, dim, mask, 574 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x}, 575 "PRODUCT"); 576 } 577 #elif LONG_DOUBLE == 128 578 CppTypeFor<TypeCategory::Real, 16> RTNAME(ProductReal16)(const Descriptor &x, 579 const char *source, int line, int dim, const Descriptor *mask) { 580 return GetTotalReduction<TypeCategory::Real, 16>(x, source, line, dim, mask, 581 NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x}, 582 "PRODUCT"); 583 } 584 #endif 585 586 void RTNAME(CppProductComplex4)(CppTypeFor<TypeCategory::Complex, 4> &result, 587 const Descriptor &x, const char *source, int line, int dim, 588 const Descriptor *mask) { 589 result = GetTotalReduction<TypeCategory::Complex, 4>(x, source, line, dim, 590 mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, 591 "PRODUCT"); 592 } 593 void RTNAME(CppProductComplex8)(CppTypeFor<TypeCategory::Complex, 8> &result, 594 const Descriptor &x, const char *source, int line, int dim, 595 const Descriptor *mask) { 596 result = GetTotalReduction<TypeCategory::Complex, 8>(x, source, line, dim, 597 mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, 598 "PRODUCT"); 599 } 600 #if LONG_DOUBLE == 80 601 void RTNAME(CppProductComplex10)(CppTypeFor<TypeCategory::Complex, 10> &result, 602 const Descriptor &x, const char *source, int line, int dim, 603 const Descriptor *mask) { 604 result = GetTotalReduction<TypeCategory::Complex, 10>(x, source, line, dim, 605 mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x}, 606 "PRODUCT"); 607 } 608 #elif LONG_DOUBLE == 128 609 void RTNAME(CppProductComplex16)(CppTypeFor<TypeCategory::Complex, 16> &result, 610 const Descriptor &x, const char *source, int line, int dim, 611 const Descriptor *mask) { 612 result = GetTotalReduction<TypeCategory::Complex, 16>(x, source, line, dim, 613 mask, ComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x}, 614 "PRODUCT"); 615 } 616 #endif 617 618 void RTNAME(ProductDim)(Descriptor &result, const Descriptor &x, int dim, 619 const char *source, int line, const Descriptor *mask) { 620 TypedPartialNumericReduction<NonComplexProductAccumulator, 621 NonComplexProductAccumulator, ComplexProductAccumulator>( 622 result, x, dim, source, line, mask, "PRODUCT"); 623 } 624 } // extern "C" 625 626 // IPARITY() 627 628 template <typename INTERMEDIATE> class IntegerXorAccumulator { 629 public: 630 explicit IntegerXorAccumulator(const Descriptor &array) : array_{array} {} 631 void Reinitialize() { xor_ = 0; } 632 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 633 *p = static_cast<A>(xor_); 634 } 635 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 636 xor_ ^= *array_.Element<A>(at); 637 return true; 638 } 639 640 private: 641 const Descriptor &array_; 642 INTERMEDIATE xor_{0}; 643 }; 644 645 extern "C" { 646 CppTypeFor<TypeCategory::Integer, 1> RTNAME(IParity1)(const Descriptor &x, 647 const char *source, int line, int dim, const Descriptor *mask) { 648 return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask, 649 IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 650 "IPARITY"); 651 } 652 CppTypeFor<TypeCategory::Integer, 2> RTNAME(IParity2)(const Descriptor &x, 653 const char *source, int line, int dim, const Descriptor *mask) { 654 return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask, 655 IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 656 "IPARITY"); 657 } 658 CppTypeFor<TypeCategory::Integer, 4> RTNAME(IParity4)(const Descriptor &x, 659 const char *source, int line, int dim, const Descriptor *mask) { 660 return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask, 661 IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x}, 662 "IPARITY"); 663 } 664 CppTypeFor<TypeCategory::Integer, 8> RTNAME(IParity8)(const Descriptor &x, 665 const char *source, int line, int dim, const Descriptor *mask) { 666 return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask, 667 IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x}, 668 "IPARITY"); 669 } 670 #ifdef __SIZEOF_INT128__ 671 CppTypeFor<TypeCategory::Integer, 16> RTNAME(IParity16)(const Descriptor &x, 672 const char *source, int line, int dim, const Descriptor *mask) { 673 return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim, 674 mask, IntegerXorAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x}, 675 "IPARITY"); 676 } 677 #endif 678 void RTNAME(IParityDim)(Descriptor &result, const Descriptor &x, int dim, 679 const char *source, int line, const Descriptor *mask) { 680 Terminator terminator{source, line}; 681 auto catKind{x.type().GetCategoryAndKind()}; 682 RUNTIME_CHECK(terminator, 683 catKind.has_value() && catKind->first == TypeCategory::Integer); 684 PartialIntegerReduction<IntegerXorAccumulator>( 685 result, x, dim, catKind->second, mask, "IPARITY", terminator); 686 } 687 } 688 689 // MAXLOC & MINLOC 690 691 template <typename T, bool IS_MAX, bool BACK> struct NumericCompare { 692 using Type = T; 693 explicit NumericCompare(std::size_t /*elemLen; ignored*/) {} 694 bool operator()(const T &value, const T &previous) const { 695 if (value == previous) { 696 return BACK; 697 } else if constexpr (IS_MAX) { 698 return value > previous; 699 } else { 700 return value < previous; 701 } 702 } 703 }; 704 705 template <typename T, bool IS_MAX, bool BACK> class CharacterCompare { 706 public: 707 using Type = T; 708 explicit CharacterCompare(std::size_t elemLen) 709 : chars_{elemLen / sizeof(T)} {} 710 bool operator()(const T &value, const T &previous) const { 711 int cmp{CharacterScalarCompare<T>(&value, &previous, chars_, chars_)}; 712 if (cmp == 0) { 713 return BACK; 714 } else if constexpr (IS_MAX) { 715 return cmp > 0; 716 } else { 717 return cmp < 0; 718 } 719 } 720 721 private: 722 std::size_t chars_; 723 }; 724 725 template <typename COMPARE> class ExtremumLocAccumulator { 726 public: 727 using Type = typename COMPARE::Type; 728 ExtremumLocAccumulator(const Descriptor &array, std::size_t chars = 0) 729 : array_{array}, argRank_{array.rank()}, compare_{array.ElementBytes()} { 730 Reinitialize(); 731 } 732 void Reinitialize() { 733 // per standard: result indices are all zero if no data 734 for (int j{0}; j < argRank_; ++j) { 735 extremumLoc_[j] = 0; 736 } 737 previous_ = nullptr; 738 } 739 int argRank() const { return argRank_; } 740 template <typename A> void GetResult(A *p, int zeroBasedDim = -1) { 741 if (zeroBasedDim >= 0) { 742 *p = extremumLoc_[zeroBasedDim]; 743 } else { 744 for (int j{0}; j < argRank_; ++j) { 745 p[j] = extremumLoc_[j]; 746 } 747 } 748 } 749 template <typename IGNORED> bool AccumulateAt(const SubscriptValue at[]) { 750 const auto &value{*array_.Element<Type>(at)}; 751 if (!previous_ || compare_(value, *previous_)) { 752 previous_ = &value; 753 for (int j{0}; j < argRank_; ++j) { 754 extremumLoc_[j] = at[j]; 755 } 756 } 757 return true; 758 } 759 760 private: 761 const Descriptor &array_; 762 int argRank_; 763 SubscriptValue extremumLoc_[maxRank]; 764 const Type *previous_{nullptr}; 765 COMPARE compare_; 766 }; 767 768 template <typename ACCUMULATOR> struct LocationResultHelper { 769 template <int KIND> struct Functor { 770 void operator()(ACCUMULATOR &accumulator, const Descriptor &result) const { 771 accumulator.GetResult( 772 result.OffsetElement<CppTypeFor<TypeCategory::Integer, KIND>>()); 773 } 774 }; 775 }; 776 777 template <typename ACCUMULATOR, typename CPPTYPE> 778 static void LocationHelper(const char *intrinsic, Descriptor &result, 779 const Descriptor &x, int kind, const Descriptor *mask, 780 Terminator &terminator) { 781 ACCUMULATOR accumulator{x}; 782 DoTotalReduction<CPPTYPE>(x, 0, mask, accumulator, intrinsic, terminator); 783 ApplyIntegerKind<LocationResultHelper<ACCUMULATOR>::template Functor, void>( 784 kind, terminator, accumulator, result); 785 } 786 787 template <TypeCategory CAT, int KIND, bool IS_MAX, 788 template <typename, bool, bool> class COMPARE> 789 inline void DoMaxOrMinLoc(const char *intrinsic, Descriptor &result, 790 const Descriptor &x, int kind, const char *source, int line, 791 const Descriptor *mask, bool back) { 792 using CppType = CppTypeFor<CAT, KIND>; 793 Terminator terminator{source, line}; 794 if (back) { 795 LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, true>>, 796 CppType>(intrinsic, result, x, kind, mask, terminator); 797 } else { 798 LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, false>>, 799 CppType>(intrinsic, result, x, kind, mask, terminator); 800 } 801 } 802 803 template <TypeCategory CAT, bool IS_MAX> struct TypedMaxOrMinLocHelper { 804 template <int KIND> struct Functor { 805 void operator()(const char *intrinsic, Descriptor &result, 806 const Descriptor &x, int kind, const char *source, int line, 807 const Descriptor *mask, bool back) const { 808 DoMaxOrMinLoc<TypeCategory::Integer, KIND, IS_MAX, NumericCompare>( 809 intrinsic, result, x, kind, source, line, mask, back); 810 } 811 }; 812 }; 813 814 template <bool IS_MAX> 815 inline void TypedMaxOrMinLoc(const char *intrinsic, Descriptor &result, 816 const Descriptor &x, int kind, const char *source, int line, 817 const Descriptor *mask, bool back) { 818 int rank{x.rank()}; 819 SubscriptValue extent[1]{rank}; 820 result.Establish(TypeCategory::Integer, kind, nullptr, 1, extent, 821 CFI_attribute_allocatable); 822 result.GetDimension(0).SetBounds(1, extent[0]); 823 Terminator terminator{source, line}; 824 if (int stat{result.Allocate()}) { 825 terminator.Crash( 826 "%s: could not allocate memory for result; STAT=%d", intrinsic, stat); 827 } 828 CheckIntegerKind(terminator, kind, intrinsic); 829 auto catKind{x.type().GetCategoryAndKind()}; 830 RUNTIME_CHECK(terminator, catKind.has_value()); 831 switch (catKind->first) { 832 case TypeCategory::Integer: 833 ApplyIntegerKind< 834 TypedMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX>::template Functor, 835 void>(catKind->second, terminator, intrinsic, result, x, kind, source, 836 line, mask, back); 837 break; 838 case TypeCategory::Real: 839 ApplyFloatingPointKind< 840 TypedMaxOrMinLocHelper<TypeCategory::Real, IS_MAX>::template Functor, 841 void>(catKind->second, terminator, intrinsic, result, x, kind, source, 842 line, mask, back); 843 break; 844 case TypeCategory::Character: 845 ApplyCharacterKind<TypedMaxOrMinLocHelper<TypeCategory::Character, 846 IS_MAX>::template Functor, 847 void>(catKind->second, terminator, intrinsic, result, x, kind, source, 848 line, mask, back); 849 break; 850 default: 851 terminator.Crash( 852 "%s: Bad data type code (%d) for array", intrinsic, x.type().raw()); 853 } 854 } 855 856 extern "C" { 857 void RTNAME(Maxloc)(Descriptor &result, const Descriptor &x, int kind, 858 const char *source, int line, const Descriptor *mask, bool back) { 859 TypedMaxOrMinLoc<true>("MAXLOC", result, x, kind, source, line, mask, back); 860 } 861 void RTNAME(Minloc)(Descriptor &result, const Descriptor &x, int kind, 862 const char *source, int line, const Descriptor *mask, bool back) { 863 TypedMaxOrMinLoc<false>("MINLOC", result, x, kind, source, line, mask, back); 864 } 865 } // extern "C" 866 867 // MAXLOC/MINLOC with DIM= 868 869 template <typename ACCUMULATOR> struct PartialLocationHelper { 870 template <int KIND> struct Functor { 871 void operator()(Descriptor &result, const Descriptor &x, int dim, 872 const Descriptor *mask, Terminator &terminator, const char *intrinsic, 873 ACCUMULATOR &accumulator) const { 874 PartialReduction<ACCUMULATOR, TypeCategory::Integer, KIND>( 875 result, x, dim, mask, terminator, intrinsic, accumulator); 876 } 877 }; 878 }; 879 880 template <TypeCategory CAT, int KIND, bool IS_MAX, 881 template <typename, bool, bool> class COMPARE, bool BACK> 882 static void DoPartialMaxOrMinLocDirection(const char *intrinsic, 883 Descriptor &result, const Descriptor &x, int kind, int dim, 884 const Descriptor *mask, Terminator &terminator) { 885 using CppType = CppTypeFor<CAT, KIND>; 886 using Accumulator = ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, BACK>>; 887 Accumulator accumulator{x}; 888 ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, void>( 889 kind, terminator, result, x, dim, mask, terminator, intrinsic, 890 accumulator); 891 } 892 893 template <TypeCategory CAT, int KIND, bool IS_MAX, 894 template <typename, bool, bool> class COMPARE> 895 inline void DoPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result, 896 const Descriptor &x, int kind, int dim, const Descriptor *mask, bool back, 897 Terminator &terminator) { 898 if (back) { 899 DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, true>( 900 intrinsic, result, x, kind, dim, mask, terminator); 901 } else { 902 DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, false>( 903 intrinsic, result, x, kind, dim, mask, terminator); 904 } 905 } 906 907 template <TypeCategory CAT, bool IS_MAX, 908 template <typename, bool, bool> class COMPARE> 909 struct DoPartialMaxOrMinLocHelper { 910 template <int KIND> struct Functor { 911 void operator()(const char *intrinsic, Descriptor &result, 912 const Descriptor &x, int kind, int dim, const Descriptor *mask, 913 bool back, Terminator &terminator) const { 914 DoPartialMaxOrMinLoc<CAT, KIND, IS_MAX, COMPARE>( 915 intrinsic, result, x, kind, dim, mask, back, terminator); 916 } 917 }; 918 }; 919 920 template <bool IS_MAX> 921 inline void TypedPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result, 922 const Descriptor &x, int kind, int dim, const char *source, int line, 923 const Descriptor *mask, bool back) { 924 Terminator terminator{source, line}; 925 CheckIntegerKind(terminator, kind, intrinsic); 926 auto catKind{x.type().GetCategoryAndKind()}; 927 RUNTIME_CHECK(terminator, catKind.has_value()); 928 switch (catKind->first) { 929 case TypeCategory::Integer: 930 ApplyIntegerKind<DoPartialMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX, 931 NumericCompare>::template Functor, 932 void>(catKind->second, terminator, intrinsic, result, x, kind, dim, 933 mask, back, terminator); 934 break; 935 case TypeCategory::Real: 936 ApplyFloatingPointKind<DoPartialMaxOrMinLocHelper<TypeCategory::Real, 937 IS_MAX, NumericCompare>::template Functor, 938 void>(catKind->second, terminator, intrinsic, result, x, kind, dim, 939 mask, back, terminator); 940 break; 941 case TypeCategory::Character: 942 ApplyCharacterKind<DoPartialMaxOrMinLocHelper<TypeCategory::Character, 943 IS_MAX, CharacterCompare>::template Functor, 944 void>(catKind->second, terminator, intrinsic, result, x, kind, dim, 945 mask, back, terminator); 946 break; 947 default: 948 terminator.Crash( 949 "%s: Bad data type code (%d) for array", intrinsic, x.type().raw()); 950 } 951 } 952 953 extern "C" { 954 void RTNAME(MaxlocDim)(Descriptor &result, const Descriptor &x, int kind, 955 int dim, const char *source, int line, const Descriptor *mask, bool back) { 956 TypedPartialMaxOrMinLoc<true>( 957 "MAXLOC", result, x, kind, dim, source, line, mask, back); 958 } 959 void RTNAME(MinlocDim)(Descriptor &result, const Descriptor &x, int kind, 960 int dim, const char *source, int line, const Descriptor *mask, bool back) { 961 TypedPartialMaxOrMinLoc<false>( 962 "MINLOC", result, x, kind, dim, source, line, mask, back); 963 } 964 } // extern "C" 965 966 // FINDLOC 967 968 template <TypeCategory CAT1, int KIND1, TypeCategory CAT2, int KIND2> 969 struct Equality { 970 using Type1 = CppTypeFor<CAT1, KIND1>; 971 using Type2 = CppTypeFor<CAT2, KIND2>; 972 bool operator()(const Descriptor &array, const SubscriptValue at[], 973 const Descriptor &target) const { 974 return *array.Element<Type1>(at) == *target.OffsetElement<Type2>(); 975 } 976 }; 977 978 template <int KIND1, int KIND2> 979 struct Equality<TypeCategory::Complex, KIND1, TypeCategory::Complex, KIND2> { 980 using Type1 = CppTypeFor<TypeCategory::Complex, KIND1>; 981 using Type2 = CppTypeFor<TypeCategory::Complex, KIND2>; 982 bool operator()(const Descriptor &array, const SubscriptValue at[], 983 const Descriptor &target) const { 984 const Type1 &xz{*array.Element<Type1>(at)}; 985 const Type2 &tz{*target.OffsetElement<Type2>()}; 986 return xz.real() == tz.real() && xz.imag() == tz.imag(); 987 } 988 }; 989 990 template <int KIND1, TypeCategory CAT2, int KIND2> 991 struct Equality<TypeCategory::Complex, KIND1, CAT2, KIND2> { 992 using Type1 = CppTypeFor<TypeCategory::Complex, KIND1>; 993 using Type2 = CppTypeFor<CAT2, KIND2>; 994 bool operator()(const Descriptor &array, const SubscriptValue at[], 995 const Descriptor &target) const { 996 const Type1 &z{*array.Element<Type1>(at)}; 997 return z.imag() == 0 && z.real() == *target.OffsetElement<Type2>(); 998 } 999 }; 1000 1001 template <TypeCategory CAT1, int KIND1, int KIND2> 1002 struct Equality<CAT1, KIND1, TypeCategory::Complex, KIND2> { 1003 using Type1 = CppTypeFor<CAT1, KIND1>; 1004 using Type2 = CppTypeFor<TypeCategory::Complex, KIND2>; 1005 bool operator()(const Descriptor &array, const SubscriptValue at[], 1006 const Descriptor &target) const { 1007 const Type2 &z{*target.OffsetElement<Type2>()}; 1008 return *array.Element<Type1>(at) == z.real() && z.imag() == 0; 1009 } 1010 }; 1011 1012 template <int KIND> struct CharacterEquality { 1013 using Type = CppTypeFor<TypeCategory::Character, KIND>; 1014 bool operator()(const Descriptor &array, const SubscriptValue at[], 1015 const Descriptor &target) const { 1016 return CharacterScalarCompare<Type>(array.Element<Type>(at), 1017 target.OffsetElement<Type>(), 1018 array.ElementBytes() / static_cast<unsigned>(KIND), 1019 target.ElementBytes() / static_cast<unsigned>(KIND)) == 0; 1020 } 1021 }; 1022 1023 struct LogicalEquivalence { 1024 bool operator()(const Descriptor &array, const SubscriptValue at[], 1025 const Descriptor &target) const { 1026 return IsLogicalElementTrue(array, at) == 1027 IsLogicalElementTrue(target, at /*ignored*/); 1028 } 1029 }; 1030 1031 template <typename EQUALITY> class LocationAccumulator { 1032 public: 1033 LocationAccumulator( 1034 const Descriptor &array, const Descriptor &target, bool back) 1035 : array_{array}, target_{target}, back_{back} { 1036 Reinitialize(); 1037 } 1038 void Reinitialize() { 1039 // per standard: result indices are all zero if no data 1040 for (int j{0}; j < rank_; ++j) { 1041 location_[j] = 0; 1042 } 1043 } 1044 template <typename A> void GetResult(A *p, int zeroBasedDim = -1) { 1045 if (zeroBasedDim >= 0) { 1046 *p = location_[zeroBasedDim]; 1047 } else { 1048 for (int j{0}; j < rank_; ++j) { 1049 p[j] = location_[j]; 1050 } 1051 } 1052 } 1053 template <typename IGNORED> bool AccumulateAt(const SubscriptValue at[]) { 1054 if (equality_(array_, at, target_)) { 1055 for (int j{0}; j < rank_; ++j) { 1056 location_[j] = at[j]; 1057 } 1058 return back_; 1059 } else { 1060 return true; 1061 } 1062 } 1063 1064 private: 1065 const Descriptor &array_; 1066 const Descriptor &target_; 1067 const bool back_{false}; 1068 const int rank_{array_.rank()}; 1069 SubscriptValue location_[maxRank]; 1070 const EQUALITY equality_{}; 1071 }; 1072 1073 template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> 1074 struct TotalNumericFindlocHelper { 1075 template <int TARGET_KIND> struct Functor { 1076 void operator()(Descriptor &result, const Descriptor &x, 1077 const Descriptor &target, int kind, int dim, const Descriptor *mask, 1078 bool back, Terminator &terminator) const { 1079 using Eq = Equality<XCAT, XKIND, TARGET_CAT, TARGET_KIND>; 1080 using Accumulator = LocationAccumulator<Eq>; 1081 Accumulator accumulator{x, target, back}; 1082 DoTotalReduction<void>(x, dim, mask, accumulator, "FINDLOC", terminator); 1083 ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, 1084 void>(kind, terminator, accumulator, result); 1085 } 1086 }; 1087 }; 1088 1089 template <TypeCategory CAT, 1090 template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> 1091 class HELPER> 1092 struct NumericFindlocHelper { 1093 template <int KIND> struct Functor { 1094 void operator()(TypeCategory targetCat, int targetKind, Descriptor &result, 1095 const Descriptor &x, const Descriptor &target, int kind, int dim, 1096 const Descriptor *mask, bool back, Terminator &terminator) const { 1097 switch (targetCat) { 1098 case TypeCategory::Integer: 1099 ApplyIntegerKind< 1100 HELPER<CAT, KIND, TypeCategory::Integer>::template Functor, void>( 1101 targetKind, terminator, result, x, target, kind, dim, mask, back, 1102 terminator); 1103 break; 1104 case TypeCategory::Real: 1105 ApplyFloatingPointKind< 1106 HELPER<CAT, KIND, TypeCategory::Real>::template Functor, void>( 1107 targetKind, terminator, result, x, target, kind, dim, mask, back, 1108 terminator); 1109 break; 1110 case TypeCategory::Complex: 1111 ApplyFloatingPointKind< 1112 HELPER<CAT, KIND, TypeCategory::Complex>::template Functor, void>( 1113 targetKind, terminator, result, x, target, kind, dim, mask, back, 1114 terminator); 1115 break; 1116 default: 1117 terminator.Crash( 1118 "FINDLOC: bad target category %d for array category %d", 1119 static_cast<int>(targetCat), static_cast<int>(CAT)); 1120 } 1121 } 1122 }; 1123 }; 1124 1125 template <int KIND> struct CharacterFindlocHelper { 1126 void operator()(Descriptor &result, const Descriptor &x, 1127 const Descriptor &target, int kind, const Descriptor *mask, bool back, 1128 Terminator &terminator) { 1129 using Accumulator = LocationAccumulator<CharacterEquality<KIND>>; 1130 Accumulator accumulator{x, target, back}; 1131 DoTotalReduction<void>(x, 0, mask, accumulator, "FINDLOC", terminator); 1132 ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, void>( 1133 kind, terminator, accumulator, result); 1134 } 1135 }; 1136 1137 static void LogicalFindlocHelper(Descriptor &result, const Descriptor &x, 1138 const Descriptor &target, int kind, const Descriptor *mask, bool back, 1139 Terminator &terminator) { 1140 using Accumulator = LocationAccumulator<LogicalEquivalence>; 1141 Accumulator accumulator{x, target, back}; 1142 DoTotalReduction<void>(x, 0, mask, accumulator, "FINDLOC", terminator); 1143 ApplyIntegerKind<LocationResultHelper<Accumulator>::template Functor, void>( 1144 kind, terminator, accumulator, result); 1145 } 1146 1147 extern "C" { 1148 void RTNAME(Findloc)(Descriptor &result, const Descriptor &x, 1149 const Descriptor &target, int kind, const char *source, int line, 1150 const Descriptor *mask, bool back) { 1151 int rank{x.rank()}; 1152 SubscriptValue extent[1]{rank}; 1153 result.Establish(TypeCategory::Integer, kind, nullptr, 1, extent, 1154 CFI_attribute_allocatable); 1155 result.GetDimension(0).SetBounds(1, extent[0]); 1156 Terminator terminator{source, line}; 1157 if (int stat{result.Allocate()}) { 1158 terminator.Crash( 1159 "FINDLOC: could not allocate memory for result; STAT=%d", stat); 1160 } 1161 CheckIntegerKind(terminator, kind, "FINDLOC"); 1162 auto xType{x.type().GetCategoryAndKind()}; 1163 auto targetType{target.type().GetCategoryAndKind()}; 1164 RUNTIME_CHECK(terminator, xType.has_value() && targetType.has_value()); 1165 switch (xType->first) { 1166 case TypeCategory::Integer: 1167 ApplyIntegerKind<NumericFindlocHelper<TypeCategory::Integer, 1168 TotalNumericFindlocHelper>::template Functor, 1169 void>(xType->second, terminator, targetType->first, targetType->second, 1170 result, x, target, kind, 0, mask, back, terminator); 1171 break; 1172 case TypeCategory::Real: 1173 ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Real, 1174 TotalNumericFindlocHelper>::template Functor, 1175 void>(xType->second, terminator, targetType->first, targetType->second, 1176 result, x, target, kind, 0, mask, back, terminator); 1177 break; 1178 case TypeCategory::Complex: 1179 ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Complex, 1180 TotalNumericFindlocHelper>::template Functor, 1181 void>(xType->second, terminator, targetType->first, targetType->second, 1182 result, x, target, kind, 0, mask, back, terminator); 1183 break; 1184 case TypeCategory::Character: 1185 RUNTIME_CHECK(terminator, 1186 targetType->first == TypeCategory::Character && 1187 targetType->second == xType->second); 1188 ApplyCharacterKind<CharacterFindlocHelper, void>(xType->second, terminator, 1189 result, x, target, kind, mask, back, terminator); 1190 break; 1191 case TypeCategory::Logical: 1192 RUNTIME_CHECK(terminator, targetType->first == TypeCategory::Logical); 1193 LogicalFindlocHelper(result, x, target, kind, mask, back, terminator); 1194 break; 1195 default: 1196 terminator.Crash( 1197 "FINDLOC: Bad data type code (%d) for array", x.type().raw()); 1198 } 1199 } 1200 } // extern "C" 1201 1202 // FINDLOC with DIM= 1203 1204 template <TypeCategory XCAT, int XKIND, TypeCategory TARGET_CAT> 1205 struct PartialNumericFindlocHelper { 1206 template <int TARGET_KIND> struct Functor { 1207 void operator()(Descriptor &result, const Descriptor &x, 1208 const Descriptor &target, int kind, int dim, const Descriptor *mask, 1209 bool back, Terminator &terminator) const { 1210 using Eq = Equality<XCAT, XKIND, TARGET_CAT, TARGET_KIND>; 1211 using Accumulator = LocationAccumulator<Eq>; 1212 Accumulator accumulator{x, target, back}; 1213 ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, 1214 void>(kind, terminator, result, x, dim, mask, terminator, "FINDLOC", 1215 accumulator); 1216 } 1217 }; 1218 }; 1219 1220 template <int KIND> struct PartialCharacterFindlocHelper { 1221 void operator()(Descriptor &result, const Descriptor &x, 1222 const Descriptor &target, int kind, int dim, const Descriptor *mask, 1223 bool back, Terminator &terminator) { 1224 using Accumulator = LocationAccumulator<CharacterEquality<KIND>>; 1225 Accumulator accumulator{x, target, back}; 1226 ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, 1227 void>(kind, terminator, result, x, dim, mask, terminator, "FINDLOC", 1228 accumulator); 1229 } 1230 }; 1231 1232 static void PartialLogicalFindlocHelper(Descriptor &result, const Descriptor &x, 1233 const Descriptor &target, int kind, int dim, const Descriptor *mask, 1234 bool back, Terminator &terminator) { 1235 using Accumulator = LocationAccumulator<LogicalEquivalence>; 1236 Accumulator accumulator{x, target, back}; 1237 ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, void>( 1238 kind, terminator, result, x, dim, mask, terminator, "FINDLOC", 1239 accumulator); 1240 } 1241 1242 extern "C" { 1243 void RTNAME(FindlocDim)(Descriptor &result, const Descriptor &x, 1244 const Descriptor &target, int kind, int dim, const char *source, int line, 1245 const Descriptor *mask, bool back) { 1246 Terminator terminator{source, line}; 1247 CheckIntegerKind(terminator, kind, "FINDLOC"); 1248 auto xType{x.type().GetCategoryAndKind()}; 1249 auto targetType{target.type().GetCategoryAndKind()}; 1250 RUNTIME_CHECK(terminator, xType.has_value() && targetType.has_value()); 1251 switch (xType->first) { 1252 case TypeCategory::Integer: 1253 ApplyIntegerKind<NumericFindlocHelper<TypeCategory::Integer, 1254 PartialNumericFindlocHelper>::template Functor, 1255 void>(xType->second, terminator, targetType->first, targetType->second, 1256 result, x, target, kind, dim, mask, back, terminator); 1257 break; 1258 case TypeCategory::Real: 1259 ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Real, 1260 PartialNumericFindlocHelper>::template Functor, 1261 void>(xType->second, terminator, targetType->first, targetType->second, 1262 result, x, target, kind, dim, mask, back, terminator); 1263 break; 1264 case TypeCategory::Complex: 1265 ApplyFloatingPointKind<NumericFindlocHelper<TypeCategory::Complex, 1266 PartialNumericFindlocHelper>::template Functor, 1267 void>(xType->second, terminator, targetType->first, targetType->second, 1268 result, x, target, kind, dim, mask, back, terminator); 1269 break; 1270 case TypeCategory::Character: 1271 RUNTIME_CHECK(terminator, 1272 targetType->first == TypeCategory::Character && 1273 targetType->second == xType->second); 1274 ApplyCharacterKind<PartialCharacterFindlocHelper, void>(xType->second, 1275 terminator, result, x, target, kind, dim, mask, back, terminator); 1276 break; 1277 case TypeCategory::Logical: 1278 RUNTIME_CHECK(terminator, targetType->first == TypeCategory::Logical); 1279 PartialLogicalFindlocHelper( 1280 result, x, target, kind, dim, mask, back, terminator); 1281 break; 1282 default: 1283 terminator.Crash( 1284 "FINDLOC: Bad data type code (%d) for array", x.type().raw()); 1285 } 1286 } 1287 } // extern "C" 1288 1289 // MAXVAL and MINVAL 1290 1291 template <TypeCategory CAT, int KIND, bool IS_MAXVAL> struct MaxOrMinIdentity { 1292 using Type = CppTypeFor<CAT, KIND>; 1293 static constexpr Type Value() { 1294 return IS_MAXVAL ? std::numeric_limits<Type>::lowest() 1295 : std::numeric_limits<Type>::max(); 1296 } 1297 }; 1298 1299 // std::numeric_limits<> may not know int128_t 1300 template <bool IS_MAXVAL> 1301 struct MaxOrMinIdentity<TypeCategory::Integer, 16, IS_MAXVAL> { 1302 using Type = CppTypeFor<TypeCategory::Integer, 16>; 1303 static constexpr Type Value() { 1304 return IS_MAXVAL ? Type{1} << 127 : ~Type{0} >> 1; 1305 } 1306 }; 1307 1308 template <TypeCategory CAT, int KIND, bool IS_MAXVAL> 1309 class NumericExtremumAccumulator { 1310 public: 1311 using Type = CppTypeFor<CAT, KIND>; 1312 explicit NumericExtremumAccumulator(const Descriptor &array) 1313 : array_{array} {} 1314 void Reinitialize() { 1315 extremum_ = MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value(); 1316 } 1317 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 1318 *p = extremum_; 1319 } 1320 bool Accumulate(Type x) { 1321 if constexpr (IS_MAXVAL) { 1322 if (x > extremum_) { 1323 extremum_ = x; 1324 } 1325 } else if (x < extremum_) { 1326 extremum_ = x; 1327 } 1328 return true; 1329 } 1330 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 1331 return Accumulate(*array_.Element<A>(at)); 1332 } 1333 1334 private: 1335 const Descriptor &array_; 1336 Type extremum_{MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value()}; 1337 }; 1338 1339 template <TypeCategory CAT, int KIND, bool IS_MAXVAL> 1340 inline CppTypeFor<CAT, KIND> TotalNumericMaxOrMin(const Descriptor &x, 1341 const char *source, int line, int dim, const Descriptor *mask, 1342 const char *intrinsic) { 1343 return GetTotalReduction<CAT, KIND>(x, source, line, dim, mask, 1344 NumericExtremumAccumulator<CAT, KIND, IS_MAXVAL>{x}, intrinsic); 1345 } 1346 1347 template <TypeCategory CAT, int KIND, bool IS_MAXVAL, 1348 template <TypeCategory, int, bool> class ACCUMULATOR> 1349 static void DoMaxOrMin(Descriptor &result, const Descriptor &x, int dim, 1350 const Descriptor *mask, const char *intrinsic, Terminator &terminator) { 1351 using Type = CppTypeFor<CAT, KIND>; 1352 if (dim == 0 || x.rank() == 1) { 1353 // Total reduction 1354 result.Establish(x.type(), x.ElementBytes(), nullptr, 0, nullptr, 1355 CFI_attribute_allocatable); 1356 if (int stat{result.Allocate()}) { 1357 terminator.Crash( 1358 "%s: could not allocate memory for result; STAT=%d", intrinsic, stat); 1359 } 1360 ACCUMULATOR<CAT, KIND, IS_MAXVAL> accumulator{x}; 1361 DoTotalReduction<Type>(x, dim, mask, accumulator, intrinsic, terminator); 1362 accumulator.GetResult(result.OffsetElement<Type>()); 1363 } else { 1364 // Partial reduction 1365 using Accumulator = ACCUMULATOR<CAT, KIND, IS_MAXVAL>; 1366 Accumulator accumulator{x}; 1367 PartialReduction<Accumulator, CAT, KIND>( 1368 result, x, dim, mask, terminator, intrinsic, accumulator); 1369 } 1370 } 1371 1372 template <TypeCategory CAT, bool IS_MAXVAL> struct MaxOrMinHelper { 1373 template <int KIND> struct Functor { 1374 void operator()(Descriptor &result, const Descriptor &x, int dim, 1375 const Descriptor *mask, const char *intrinsic, 1376 Terminator &terminator) const { 1377 DoMaxOrMin<CAT, KIND, IS_MAXVAL, NumericExtremumAccumulator>( 1378 result, x, dim, mask, intrinsic, terminator); 1379 } 1380 }; 1381 }; 1382 1383 template <bool IS_MAXVAL> 1384 inline void NumericMaxOrMin(Descriptor &result, const Descriptor &x, int dim, 1385 const char *source, int line, const Descriptor *mask, 1386 const char *intrinsic) { 1387 Terminator terminator{source, line}; 1388 auto type{x.type().GetCategoryAndKind()}; 1389 RUNTIME_CHECK(terminator, type); 1390 switch (type->first) { 1391 case TypeCategory::Integer: 1392 ApplyIntegerKind< 1393 MaxOrMinHelper<TypeCategory::Integer, IS_MAXVAL>::template Functor, 1394 void>( 1395 type->second, terminator, result, x, dim, mask, intrinsic, terminator); 1396 break; 1397 case TypeCategory::Real: 1398 ApplyFloatingPointKind< 1399 MaxOrMinHelper<TypeCategory::Real, IS_MAXVAL>::template Functor, void>( 1400 type->second, terminator, result, x, dim, mask, intrinsic, terminator); 1401 break; 1402 default: 1403 terminator.Crash("%s: bad type code %d", intrinsic, x.type().raw()); 1404 } 1405 } 1406 1407 template <TypeCategory, int KIND, bool IS_MAXVAL> 1408 class CharacterExtremumAccumulator { 1409 public: 1410 using Type = CppTypeFor<TypeCategory::Character, KIND>; 1411 explicit CharacterExtremumAccumulator(const Descriptor &array) 1412 : array_{array}, charLen_{array_.ElementBytes() / KIND} {} 1413 void Reinitialize() { extremum_ = nullptr; } 1414 template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const { 1415 static_assert(std::is_same_v<A, Type>); 1416 if (extremum_) { 1417 std::memcpy(p, extremum_, charLen_); 1418 } else { 1419 // empty array: result is all zero-valued characters 1420 std::memset(p, 0, charLen_); 1421 } 1422 } 1423 bool Accumulate(const Type *x) { 1424 if (!extremum_) { 1425 extremum_ = x; 1426 } else { 1427 int cmp{CharacterScalarCompare(x, extremum_, charLen_, charLen_)}; 1428 if (IS_MAXVAL == (cmp > 0)) { 1429 extremum_ = x; 1430 } 1431 } 1432 return true; 1433 } 1434 template <typename A> bool AccumulateAt(const SubscriptValue at[]) { 1435 return Accumulate(array_.Element<A>(at)); 1436 } 1437 1438 private: 1439 const Descriptor &array_; 1440 std::size_t charLen_; 1441 const Type *extremum_{nullptr}; 1442 }; 1443 1444 template <bool IS_MAXVAL> struct CharacterMaxOrMinHelper { 1445 template <int KIND> struct Functor { 1446 void operator()(Descriptor &result, const Descriptor &x, int dim, 1447 const Descriptor *mask, const char *intrinsic, 1448 Terminator &terminator) const { 1449 DoMaxOrMin<TypeCategory::Character, KIND, IS_MAXVAL, 1450 CharacterExtremumAccumulator>( 1451 result, x, dim, mask, intrinsic, terminator); 1452 } 1453 }; 1454 }; 1455 1456 template <bool IS_MAXVAL> 1457 inline void CharacterMaxOrMin(Descriptor &result, const Descriptor &x, int dim, 1458 const char *source, int line, const Descriptor *mask, 1459 const char *intrinsic) { 1460 Terminator terminator{source, line}; 1461 auto type{x.type().GetCategoryAndKind()}; 1462 RUNTIME_CHECK(terminator, type && type->first == TypeCategory::Character); 1463 ApplyCharacterKind<CharacterMaxOrMinHelper<IS_MAXVAL>::template Functor, 1464 void>( 1465 type->second, terminator, result, x, dim, mask, intrinsic, terminator); 1466 } 1467 1468 extern "C" { 1469 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MaxvalInteger1)(const Descriptor &x, 1470 const char *source, int line, int dim, const Descriptor *mask) { 1471 return TotalNumericMaxOrMin<TypeCategory::Integer, 1, true>( 1472 x, source, line, dim, mask, "MAXVAL"); 1473 } 1474 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MaxvalInteger2)(const Descriptor &x, 1475 const char *source, int line, int dim, const Descriptor *mask) { 1476 return TotalNumericMaxOrMin<TypeCategory::Integer, 2, true>( 1477 x, source, line, dim, mask, "MAXVAL"); 1478 } 1479 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MaxvalInteger4)(const Descriptor &x, 1480 const char *source, int line, int dim, const Descriptor *mask) { 1481 return TotalNumericMaxOrMin<TypeCategory::Integer, 4, true>( 1482 x, source, line, dim, mask, "MAXVAL"); 1483 } 1484 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MaxvalInteger8)(const Descriptor &x, 1485 const char *source, int line, int dim, const Descriptor *mask) { 1486 return TotalNumericMaxOrMin<TypeCategory::Integer, 8, true>( 1487 x, source, line, dim, mask, "MAXVAL"); 1488 } 1489 #ifdef __SIZEOF_INT128__ 1490 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MaxvalInteger16)( 1491 const Descriptor &x, const char *source, int line, int dim, 1492 const Descriptor *mask) { 1493 return TotalNumericMaxOrMin<TypeCategory::Integer, 16, true>( 1494 x, source, line, dim, mask, "MAXVAL"); 1495 } 1496 #endif 1497 1498 // TODO: REAL(2 & 3) 1499 CppTypeFor<TypeCategory::Real, 4> RTNAME(MaxvalReal4)(const Descriptor &x, 1500 const char *source, int line, int dim, const Descriptor *mask) { 1501 return TotalNumericMaxOrMin<TypeCategory::Real, 4, true>( 1502 x, source, line, dim, mask, "MAXVAL"); 1503 } 1504 CppTypeFor<TypeCategory::Real, 8> RTNAME(MaxvalReal8)(const Descriptor &x, 1505 const char *source, int line, int dim, const Descriptor *mask) { 1506 return TotalNumericMaxOrMin<TypeCategory::Real, 8, true>( 1507 x, source, line, dim, mask, "MAXVAL"); 1508 } 1509 #if LONG_DOUBLE == 80 1510 CppTypeFor<TypeCategory::Real, 10> RTNAME(MaxvalReal10)(const Descriptor &x, 1511 const char *source, int line, int dim, const Descriptor *mask) { 1512 return TotalNumericMaxOrMin<TypeCategory::Real, 10, true>( 1513 x, source, line, dim, mask, "MAXVAL"); 1514 } 1515 #elif LONG_DOUBLE == 128 1516 CppTypeFor<TypeCategory::Real, 16> RTNAME(MaxvalReal16)(const Descriptor &x, 1517 const char *source, int line, int dim, const Descriptor *mask) { 1518 return TotalNumericMaxOrMin<TypeCategory::Real, 16, true>( 1519 x, source, line, dim, mask, "MAXVAL"); 1520 } 1521 #endif 1522 1523 void RTNAME(MaxvalCharacter)(Descriptor &result, const Descriptor &x, 1524 const char *source, int line, const Descriptor *mask) { 1525 CharacterMaxOrMin<true>(result, x, 0, source, line, mask, "MAXVAL"); 1526 } 1527 1528 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MinvalInteger1)(const Descriptor &x, 1529 const char *source, int line, int dim, const Descriptor *mask) { 1530 return TotalNumericMaxOrMin<TypeCategory::Integer, 1, false>( 1531 x, source, line, dim, mask, "MINVAL"); 1532 } 1533 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MinvalInteger2)(const Descriptor &x, 1534 const char *source, int line, int dim, const Descriptor *mask) { 1535 return TotalNumericMaxOrMin<TypeCategory::Integer, 2, false>( 1536 x, source, line, dim, mask, "MINVAL"); 1537 } 1538 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MinvalInteger4)(const Descriptor &x, 1539 const char *source, int line, int dim, const Descriptor *mask) { 1540 return TotalNumericMaxOrMin<TypeCategory::Integer, 4, false>( 1541 x, source, line, dim, mask, "MINVAL"); 1542 } 1543 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MinvalInteger8)(const Descriptor &x, 1544 const char *source, int line, int dim, const Descriptor *mask) { 1545 return TotalNumericMaxOrMin<TypeCategory::Integer, 8, false>( 1546 x, source, line, dim, mask, "MINVAL"); 1547 } 1548 #ifdef __SIZEOF_INT128__ 1549 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MinvalInteger16)( 1550 const Descriptor &x, const char *source, int line, int dim, 1551 const Descriptor *mask) { 1552 return TotalNumericMaxOrMin<TypeCategory::Integer, 16, false>( 1553 x, source, line, dim, mask, "MINVAL"); 1554 } 1555 #endif 1556 1557 // TODO: REAL(2 & 3) 1558 CppTypeFor<TypeCategory::Real, 4> RTNAME(MinvalReal4)(const Descriptor &x, 1559 const char *source, int line, int dim, const Descriptor *mask) { 1560 return TotalNumericMaxOrMin<TypeCategory::Real, 4, false>( 1561 x, source, line, dim, mask, "MINVAL"); 1562 } 1563 CppTypeFor<TypeCategory::Real, 8> RTNAME(MinvalReal8)(const Descriptor &x, 1564 const char *source, int line, int dim, const Descriptor *mask) { 1565 return TotalNumericMaxOrMin<TypeCategory::Real, 8, false>( 1566 x, source, line, dim, mask, "MINVAL"); 1567 } 1568 #if LONG_DOUBLE == 80 1569 CppTypeFor<TypeCategory::Real, 10> RTNAME(MinvalReal10)(const Descriptor &x, 1570 const char *source, int line, int dim, const Descriptor *mask) { 1571 return TotalNumericMaxOrMin<TypeCategory::Real, 10, false>( 1572 x, source, line, dim, mask, "MINVAL"); 1573 } 1574 #elif LONG_DOUBLE == 128 1575 CppTypeFor<TypeCategory::Real, 16> RTNAME(MinvalReal16)(const Descriptor &x, 1576 const char *source, int line, int dim, const Descriptor *mask) { 1577 return TotalNumericMaxOrMin<TypeCategory::Real, 16, false>( 1578 x, source, line, dim, mask, "MINVAL"); 1579 } 1580 #endif 1581 1582 void RTNAME(MinvalCharacter)(Descriptor &result, const Descriptor &x, 1583 const char *source, int line, const Descriptor *mask) { 1584 CharacterMaxOrMin<false>(result, x, 0, source, line, mask, "MINVAL"); 1585 } 1586 1587 void RTNAME(MaxvalDim)(Descriptor &result, const Descriptor &x, int dim, 1588 const char *source, int line, const Descriptor *mask) { 1589 if (x.type().IsCharacter()) { 1590 CharacterMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL"); 1591 } else { 1592 NumericMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL"); 1593 } 1594 } 1595 void RTNAME(MinvalDim)(Descriptor &result, const Descriptor &x, int dim, 1596 const char *source, int line, const Descriptor *mask) { 1597 if (x.type().IsCharacter()) { 1598 CharacterMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL"); 1599 } else { 1600 NumericMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL"); 1601 } 1602 } 1603 1604 } // extern "C" 1605 1606 // ALL, ANY, COUNT, & PARITY 1607 1608 enum class LogicalReduction { All, Any, Parity }; 1609 1610 template <LogicalReduction REDUCTION> class LogicalAccumulator { 1611 public: 1612 using Type = bool; 1613 explicit LogicalAccumulator(const Descriptor &array) : array_{array} {} 1614 void Reinitialize() { result_ = REDUCTION == LogicalReduction::All; } 1615 bool Result() const { return result_; } 1616 bool Accumulate(bool x) { 1617 if constexpr (REDUCTION == LogicalReduction::Parity) { 1618 result_ = result_ != x; 1619 } else if (x != (REDUCTION == LogicalReduction::All)) { 1620 result_ = x; 1621 return false; 1622 } 1623 return true; 1624 } 1625 template <typename IGNORED = void> 1626 bool AccumulateAt(const SubscriptValue at[]) { 1627 return Accumulate(IsLogicalElementTrue(array_, at)); 1628 } 1629 1630 private: 1631 const Descriptor &array_; 1632 bool result_{REDUCTION == LogicalReduction::All}; 1633 }; 1634 1635 template <typename ACCUMULATOR> 1636 inline auto GetTotalLogicalReduction(const Descriptor &x, const char *source, 1637 int line, int dim, ACCUMULATOR &&accumulator, const char *intrinsic) -> 1638 typename ACCUMULATOR::Type { 1639 Terminator terminator{source, line}; 1640 if (dim < 0 || dim > 1) { 1641 terminator.Crash("%s: bad DIM=%d", intrinsic, dim); 1642 } 1643 SubscriptValue xAt[maxRank]; 1644 x.GetLowerBounds(xAt); 1645 for (auto elements{x.Elements()}; elements--; x.IncrementSubscripts(xAt)) { 1646 if (!accumulator.AccumulateAt(xAt)) { 1647 break; // cut short, result is known 1648 } 1649 } 1650 return accumulator.Result(); 1651 } 1652 1653 template <typename ACCUMULATOR> 1654 inline auto ReduceLogicalDimToScalar(const Descriptor &x, int zeroBasedDim, 1655 SubscriptValue subscripts[]) -> typename ACCUMULATOR::Type { 1656 ACCUMULATOR accumulator{x}; 1657 SubscriptValue xAt[maxRank]; 1658 GetExpandedSubscripts(xAt, x, zeroBasedDim, subscripts); 1659 const auto &dim{x.GetDimension(zeroBasedDim)}; 1660 SubscriptValue at{dim.LowerBound()}; 1661 for (auto n{dim.Extent()}; n-- > 0; ++at) { 1662 xAt[zeroBasedDim] = at; 1663 if (!accumulator.AccumulateAt(xAt)) { 1664 break; 1665 } 1666 } 1667 return accumulator.Result(); 1668 } 1669 1670 template <LogicalReduction REDUCTION> struct LogicalReduceHelper { 1671 template <int KIND> struct Functor { 1672 void operator()(Descriptor &result, const Descriptor &x, int dim, 1673 Terminator &terminator, const char *intrinsic) const { 1674 // Standard requires result to have same LOGICAL kind as argument. 1675 CreatePartialReductionResult( 1676 result, x, dim, terminator, intrinsic, x.type()); 1677 SubscriptValue at[maxRank]; 1678 result.GetLowerBounds(at); 1679 INTERNAL_CHECK(at[0] == 1); 1680 using CppType = CppTypeFor<TypeCategory::Logical, KIND>; 1681 for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { 1682 *result.Element<CppType>(at) = 1683 ReduceLogicalDimToScalar<LogicalAccumulator<REDUCTION>>( 1684 x, dim - 1, at); 1685 } 1686 } 1687 }; 1688 }; 1689 1690 template <LogicalReduction REDUCTION> 1691 inline void DoReduceLogicalDimension(Descriptor &result, const Descriptor &x, 1692 int dim, Terminator &terminator, const char *intrinsic) { 1693 auto catKind{x.type().GetCategoryAndKind()}; 1694 RUNTIME_CHECK(terminator, catKind && catKind->first == TypeCategory::Logical); 1695 ApplyLogicalKind<LogicalReduceHelper<REDUCTION>::template Functor, void>( 1696 catKind->second, terminator, result, x, dim, terminator, intrinsic); 1697 } 1698 1699 // COUNT 1700 1701 class CountAccumulator { 1702 public: 1703 using Type = std::int64_t; 1704 explicit CountAccumulator(const Descriptor &array) : array_{array} {} 1705 void Reinitialize() { result_ = 0; } 1706 Type Result() const { return result_; } 1707 template <typename IGNORED = void> 1708 bool AccumulateAt(const SubscriptValue at[]) { 1709 if (IsLogicalElementTrue(array_, at)) { 1710 ++result_; 1711 } 1712 return true; 1713 } 1714 1715 private: 1716 const Descriptor &array_; 1717 Type result_{0}; 1718 }; 1719 1720 template <int KIND> struct CountDimension { 1721 void operator()(Descriptor &result, const Descriptor &x, int dim, 1722 Terminator &terminator) const { 1723 CreatePartialReductionResult(result, x, dim, terminator, "COUNT", 1724 TypeCode{TypeCategory::Integer, KIND}); 1725 SubscriptValue at[maxRank]; 1726 result.GetLowerBounds(at); 1727 INTERNAL_CHECK(at[0] == 1); 1728 using CppType = CppTypeFor<TypeCategory::Integer, KIND>; 1729 for (auto n{result.Elements()}; n-- > 0; result.IncrementSubscripts(at)) { 1730 *result.Element<CppType>(at) = 1731 ReduceLogicalDimToScalar<CountAccumulator>(x, dim - 1, at); 1732 } 1733 } 1734 }; 1735 1736 extern "C" { 1737 1738 bool RTNAME(All)(const Descriptor &x, const char *source, int line, int dim) { 1739 return GetTotalLogicalReduction(x, source, line, dim, 1740 LogicalAccumulator<LogicalReduction::All>{x}, "ALL"); 1741 } 1742 void RTNAME(AllDim)(Descriptor &result, const Descriptor &x, int dim, 1743 const char *source, int line) { 1744 Terminator terminator{source, line}; 1745 DoReduceLogicalDimension<LogicalReduction::All>( 1746 result, x, dim, terminator, "ALL"); 1747 } 1748 1749 bool RTNAME(Any)(const Descriptor &x, const char *source, int line, int dim) { 1750 return GetTotalLogicalReduction(x, source, line, dim, 1751 LogicalAccumulator<LogicalReduction::Any>{x}, "ANY"); 1752 } 1753 void RTNAME(AnyDim)(Descriptor &result, const Descriptor &x, int dim, 1754 const char *source, int line) { 1755 Terminator terminator{source, line}; 1756 DoReduceLogicalDimension<LogicalReduction::Any>( 1757 result, x, dim, terminator, "ANY"); 1758 } 1759 1760 std::int64_t RTNAME(Count)( 1761 const Descriptor &x, const char *source, int line, int dim) { 1762 return GetTotalLogicalReduction( 1763 x, source, line, dim, CountAccumulator{x}, "COUNT"); 1764 } 1765 1766 void RTNAME(CountDim)(Descriptor &result, const Descriptor &x, int dim, 1767 int kind, const char *source, int line) { 1768 Terminator terminator{source, line}; 1769 ApplyIntegerKind<CountDimension, void>( 1770 kind, terminator, result, x, dim, terminator); 1771 } 1772 1773 bool RTNAME(Parity)( 1774 const Descriptor &x, const char *source, int line, int dim) { 1775 return GetTotalLogicalReduction(x, source, line, dim, 1776 LogicalAccumulator<LogicalReduction::Parity>{x}, "PARITY"); 1777 } 1778 void RTNAME(ParityDim)(Descriptor &result, const Descriptor &x, int dim, 1779 const char *source, int line) { 1780 Terminator terminator{source, line}; 1781 DoReduceLogicalDimension<LogicalReduction::Parity>( 1782 result, x, dim, terminator, "PARITY"); 1783 } 1784 1785 } // extern "C" 1786 } // namespace Fortran::runtime 1787