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