1 //===-- runtime/extrema.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 MAXLOC, MINLOC, MAXVAL, & MINVAL for all required operand types
10 // and shapes and (for MAXLOC & MINLOC) result integer kinds.  Also implements
11 // NORM2 using common infrastructure.
12 
13 #include "reduction-templates.h"
14 #include "flang/Runtime/character.h"
15 #include "flang/Runtime/float128.h"
16 #include "flang/Runtime/reduction.h"
17 #include <algorithm>
18 #include <cfloat>
19 #include <cinttypes>
20 #include <cmath>
21 #include <optional>
22 
23 namespace Fortran::runtime {
24 
25 // MAXLOC & MINLOC
26 
27 template <typename T, bool IS_MAX, bool BACK> struct NumericCompare {
28   using Type = T;
NumericCompareFortran::runtime::NumericCompare29   explicit NumericCompare(std::size_t /*elemLen; ignored*/) {}
operator ()Fortran::runtime::NumericCompare30   bool operator()(const T &value, const T &previous) const {
31     if (value == previous) {
32       return BACK;
33     } else if constexpr (IS_MAX) {
34       return value > previous;
35     } else {
36       return value < previous;
37     }
38   }
39 };
40 
41 template <typename T, bool IS_MAX, bool BACK> class CharacterCompare {
42 public:
43   using Type = T;
CharacterCompare(std::size_t elemLen)44   explicit CharacterCompare(std::size_t elemLen)
45       : chars_{elemLen / sizeof(T)} {}
operator ()(const T & value,const T & previous) const46   bool operator()(const T &value, const T &previous) const {
47     int cmp{CharacterScalarCompare<T>(&value, &previous, chars_, chars_)};
48     if (cmp == 0) {
49       return BACK;
50     } else if constexpr (IS_MAX) {
51       return cmp > 0;
52     } else {
53       return cmp < 0;
54     }
55   }
56 
57 private:
58   std::size_t chars_;
59 };
60 
61 template <typename COMPARE> class ExtremumLocAccumulator {
62 public:
63   using Type = typename COMPARE::Type;
ExtremumLocAccumulator(const Descriptor & array)64   ExtremumLocAccumulator(const Descriptor &array)
65       : array_{array}, argRank_{array.rank()}, compare_{array.ElementBytes()} {
66     Reinitialize();
67   }
Reinitialize()68   void Reinitialize() {
69     // per standard: result indices are all zero if no data
70     for (int j{0}; j < argRank_; ++j) {
71       extremumLoc_[j] = 0;
72     }
73     previous_ = nullptr;
74   }
argRank() const75   int argRank() const { return argRank_; }
GetResult(A * p,int zeroBasedDim=-1)76   template <typename A> void GetResult(A *p, int zeroBasedDim = -1) {
77     if (zeroBasedDim >= 0) {
78       *p = extremumLoc_[zeroBasedDim] -
79           array_.GetDimension(zeroBasedDim).LowerBound() + 1;
80     } else {
81       for (int j{0}; j < argRank_; ++j) {
82         p[j] = extremumLoc_[j] - array_.GetDimension(j).LowerBound() + 1;
83       }
84     }
85   }
AccumulateAt(const SubscriptValue at[])86   template <typename IGNORED> bool AccumulateAt(const SubscriptValue at[]) {
87     const auto &value{*array_.Element<Type>(at)};
88     if (!previous_ || compare_(value, *previous_)) {
89       previous_ = &value;
90       for (int j{0}; j < argRank_; ++j) {
91         extremumLoc_[j] = at[j];
92       }
93     }
94     return true;
95   }
96 
97 private:
98   const Descriptor &array_;
99   int argRank_;
100   SubscriptValue extremumLoc_[maxRank];
101   const Type *previous_{nullptr};
102   COMPARE compare_;
103 };
104 
105 template <typename ACCUMULATOR, typename CPPTYPE>
LocationHelper(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,const Descriptor * mask,Terminator & terminator)106 static void LocationHelper(const char *intrinsic, Descriptor &result,
107     const Descriptor &x, int kind, const Descriptor *mask,
108     Terminator &terminator) {
109   ACCUMULATOR accumulator{x};
110   DoTotalReduction<CPPTYPE>(x, 0, mask, accumulator, intrinsic, terminator);
111   ApplyIntegerKind<LocationResultHelper<ACCUMULATOR>::template Functor, void>(
112       kind, terminator, accumulator, result);
113 }
114 
115 template <TypeCategory CAT, int KIND, bool IS_MAX,
116     template <typename, bool, bool> class COMPARE>
DoMaxOrMinLoc(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,const char * source,int line,const Descriptor * mask,bool back)117 inline void DoMaxOrMinLoc(const char *intrinsic, Descriptor &result,
118     const Descriptor &x, int kind, const char *source, int line,
119     const Descriptor *mask, bool back) {
120   using CppType = CppTypeFor<CAT, KIND>;
121   Terminator terminator{source, line};
122   if (back) {
123     LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, true>>,
124         CppType>(intrinsic, result, x, kind, mask, terminator);
125   } else {
126     LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, false>>,
127         CppType>(intrinsic, result, x, kind, mask, terminator);
128   }
129 }
130 
131 template <TypeCategory CAT, bool IS_MAX> struct TypedMaxOrMinLocHelper {
132   template <int KIND> struct Functor {
operator ()Fortran::runtime::TypedMaxOrMinLocHelper::Functor133     void operator()(const char *intrinsic, Descriptor &result,
134         const Descriptor &x, int kind, const char *source, int line,
135         const Descriptor *mask, bool back) const {
136       DoMaxOrMinLoc<CAT, KIND, IS_MAX, NumericCompare>(
137           intrinsic, result, x, kind, source, line, mask, back);
138     }
139   };
140 };
141 
142 template <bool IS_MAX>
TypedMaxOrMinLoc(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,const char * source,int line,const Descriptor * mask,bool back)143 inline void TypedMaxOrMinLoc(const char *intrinsic, Descriptor &result,
144     const Descriptor &x, int kind, const char *source, int line,
145     const Descriptor *mask, bool back) {
146   int rank{x.rank()};
147   SubscriptValue extent[1]{rank};
148   result.Establish(TypeCategory::Integer, kind, nullptr, 1, extent,
149       CFI_attribute_allocatable);
150   result.GetDimension(0).SetBounds(1, extent[0]);
151   Terminator terminator{source, line};
152   if (int stat{result.Allocate()}) {
153     terminator.Crash(
154         "%s: could not allocate memory for result; STAT=%d", intrinsic, stat);
155   }
156   CheckIntegerKind(terminator, kind, intrinsic);
157   auto catKind{x.type().GetCategoryAndKind()};
158   RUNTIME_CHECK(terminator, catKind.has_value());
159   switch (catKind->first) {
160   case TypeCategory::Integer:
161     ApplyIntegerKind<
162         TypedMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX>::template Functor,
163         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
164         line, mask, back);
165     break;
166   case TypeCategory::Real:
167     ApplyFloatingPointKind<
168         TypedMaxOrMinLocHelper<TypeCategory::Real, IS_MAX>::template Functor,
169         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
170         line, mask, back);
171     break;
172   case TypeCategory::Character:
173     ApplyCharacterKind<TypedMaxOrMinLocHelper<TypeCategory::Character,
174                            IS_MAX>::template Functor,
175         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
176         line, mask, back);
177     break;
178   default:
179     terminator.Crash(
180         "%s: bad data type code (%d) for array", intrinsic, x.type().raw());
181   }
182 }
183 
184 extern "C" {
RTNAME(Maxloc)185 void RTNAME(Maxloc)(Descriptor &result, const Descriptor &x, int kind,
186     const char *source, int line, const Descriptor *mask, bool back) {
187   TypedMaxOrMinLoc<true>("MAXLOC", result, x, kind, source, line, mask, back);
188 }
RTNAME(Minloc)189 void RTNAME(Minloc)(Descriptor &result, const Descriptor &x, int kind,
190     const char *source, int line, const Descriptor *mask, bool back) {
191   TypedMaxOrMinLoc<false>("MINLOC", result, x, kind, source, line, mask, back);
192 }
193 } // extern "C"
194 
195 // MAXLOC/MINLOC with DIM=
196 
197 template <TypeCategory CAT, int KIND, bool IS_MAX,
198     template <typename, bool, bool> class COMPARE, bool BACK>
DoPartialMaxOrMinLocDirection(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,int dim,const Descriptor * mask,Terminator & terminator)199 static void DoPartialMaxOrMinLocDirection(const char *intrinsic,
200     Descriptor &result, const Descriptor &x, int kind, int dim,
201     const Descriptor *mask, Terminator &terminator) {
202   using CppType = CppTypeFor<CAT, KIND>;
203   using Accumulator = ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, BACK>>;
204   Accumulator accumulator{x};
205   ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, void>(
206       kind, terminator, result, x, dim, mask, terminator, intrinsic,
207       accumulator);
208 }
209 
210 template <TypeCategory CAT, int KIND, bool IS_MAX,
211     template <typename, bool, bool> class COMPARE>
DoPartialMaxOrMinLoc(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,int dim,const Descriptor * mask,bool back,Terminator & terminator)212 inline void DoPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result,
213     const Descriptor &x, int kind, int dim, const Descriptor *mask, bool back,
214     Terminator &terminator) {
215   if (back) {
216     DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, true>(
217         intrinsic, result, x, kind, dim, mask, terminator);
218   } else {
219     DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, false>(
220         intrinsic, result, x, kind, dim, mask, terminator);
221   }
222 }
223 
224 template <TypeCategory CAT, bool IS_MAX,
225     template <typename, bool, bool> class COMPARE>
226 struct DoPartialMaxOrMinLocHelper {
227   template <int KIND> struct Functor {
operator ()Fortran::runtime::DoPartialMaxOrMinLocHelper::Functor228     void operator()(const char *intrinsic, Descriptor &result,
229         const Descriptor &x, int kind, int dim, const Descriptor *mask,
230         bool back, Terminator &terminator) const {
231       DoPartialMaxOrMinLoc<CAT, KIND, IS_MAX, COMPARE>(
232           intrinsic, result, x, kind, dim, mask, back, terminator);
233     }
234   };
235 };
236 
237 template <bool IS_MAX>
TypedPartialMaxOrMinLoc(const char * intrinsic,Descriptor & result,const Descriptor & x,int kind,int dim,const char * source,int line,const Descriptor * mask,bool back)238 inline void TypedPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result,
239     const Descriptor &x, int kind, int dim, const char *source, int line,
240     const Descriptor *mask, bool back) {
241   Terminator terminator{source, line};
242   CheckIntegerKind(terminator, kind, intrinsic);
243   auto catKind{x.type().GetCategoryAndKind()};
244   RUNTIME_CHECK(terminator, catKind.has_value());
245   const Descriptor *maskToUse{mask};
246   SubscriptValue maskAt[maxRank]; // contents unused
247   if (mask && mask->rank() == 0) {
248     if (IsLogicalElementTrue(*mask, maskAt)) {
249       // A scalar MASK that's .TRUE.  In this case, just get rid of the MASK.
250       maskToUse = nullptr;
251     } else {
252       // For scalar MASK arguments that are .FALSE., return all zeroes
253       CreatePartialReductionResult(result, x, dim, terminator, intrinsic,
254           TypeCode{TypeCategory::Integer, kind});
255       std::memset(
256           result.OffsetElement(), 0, result.Elements() * result.ElementBytes());
257       return;
258     }
259   }
260   switch (catKind->first) {
261   case TypeCategory::Integer:
262     ApplyIntegerKind<DoPartialMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX,
263                          NumericCompare>::template Functor,
264         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
265         maskToUse, back, terminator);
266     break;
267   case TypeCategory::Real:
268     ApplyFloatingPointKind<DoPartialMaxOrMinLocHelper<TypeCategory::Real,
269                                IS_MAX, NumericCompare>::template Functor,
270         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
271         maskToUse, back, terminator);
272     break;
273   case TypeCategory::Character:
274     ApplyCharacterKind<DoPartialMaxOrMinLocHelper<TypeCategory::Character,
275                            IS_MAX, CharacterCompare>::template Functor,
276         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
277         maskToUse, back, terminator);
278     break;
279   default:
280     terminator.Crash(
281         "%s: bad data type code (%d) for array", intrinsic, x.type().raw());
282   }
283 }
284 
285 extern "C" {
RTNAME(MaxlocDim)286 void RTNAME(MaxlocDim)(Descriptor &result, const Descriptor &x, int kind,
287     int dim, const char *source, int line, const Descriptor *mask, bool back) {
288   TypedPartialMaxOrMinLoc<true>(
289       "MAXLOC", result, x, kind, dim, source, line, mask, back);
290 }
RTNAME(MinlocDim)291 void RTNAME(MinlocDim)(Descriptor &result, const Descriptor &x, int kind,
292     int dim, const char *source, int line, const Descriptor *mask, bool back) {
293   TypedPartialMaxOrMinLoc<false>(
294       "MINLOC", result, x, kind, dim, source, line, mask, back);
295 }
296 } // extern "C"
297 
298 // MAXVAL and MINVAL
299 
300 template <TypeCategory CAT, int KIND, bool IS_MAXVAL> struct MaxOrMinIdentity {
301   using Type = CppTypeFor<CAT, KIND>;
ValueFortran::runtime::MaxOrMinIdentity302   static constexpr Type Value() {
303     return IS_MAXVAL ? std::numeric_limits<Type>::lowest()
304                      : std::numeric_limits<Type>::max();
305   }
306 };
307 
308 // std::numeric_limits<> may not know int128_t
309 template <bool IS_MAXVAL>
310 struct MaxOrMinIdentity<TypeCategory::Integer, 16, IS_MAXVAL> {
311   using Type = CppTypeFor<TypeCategory::Integer, 16>;
ValueFortran::runtime::MaxOrMinIdentity312   static constexpr Type Value() {
313     return IS_MAXVAL ? Type{1} << 127 : ~Type{0} >> 1;
314   }
315 };
316 
317 template <TypeCategory CAT, int KIND, bool IS_MAXVAL>
318 class NumericExtremumAccumulator {
319 public:
320   using Type = CppTypeFor<CAT, KIND>;
NumericExtremumAccumulator(const Descriptor & array)321   explicit NumericExtremumAccumulator(const Descriptor &array)
322       : array_{array} {}
Reinitialize()323   void Reinitialize() {
324     extremum_ = MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value();
325   }
GetResult(A * p,int=-1) const326   template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
327     *p = extremum_;
328   }
Accumulate(Type x)329   bool Accumulate(Type x) {
330     if constexpr (IS_MAXVAL) {
331       if (x > extremum_) {
332         extremum_ = x;
333       }
334     } else if (x < extremum_) {
335       extremum_ = x;
336     }
337     return true;
338   }
AccumulateAt(const SubscriptValue at[])339   template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
340     return Accumulate(*array_.Element<A>(at));
341   }
342 
343 private:
344   const Descriptor &array_;
345   Type extremum_{MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value()};
346 };
347 
348 template <TypeCategory CAT, int KIND, bool IS_MAXVAL>
TotalNumericMaxOrMin(const Descriptor & x,const char * source,int line,int dim,const Descriptor * mask,const char * intrinsic)349 inline CppTypeFor<CAT, KIND> TotalNumericMaxOrMin(const Descriptor &x,
350     const char *source, int line, int dim, const Descriptor *mask,
351     const char *intrinsic) {
352   return GetTotalReduction<CAT, KIND>(x, source, line, dim, mask,
353       NumericExtremumAccumulator<CAT, KIND, IS_MAXVAL>{x}, intrinsic);
354 }
355 
356 template <TypeCategory CAT, int KIND, typename ACCUMULATOR>
DoMaxMinNorm2(Descriptor & result,const Descriptor & x,int dim,const Descriptor * mask,const char * intrinsic,Terminator & terminator)357 static void DoMaxMinNorm2(Descriptor &result, const Descriptor &x, int dim,
358     const Descriptor *mask, const char *intrinsic, Terminator &terminator) {
359   using Type = CppTypeFor<CAT, KIND>;
360   ACCUMULATOR accumulator{x};
361   if (dim == 0 || x.rank() == 1) {
362     // Total reduction
363     result.Establish(x.type(), x.ElementBytes(), nullptr, 0, nullptr,
364         CFI_attribute_allocatable);
365     if (int stat{result.Allocate()}) {
366       terminator.Crash(
367           "%s: could not allocate memory for result; STAT=%d", intrinsic, stat);
368     }
369     DoTotalReduction<Type>(x, dim, mask, accumulator, intrinsic, terminator);
370     accumulator.GetResult(result.OffsetElement<Type>());
371   } else {
372     // Partial reduction
373     PartialReduction<ACCUMULATOR, CAT, KIND>(
374         result, x, dim, mask, terminator, intrinsic, accumulator);
375   }
376 }
377 
378 template <TypeCategory CAT, bool IS_MAXVAL> struct MaxOrMinHelper {
379   template <int KIND> struct Functor {
operator ()Fortran::runtime::MaxOrMinHelper::Functor380     void operator()(Descriptor &result, const Descriptor &x, int dim,
381         const Descriptor *mask, const char *intrinsic,
382         Terminator &terminator) const {
383       DoMaxMinNorm2<CAT, KIND,
384           NumericExtremumAccumulator<CAT, KIND, IS_MAXVAL>>(
385           result, x, dim, mask, intrinsic, terminator);
386     }
387   };
388 };
389 
390 template <bool IS_MAXVAL>
NumericMaxOrMin(Descriptor & result,const Descriptor & x,int dim,const char * source,int line,const Descriptor * mask,const char * intrinsic)391 inline void NumericMaxOrMin(Descriptor &result, const Descriptor &x, int dim,
392     const char *source, int line, const Descriptor *mask,
393     const char *intrinsic) {
394   Terminator terminator{source, line};
395   auto type{x.type().GetCategoryAndKind()};
396   RUNTIME_CHECK(terminator, type);
397   switch (type->first) {
398   case TypeCategory::Integer:
399     ApplyIntegerKind<
400         MaxOrMinHelper<TypeCategory::Integer, IS_MAXVAL>::template Functor,
401         void>(
402         type->second, terminator, result, x, dim, mask, intrinsic, terminator);
403     break;
404   case TypeCategory::Real:
405     ApplyFloatingPointKind<
406         MaxOrMinHelper<TypeCategory::Real, IS_MAXVAL>::template Functor, void>(
407         type->second, terminator, result, x, dim, mask, intrinsic, terminator);
408     break;
409   default:
410     terminator.Crash("%s: bad type code %d", intrinsic, x.type().raw());
411   }
412 }
413 
414 template <int KIND, bool IS_MAXVAL> class CharacterExtremumAccumulator {
415 public:
416   using Type = CppTypeFor<TypeCategory::Character, KIND>;
CharacterExtremumAccumulator(const Descriptor & array)417   explicit CharacterExtremumAccumulator(const Descriptor &array)
418       : array_{array}, charLen_{array_.ElementBytes() / KIND} {}
Reinitialize()419   void Reinitialize() { extremum_ = nullptr; }
GetResult(A * p,int=-1) const420   template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
421     static_assert(std::is_same_v<A, Type>);
422     if (extremum_) {
423       std::memcpy(p, extremum_, charLen_);
424     } else {
425       // empty array: result is all zero-valued characters
426       std::memset(p, 0, charLen_);
427     }
428   }
Accumulate(const Type * x)429   bool Accumulate(const Type *x) {
430     if (!extremum_) {
431       extremum_ = x;
432     } else {
433       int cmp{CharacterScalarCompare(x, extremum_, charLen_, charLen_)};
434       if (IS_MAXVAL == (cmp > 0)) {
435         extremum_ = x;
436       }
437     }
438     return true;
439   }
AccumulateAt(const SubscriptValue at[])440   template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
441     return Accumulate(array_.Element<A>(at));
442   }
443 
444 private:
445   const Descriptor &array_;
446   std::size_t charLen_;
447   const Type *extremum_{nullptr};
448 };
449 
450 template <bool IS_MAXVAL> struct CharacterMaxOrMinHelper {
451   template <int KIND> struct Functor {
operator ()Fortran::runtime::CharacterMaxOrMinHelper::Functor452     void operator()(Descriptor &result, const Descriptor &x, int dim,
453         const Descriptor *mask, const char *intrinsic,
454         Terminator &terminator) const {
455       DoMaxMinNorm2<TypeCategory::Character, KIND,
456           CharacterExtremumAccumulator<KIND, IS_MAXVAL>>(
457           result, x, dim, mask, intrinsic, terminator);
458     }
459   };
460 };
461 
462 template <bool IS_MAXVAL>
CharacterMaxOrMin(Descriptor & result,const Descriptor & x,int dim,const char * source,int line,const Descriptor * mask,const char * intrinsic)463 inline void CharacterMaxOrMin(Descriptor &result, const Descriptor &x, int dim,
464     const char *source, int line, const Descriptor *mask,
465     const char *intrinsic) {
466   Terminator terminator{source, line};
467   auto type{x.type().GetCategoryAndKind()};
468   RUNTIME_CHECK(terminator, type && type->first == TypeCategory::Character);
469   ApplyCharacterKind<CharacterMaxOrMinHelper<IS_MAXVAL>::template Functor,
470       void>(
471       type->second, terminator, result, x, dim, mask, intrinsic, terminator);
472 }
473 
474 extern "C" {
RTNAME(MaxvalInteger1)475 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MaxvalInteger1)(const Descriptor &x,
476     const char *source, int line, int dim, const Descriptor *mask) {
477   return TotalNumericMaxOrMin<TypeCategory::Integer, 1, true>(
478       x, source, line, dim, mask, "MAXVAL");
479 }
RTNAME(MaxvalInteger2)480 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MaxvalInteger2)(const Descriptor &x,
481     const char *source, int line, int dim, const Descriptor *mask) {
482   return TotalNumericMaxOrMin<TypeCategory::Integer, 2, true>(
483       x, source, line, dim, mask, "MAXVAL");
484 }
RTNAME(MaxvalInteger4)485 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MaxvalInteger4)(const Descriptor &x,
486     const char *source, int line, int dim, const Descriptor *mask) {
487   return TotalNumericMaxOrMin<TypeCategory::Integer, 4, true>(
488       x, source, line, dim, mask, "MAXVAL");
489 }
RTNAME(MaxvalInteger8)490 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MaxvalInteger8)(const Descriptor &x,
491     const char *source, int line, int dim, const Descriptor *mask) {
492   return TotalNumericMaxOrMin<TypeCategory::Integer, 8, true>(
493       x, source, line, dim, mask, "MAXVAL");
494 }
495 #ifdef __SIZEOF_INT128__
RTNAME(MaxvalInteger16)496 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MaxvalInteger16)(
497     const Descriptor &x, const char *source, int line, int dim,
498     const Descriptor *mask) {
499   return TotalNumericMaxOrMin<TypeCategory::Integer, 16, true>(
500       x, source, line, dim, mask, "MAXVAL");
501 }
502 #endif
503 
504 // TODO: REAL(2 & 3)
RTNAME(MaxvalReal4)505 CppTypeFor<TypeCategory::Real, 4> RTNAME(MaxvalReal4)(const Descriptor &x,
506     const char *source, int line, int dim, const Descriptor *mask) {
507   return TotalNumericMaxOrMin<TypeCategory::Real, 4, true>(
508       x, source, line, dim, mask, "MAXVAL");
509 }
RTNAME(MaxvalReal8)510 CppTypeFor<TypeCategory::Real, 8> RTNAME(MaxvalReal8)(const Descriptor &x,
511     const char *source, int line, int dim, const Descriptor *mask) {
512   return TotalNumericMaxOrMin<TypeCategory::Real, 8, true>(
513       x, source, line, dim, mask, "MAXVAL");
514 }
515 #if LDBL_MANT_DIG == 64
RTNAME(MaxvalReal10)516 CppTypeFor<TypeCategory::Real, 10> RTNAME(MaxvalReal10)(const Descriptor &x,
517     const char *source, int line, int dim, const Descriptor *mask) {
518   return TotalNumericMaxOrMin<TypeCategory::Real, 10, true>(
519       x, source, line, dim, mask, "MAXVAL");
520 }
521 #endif
522 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
RTNAME(MaxvalReal16)523 CppTypeFor<TypeCategory::Real, 16> RTNAME(MaxvalReal16)(const Descriptor &x,
524     const char *source, int line, int dim, const Descriptor *mask) {
525   return TotalNumericMaxOrMin<TypeCategory::Real, 16, true>(
526       x, source, line, dim, mask, "MAXVAL");
527 }
528 #endif
529 
RTNAME(MaxvalCharacter)530 void RTNAME(MaxvalCharacter)(Descriptor &result, const Descriptor &x,
531     const char *source, int line, const Descriptor *mask) {
532   CharacterMaxOrMin<true>(result, x, 0, source, line, mask, "MAXVAL");
533 }
534 
RTNAME(MinvalInteger1)535 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MinvalInteger1)(const Descriptor &x,
536     const char *source, int line, int dim, const Descriptor *mask) {
537   return TotalNumericMaxOrMin<TypeCategory::Integer, 1, false>(
538       x, source, line, dim, mask, "MINVAL");
539 }
RTNAME(MinvalInteger2)540 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MinvalInteger2)(const Descriptor &x,
541     const char *source, int line, int dim, const Descriptor *mask) {
542   return TotalNumericMaxOrMin<TypeCategory::Integer, 2, false>(
543       x, source, line, dim, mask, "MINVAL");
544 }
RTNAME(MinvalInteger4)545 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MinvalInteger4)(const Descriptor &x,
546     const char *source, int line, int dim, const Descriptor *mask) {
547   return TotalNumericMaxOrMin<TypeCategory::Integer, 4, false>(
548       x, source, line, dim, mask, "MINVAL");
549 }
RTNAME(MinvalInteger8)550 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MinvalInteger8)(const Descriptor &x,
551     const char *source, int line, int dim, const Descriptor *mask) {
552   return TotalNumericMaxOrMin<TypeCategory::Integer, 8, false>(
553       x, source, line, dim, mask, "MINVAL");
554 }
555 #ifdef __SIZEOF_INT128__
RTNAME(MinvalInteger16)556 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MinvalInteger16)(
557     const Descriptor &x, const char *source, int line, int dim,
558     const Descriptor *mask) {
559   return TotalNumericMaxOrMin<TypeCategory::Integer, 16, false>(
560       x, source, line, dim, mask, "MINVAL");
561 }
562 #endif
563 
564 // TODO: REAL(2 & 3)
RTNAME(MinvalReal4)565 CppTypeFor<TypeCategory::Real, 4> RTNAME(MinvalReal4)(const Descriptor &x,
566     const char *source, int line, int dim, const Descriptor *mask) {
567   return TotalNumericMaxOrMin<TypeCategory::Real, 4, false>(
568       x, source, line, dim, mask, "MINVAL");
569 }
RTNAME(MinvalReal8)570 CppTypeFor<TypeCategory::Real, 8> RTNAME(MinvalReal8)(const Descriptor &x,
571     const char *source, int line, int dim, const Descriptor *mask) {
572   return TotalNumericMaxOrMin<TypeCategory::Real, 8, false>(
573       x, source, line, dim, mask, "MINVAL");
574 }
575 #if LDBL_MANT_DIG == 64
RTNAME(MinvalReal10)576 CppTypeFor<TypeCategory::Real, 10> RTNAME(MinvalReal10)(const Descriptor &x,
577     const char *source, int line, int dim, const Descriptor *mask) {
578   return TotalNumericMaxOrMin<TypeCategory::Real, 10, false>(
579       x, source, line, dim, mask, "MINVAL");
580 }
581 #endif
582 #if LDBL_MANT_DIG == 113 || HAS_FLOAT128
RTNAME(MinvalReal16)583 CppTypeFor<TypeCategory::Real, 16> RTNAME(MinvalReal16)(const Descriptor &x,
584     const char *source, int line, int dim, const Descriptor *mask) {
585   return TotalNumericMaxOrMin<TypeCategory::Real, 16, false>(
586       x, source, line, dim, mask, "MINVAL");
587 }
588 #endif
589 
RTNAME(MinvalCharacter)590 void RTNAME(MinvalCharacter)(Descriptor &result, const Descriptor &x,
591     const char *source, int line, const Descriptor *mask) {
592   CharacterMaxOrMin<false>(result, x, 0, source, line, mask, "MINVAL");
593 }
594 
RTNAME(MaxvalDim)595 void RTNAME(MaxvalDim)(Descriptor &result, const Descriptor &x, int dim,
596     const char *source, int line, const Descriptor *mask) {
597   if (x.type().IsCharacter()) {
598     CharacterMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL");
599   } else {
600     NumericMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL");
601   }
602 }
RTNAME(MinvalDim)603 void RTNAME(MinvalDim)(Descriptor &result, const Descriptor &x, int dim,
604     const char *source, int line, const Descriptor *mask) {
605   if (x.type().IsCharacter()) {
606     CharacterMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL");
607   } else {
608     NumericMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL");
609   }
610 }
611 } // extern "C"
612 
613 // NORM2
614 
615 template <int KIND> class Norm2Accumulator {
616 public:
617   using Type = CppTypeFor<TypeCategory::Real, KIND>;
618   // Use at least double precision for accumulators.
619   // Don't use __float128, it doesn't work with abs() or sqrt() yet.
620   static constexpr int largestLDKind {
621 #if LDBL_MANT_DIG == 113
622     16
623 #elif LDBL_MANT_DIG == 64
624     10
625 #else
626     8
627 #endif
628   };
629   using AccumType = CppTypeFor<TypeCategory::Real,
630       std::max(std::min(largestLDKind, KIND), 8)>;
Norm2Accumulator(const Descriptor & array)631   explicit Norm2Accumulator(const Descriptor &array) : array_{array} {}
Reinitialize()632   void Reinitialize() { max_ = sum_ = 0; }
GetResult(A * p,int=-1) const633   template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
634     // m * sqrt(1 + sum((others(:)/m)**2))
635     *p = static_cast<Type>(max_ * std::sqrt(1 + sum_));
636   }
Accumulate(Type x)637   bool Accumulate(Type x) {
638     auto absX{std::abs(static_cast<AccumType>(x))};
639     if (!max_) {
640       max_ = x;
641     } else if (absX > max_) {
642       auto t{max_ / absX}; // < 1.0
643       auto tsq{t * t};
644       sum_ *= tsq; // scale sum to reflect change to the max
645       sum_ += tsq; // include a term for the previous max
646       max_ = absX;
647     } else { // absX <= max_
648       auto t{absX / max_};
649       sum_ += t * t;
650     }
651     return true;
652   }
AccumulateAt(const SubscriptValue at[])653   template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
654     return Accumulate(*array_.Element<A>(at));
655   }
656 
657 private:
658   const Descriptor &array_;
659   AccumType max_{0}; // value (m) with largest magnitude
660   AccumType sum_{0}; // sum((others(:)/m)**2)
661 };
662 
663 template <int KIND> struct Norm2Helper {
operator ()Fortran::runtime::Norm2Helper664   void operator()(Descriptor &result, const Descriptor &x, int dim,
665       const Descriptor *mask, Terminator &terminator) const {
666     DoMaxMinNorm2<TypeCategory::Real, KIND, Norm2Accumulator<KIND>>(
667         result, x, dim, mask, "NORM2", terminator);
668   }
669 };
670 
671 extern "C" {
672 // TODO: REAL(2 & 3)
RTNAME(Norm2_4)673 CppTypeFor<TypeCategory::Real, 4> RTNAME(Norm2_4)(const Descriptor &x,
674     const char *source, int line, int dim, const Descriptor *mask) {
675   return GetTotalReduction<TypeCategory::Real, 4>(
676       x, source, line, dim, mask, Norm2Accumulator<4>{x}, "NORM2");
677 }
RTNAME(Norm2_8)678 CppTypeFor<TypeCategory::Real, 8> RTNAME(Norm2_8)(const Descriptor &x,
679     const char *source, int line, int dim, const Descriptor *mask) {
680   return GetTotalReduction<TypeCategory::Real, 8>(
681       x, source, line, dim, mask, Norm2Accumulator<8>{x}, "NORM2");
682 }
683 #if LDBL_MANT_DIG == 64
RTNAME(Norm2_10)684 CppTypeFor<TypeCategory::Real, 10> RTNAME(Norm2_10)(const Descriptor &x,
685     const char *source, int line, int dim, const Descriptor *mask) {
686   return GetTotalReduction<TypeCategory::Real, 10>(
687       x, source, line, dim, mask, Norm2Accumulator<10>{x}, "NORM2");
688 }
689 #endif
690 #if LDBL_MANT_DIG == 113
RTNAME(Norm2_16)691 CppTypeFor<TypeCategory::Real, 16> RTNAME(Norm2_16)(const Descriptor &x,
692     const char *source, int line, int dim, const Descriptor *mask) {
693   return GetTotalReduction<TypeCategory::Real, 16>(
694       x, source, line, dim, mask, Norm2Accumulator<16>{x}, "NORM2");
695 }
696 #endif
697 
RTNAME(Norm2Dim)698 void RTNAME(Norm2Dim)(Descriptor &result, const Descriptor &x, int dim,
699     const char *source, int line, const Descriptor *mask) {
700   Terminator terminator{source, line};
701   auto type{x.type().GetCategoryAndKind()};
702   RUNTIME_CHECK(terminator, type);
703   if (type->first == TypeCategory::Real) {
704     ApplyFloatingPointKind<Norm2Helper, void>(
705         type->second, terminator, result, x, dim, mask, terminator);
706   } else {
707     terminator.Crash("NORM2: bad type code %d", x.type().raw());
708   }
709 }
710 } // extern "C"
711 } // namespace Fortran::runtime
712