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.
11 
12 #include "character.h"
13 #include "reduction-templates.h"
14 #include "reduction.h"
15 #include "flang/Common/long-double.h"
16 #include <cinttypes>
17 
18 namespace Fortran::runtime {
19 // MAXLOC & MINLOC
20 
21 template <typename T, bool IS_MAX, bool BACK> struct NumericCompare {
22   using Type = T;
23   explicit NumericCompare(std::size_t /*elemLen; ignored*/) {}
24   bool operator()(const T &value, const T &previous) const {
25     if (value == previous) {
26       return BACK;
27     } else if constexpr (IS_MAX) {
28       return value > previous;
29     } else {
30       return value < previous;
31     }
32   }
33 };
34 
35 template <typename T, bool IS_MAX, bool BACK> class CharacterCompare {
36 public:
37   using Type = T;
38   explicit CharacterCompare(std::size_t elemLen)
39       : chars_{elemLen / sizeof(T)} {}
40   bool operator()(const T &value, const T &previous) const {
41     int cmp{CharacterScalarCompare<T>(&value, &previous, chars_, chars_)};
42     if (cmp == 0) {
43       return BACK;
44     } else if constexpr (IS_MAX) {
45       return cmp > 0;
46     } else {
47       return cmp < 0;
48     }
49   }
50 
51 private:
52   std::size_t chars_;
53 };
54 
55 template <typename COMPARE> class ExtremumLocAccumulator {
56 public:
57   using Type = typename COMPARE::Type;
58   ExtremumLocAccumulator(const Descriptor &array, std::size_t chars = 0)
59       : array_{array}, argRank_{array.rank()}, compare_{array.ElementBytes()} {
60     Reinitialize();
61   }
62   void Reinitialize() {
63     // per standard: result indices are all zero if no data
64     for (int j{0}; j < argRank_; ++j) {
65       extremumLoc_[j] = 0;
66     }
67     previous_ = nullptr;
68   }
69   int argRank() const { return argRank_; }
70   template <typename A> void GetResult(A *p, int zeroBasedDim = -1) {
71     if (zeroBasedDim >= 0) {
72       *p = extremumLoc_[zeroBasedDim] -
73           array_.GetDimension(zeroBasedDim).LowerBound() + 1;
74     } else {
75       for (int j{0}; j < argRank_; ++j) {
76         p[j] = extremumLoc_[j] - array_.GetDimension(j).LowerBound() + 1;
77       }
78     }
79   }
80   template <typename IGNORED> bool AccumulateAt(const SubscriptValue at[]) {
81     const auto &value{*array_.Element<Type>(at)};
82     if (!previous_ || compare_(value, *previous_)) {
83       previous_ = &value;
84       for (int j{0}; j < argRank_; ++j) {
85         extremumLoc_[j] = at[j];
86       }
87     }
88     return true;
89   }
90 
91 private:
92   const Descriptor &array_;
93   int argRank_;
94   SubscriptValue extremumLoc_[maxRank];
95   const Type *previous_{nullptr};
96   COMPARE compare_;
97 };
98 
99 template <typename ACCUMULATOR, typename CPPTYPE>
100 static void LocationHelper(const char *intrinsic, Descriptor &result,
101     const Descriptor &x, int kind, const Descriptor *mask,
102     Terminator &terminator) {
103   ACCUMULATOR accumulator{x};
104   DoTotalReduction<CPPTYPE>(x, 0, mask, accumulator, intrinsic, terminator);
105   ApplyIntegerKind<LocationResultHelper<ACCUMULATOR>::template Functor, void>(
106       kind, terminator, accumulator, result);
107 }
108 
109 template <TypeCategory CAT, int KIND, bool IS_MAX,
110     template <typename, bool, bool> class COMPARE>
111 inline void DoMaxOrMinLoc(const char *intrinsic, Descriptor &result,
112     const Descriptor &x, int kind, const char *source, int line,
113     const Descriptor *mask, bool back) {
114   using CppType = CppTypeFor<CAT, KIND>;
115   Terminator terminator{source, line};
116   if (back) {
117     LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, true>>,
118         CppType>(intrinsic, result, x, kind, mask, terminator);
119   } else {
120     LocationHelper<ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, false>>,
121         CppType>(intrinsic, result, x, kind, mask, terminator);
122   }
123 }
124 
125 template <TypeCategory CAT, bool IS_MAX> struct TypedMaxOrMinLocHelper {
126   template <int KIND> struct Functor {
127     void operator()(const char *intrinsic, Descriptor &result,
128         const Descriptor &x, int kind, const char *source, int line,
129         const Descriptor *mask, bool back) const {
130       DoMaxOrMinLoc<TypeCategory::Integer, KIND, IS_MAX, NumericCompare>(
131           intrinsic, result, x, kind, source, line, mask, back);
132     }
133   };
134 };
135 
136 template <bool IS_MAX>
137 inline void TypedMaxOrMinLoc(const char *intrinsic, Descriptor &result,
138     const Descriptor &x, int kind, const char *source, int line,
139     const Descriptor *mask, bool back) {
140   int rank{x.rank()};
141   SubscriptValue extent[1]{rank};
142   result.Establish(TypeCategory::Integer, kind, nullptr, 1, extent,
143       CFI_attribute_allocatable);
144   result.GetDimension(0).SetBounds(1, extent[0]);
145   Terminator terminator{source, line};
146   if (int stat{result.Allocate()}) {
147     terminator.Crash(
148         "%s: could not allocate memory for result; STAT=%d", intrinsic, stat);
149   }
150   CheckIntegerKind(terminator, kind, intrinsic);
151   auto catKind{x.type().GetCategoryAndKind()};
152   RUNTIME_CHECK(terminator, catKind.has_value());
153   switch (catKind->first) {
154   case TypeCategory::Integer:
155     ApplyIntegerKind<
156         TypedMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX>::template Functor,
157         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
158         line, mask, back);
159     break;
160   case TypeCategory::Real:
161     ApplyFloatingPointKind<
162         TypedMaxOrMinLocHelper<TypeCategory::Real, IS_MAX>::template Functor,
163         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
164         line, mask, back);
165     break;
166   case TypeCategory::Character:
167     ApplyCharacterKind<TypedMaxOrMinLocHelper<TypeCategory::Character,
168                            IS_MAX>::template Functor,
169         void>(catKind->second, terminator, intrinsic, result, x, kind, source,
170         line, mask, back);
171     break;
172   default:
173     terminator.Crash(
174         "%s: Bad data type code (%d) for array", intrinsic, x.type().raw());
175   }
176 }
177 
178 extern "C" {
179 void RTNAME(Maxloc)(Descriptor &result, const Descriptor &x, int kind,
180     const char *source, int line, const Descriptor *mask, bool back) {
181   TypedMaxOrMinLoc<true>("MAXLOC", result, x, kind, source, line, mask, back);
182 }
183 void RTNAME(Minloc)(Descriptor &result, const Descriptor &x, int kind,
184     const char *source, int line, const Descriptor *mask, bool back) {
185   TypedMaxOrMinLoc<false>("MINLOC", result, x, kind, source, line, mask, back);
186 }
187 } // extern "C"
188 
189 // MAXLOC/MINLOC with DIM=
190 
191 template <TypeCategory CAT, int KIND, bool IS_MAX,
192     template <typename, bool, bool> class COMPARE, bool BACK>
193 static void DoPartialMaxOrMinLocDirection(const char *intrinsic,
194     Descriptor &result, const Descriptor &x, int kind, int dim,
195     const Descriptor *mask, Terminator &terminator) {
196   using CppType = CppTypeFor<CAT, KIND>;
197   using Accumulator = ExtremumLocAccumulator<COMPARE<CppType, IS_MAX, BACK>>;
198   Accumulator accumulator{x};
199   ApplyIntegerKind<PartialLocationHelper<Accumulator>::template Functor, void>(
200       kind, terminator, result, x, dim, mask, terminator, intrinsic,
201       accumulator);
202 }
203 
204 template <TypeCategory CAT, int KIND, bool IS_MAX,
205     template <typename, bool, bool> class COMPARE>
206 inline void DoPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result,
207     const Descriptor &x, int kind, int dim, const Descriptor *mask, bool back,
208     Terminator &terminator) {
209   if (back) {
210     DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, true>(
211         intrinsic, result, x, kind, dim, mask, terminator);
212   } else {
213     DoPartialMaxOrMinLocDirection<CAT, KIND, IS_MAX, COMPARE, false>(
214         intrinsic, result, x, kind, dim, mask, terminator);
215   }
216 }
217 
218 template <TypeCategory CAT, bool IS_MAX,
219     template <typename, bool, bool> class COMPARE>
220 struct DoPartialMaxOrMinLocHelper {
221   template <int KIND> struct Functor {
222     void operator()(const char *intrinsic, Descriptor &result,
223         const Descriptor &x, int kind, int dim, const Descriptor *mask,
224         bool back, Terminator &terminator) const {
225       DoPartialMaxOrMinLoc<CAT, KIND, IS_MAX, COMPARE>(
226           intrinsic, result, x, kind, dim, mask, back, terminator);
227     }
228   };
229 };
230 
231 template <bool IS_MAX>
232 inline void TypedPartialMaxOrMinLoc(const char *intrinsic, Descriptor &result,
233     const Descriptor &x, int kind, int dim, const char *source, int line,
234     const Descriptor *mask, bool back) {
235   Terminator terminator{source, line};
236   CheckIntegerKind(terminator, kind, intrinsic);
237   auto catKind{x.type().GetCategoryAndKind()};
238   RUNTIME_CHECK(terminator, catKind.has_value());
239   switch (catKind->first) {
240   case TypeCategory::Integer:
241     ApplyIntegerKind<DoPartialMaxOrMinLocHelper<TypeCategory::Integer, IS_MAX,
242                          NumericCompare>::template Functor,
243         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
244         mask, back, terminator);
245     break;
246   case TypeCategory::Real:
247     ApplyFloatingPointKind<DoPartialMaxOrMinLocHelper<TypeCategory::Real,
248                                IS_MAX, NumericCompare>::template Functor,
249         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
250         mask, back, terminator);
251     break;
252   case TypeCategory::Character:
253     ApplyCharacterKind<DoPartialMaxOrMinLocHelper<TypeCategory::Character,
254                            IS_MAX, CharacterCompare>::template Functor,
255         void>(catKind->second, terminator, intrinsic, result, x, kind, dim,
256         mask, back, terminator);
257     break;
258   default:
259     terminator.Crash(
260         "%s: Bad data type code (%d) for array", intrinsic, x.type().raw());
261   }
262 }
263 
264 extern "C" {
265 void RTNAME(MaxlocDim)(Descriptor &result, const Descriptor &x, int kind,
266     int dim, const char *source, int line, const Descriptor *mask, bool back) {
267   TypedPartialMaxOrMinLoc<true>(
268       "MAXLOC", result, x, kind, dim, source, line, mask, back);
269 }
270 void RTNAME(MinlocDim)(Descriptor &result, const Descriptor &x, int kind,
271     int dim, const char *source, int line, const Descriptor *mask, bool back) {
272   TypedPartialMaxOrMinLoc<false>(
273       "MINLOC", result, x, kind, dim, source, line, mask, back);
274 }
275 } // extern "C"
276 
277 // MAXVAL and MINVAL
278 
279 template <TypeCategory CAT, int KIND, bool IS_MAXVAL> struct MaxOrMinIdentity {
280   using Type = CppTypeFor<CAT, KIND>;
281   static constexpr Type Value() {
282     return IS_MAXVAL ? std::numeric_limits<Type>::lowest()
283                      : std::numeric_limits<Type>::max();
284   }
285 };
286 
287 // std::numeric_limits<> may not know int128_t
288 template <bool IS_MAXVAL>
289 struct MaxOrMinIdentity<TypeCategory::Integer, 16, IS_MAXVAL> {
290   using Type = CppTypeFor<TypeCategory::Integer, 16>;
291   static constexpr Type Value() {
292     return IS_MAXVAL ? Type{1} << 127 : ~Type{0} >> 1;
293   }
294 };
295 
296 template <TypeCategory CAT, int KIND, bool IS_MAXVAL>
297 class NumericExtremumAccumulator {
298 public:
299   using Type = CppTypeFor<CAT, KIND>;
300   explicit NumericExtremumAccumulator(const Descriptor &array)
301       : array_{array} {}
302   void Reinitialize() {
303     extremum_ = MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value();
304   }
305   template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
306     *p = extremum_;
307   }
308   bool Accumulate(Type x) {
309     if constexpr (IS_MAXVAL) {
310       if (x > extremum_) {
311         extremum_ = x;
312       }
313     } else if (x < extremum_) {
314       extremum_ = x;
315     }
316     return true;
317   }
318   template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
319     return Accumulate(*array_.Element<A>(at));
320   }
321 
322 private:
323   const Descriptor &array_;
324   Type extremum_{MaxOrMinIdentity<CAT, KIND, IS_MAXVAL>::Value()};
325 };
326 
327 template <TypeCategory CAT, int KIND, bool IS_MAXVAL>
328 inline CppTypeFor<CAT, KIND> TotalNumericMaxOrMin(const Descriptor &x,
329     const char *source, int line, int dim, const Descriptor *mask,
330     const char *intrinsic) {
331   return GetTotalReduction<CAT, KIND>(x, source, line, dim, mask,
332       NumericExtremumAccumulator<CAT, KIND, IS_MAXVAL>{x}, intrinsic);
333 }
334 
335 template <TypeCategory CAT, int KIND, bool IS_MAXVAL,
336     template <TypeCategory, int, bool> class ACCUMULATOR>
337 static void DoMaxOrMin(Descriptor &result, const Descriptor &x, int dim,
338     const Descriptor *mask, const char *intrinsic, Terminator &terminator) {
339   using Type = CppTypeFor<CAT, KIND>;
340   if (dim == 0 || x.rank() == 1) {
341     // Total reduction
342     result.Establish(x.type(), x.ElementBytes(), nullptr, 0, nullptr,
343         CFI_attribute_allocatable);
344     if (int stat{result.Allocate()}) {
345       terminator.Crash(
346           "%s: could not allocate memory for result; STAT=%d", intrinsic, stat);
347     }
348     ACCUMULATOR<CAT, KIND, IS_MAXVAL> accumulator{x};
349     DoTotalReduction<Type>(x, dim, mask, accumulator, intrinsic, terminator);
350     accumulator.GetResult(result.OffsetElement<Type>());
351   } else {
352     // Partial reduction
353     using Accumulator = ACCUMULATOR<CAT, KIND, IS_MAXVAL>;
354     Accumulator accumulator{x};
355     PartialReduction<Accumulator, CAT, KIND>(
356         result, x, dim, mask, terminator, intrinsic, accumulator);
357   }
358 }
359 
360 template <TypeCategory CAT, bool IS_MAXVAL> struct MaxOrMinHelper {
361   template <int KIND> struct Functor {
362     void operator()(Descriptor &result, const Descriptor &x, int dim,
363         const Descriptor *mask, const char *intrinsic,
364         Terminator &terminator) const {
365       DoMaxOrMin<CAT, KIND, IS_MAXVAL, NumericExtremumAccumulator>(
366           result, x, dim, mask, intrinsic, terminator);
367     }
368   };
369 };
370 
371 template <bool IS_MAXVAL>
372 inline void NumericMaxOrMin(Descriptor &result, const Descriptor &x, int dim,
373     const char *source, int line, const Descriptor *mask,
374     const char *intrinsic) {
375   Terminator terminator{source, line};
376   auto type{x.type().GetCategoryAndKind()};
377   RUNTIME_CHECK(terminator, type);
378   switch (type->first) {
379   case TypeCategory::Integer:
380     ApplyIntegerKind<
381         MaxOrMinHelper<TypeCategory::Integer, IS_MAXVAL>::template Functor,
382         void>(
383         type->second, terminator, result, x, dim, mask, intrinsic, terminator);
384     break;
385   case TypeCategory::Real:
386     ApplyFloatingPointKind<
387         MaxOrMinHelper<TypeCategory::Real, IS_MAXVAL>::template Functor, void>(
388         type->second, terminator, result, x, dim, mask, intrinsic, terminator);
389     break;
390   default:
391     terminator.Crash("%s: bad type code %d", intrinsic, x.type().raw());
392   }
393 }
394 
395 template <TypeCategory, int KIND, bool IS_MAXVAL>
396 class CharacterExtremumAccumulator {
397 public:
398   using Type = CppTypeFor<TypeCategory::Character, KIND>;
399   explicit CharacterExtremumAccumulator(const Descriptor &array)
400       : array_{array}, charLen_{array_.ElementBytes() / KIND} {}
401   void Reinitialize() { extremum_ = nullptr; }
402   template <typename A> void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
403     static_assert(std::is_same_v<A, Type>);
404     if (extremum_) {
405       std::memcpy(p, extremum_, charLen_);
406     } else {
407       // empty array: result is all zero-valued characters
408       std::memset(p, 0, charLen_);
409     }
410   }
411   bool Accumulate(const Type *x) {
412     if (!extremum_) {
413       extremum_ = x;
414     } else {
415       int cmp{CharacterScalarCompare(x, extremum_, charLen_, charLen_)};
416       if (IS_MAXVAL == (cmp > 0)) {
417         extremum_ = x;
418       }
419     }
420     return true;
421   }
422   template <typename A> bool AccumulateAt(const SubscriptValue at[]) {
423     return Accumulate(array_.Element<A>(at));
424   }
425 
426 private:
427   const Descriptor &array_;
428   std::size_t charLen_;
429   const Type *extremum_{nullptr};
430 };
431 
432 template <bool IS_MAXVAL> struct CharacterMaxOrMinHelper {
433   template <int KIND> struct Functor {
434     void operator()(Descriptor &result, const Descriptor &x, int dim,
435         const Descriptor *mask, const char *intrinsic,
436         Terminator &terminator) const {
437       DoMaxOrMin<TypeCategory::Character, KIND, IS_MAXVAL,
438           CharacterExtremumAccumulator>(
439           result, x, dim, mask, intrinsic, terminator);
440     }
441   };
442 };
443 
444 template <bool IS_MAXVAL>
445 inline void CharacterMaxOrMin(Descriptor &result, const Descriptor &x, int dim,
446     const char *source, int line, const Descriptor *mask,
447     const char *intrinsic) {
448   Terminator terminator{source, line};
449   auto type{x.type().GetCategoryAndKind()};
450   RUNTIME_CHECK(terminator, type && type->first == TypeCategory::Character);
451   ApplyCharacterKind<CharacterMaxOrMinHelper<IS_MAXVAL>::template Functor,
452       void>(
453       type->second, terminator, result, x, dim, mask, intrinsic, terminator);
454 }
455 
456 extern "C" {
457 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MaxvalInteger1)(const Descriptor &x,
458     const char *source, int line, int dim, const Descriptor *mask) {
459   return TotalNumericMaxOrMin<TypeCategory::Integer, 1, true>(
460       x, source, line, dim, mask, "MAXVAL");
461 }
462 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MaxvalInteger2)(const Descriptor &x,
463     const char *source, int line, int dim, const Descriptor *mask) {
464   return TotalNumericMaxOrMin<TypeCategory::Integer, 2, true>(
465       x, source, line, dim, mask, "MAXVAL");
466 }
467 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MaxvalInteger4)(const Descriptor &x,
468     const char *source, int line, int dim, const Descriptor *mask) {
469   return TotalNumericMaxOrMin<TypeCategory::Integer, 4, true>(
470       x, source, line, dim, mask, "MAXVAL");
471 }
472 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MaxvalInteger8)(const Descriptor &x,
473     const char *source, int line, int dim, const Descriptor *mask) {
474   return TotalNumericMaxOrMin<TypeCategory::Integer, 8, true>(
475       x, source, line, dim, mask, "MAXVAL");
476 }
477 #ifdef __SIZEOF_INT128__
478 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MaxvalInteger16)(
479     const Descriptor &x, const char *source, int line, int dim,
480     const Descriptor *mask) {
481   return TotalNumericMaxOrMin<TypeCategory::Integer, 16, true>(
482       x, source, line, dim, mask, "MAXVAL");
483 }
484 #endif
485 
486 // TODO: REAL(2 & 3)
487 CppTypeFor<TypeCategory::Real, 4> RTNAME(MaxvalReal4)(const Descriptor &x,
488     const char *source, int line, int dim, const Descriptor *mask) {
489   return TotalNumericMaxOrMin<TypeCategory::Real, 4, true>(
490       x, source, line, dim, mask, "MAXVAL");
491 }
492 CppTypeFor<TypeCategory::Real, 8> RTNAME(MaxvalReal8)(const Descriptor &x,
493     const char *source, int line, int dim, const Descriptor *mask) {
494   return TotalNumericMaxOrMin<TypeCategory::Real, 8, true>(
495       x, source, line, dim, mask, "MAXVAL");
496 }
497 #if LONG_DOUBLE == 80
498 CppTypeFor<TypeCategory::Real, 10> RTNAME(MaxvalReal10)(const Descriptor &x,
499     const char *source, int line, int dim, const Descriptor *mask) {
500   return TotalNumericMaxOrMin<TypeCategory::Real, 10, true>(
501       x, source, line, dim, mask, "MAXVAL");
502 }
503 #elif LONG_DOUBLE == 128
504 CppTypeFor<TypeCategory::Real, 16> RTNAME(MaxvalReal16)(const Descriptor &x,
505     const char *source, int line, int dim, const Descriptor *mask) {
506   return TotalNumericMaxOrMin<TypeCategory::Real, 16, true>(
507       x, source, line, dim, mask, "MAXVAL");
508 }
509 #endif
510 
511 void RTNAME(MaxvalCharacter)(Descriptor &result, const Descriptor &x,
512     const char *source, int line, const Descriptor *mask) {
513   CharacterMaxOrMin<true>(result, x, 0, source, line, mask, "MAXVAL");
514 }
515 
516 CppTypeFor<TypeCategory::Integer, 1> RTNAME(MinvalInteger1)(const Descriptor &x,
517     const char *source, int line, int dim, const Descriptor *mask) {
518   return TotalNumericMaxOrMin<TypeCategory::Integer, 1, false>(
519       x, source, line, dim, mask, "MINVAL");
520 }
521 CppTypeFor<TypeCategory::Integer, 2> RTNAME(MinvalInteger2)(const Descriptor &x,
522     const char *source, int line, int dim, const Descriptor *mask) {
523   return TotalNumericMaxOrMin<TypeCategory::Integer, 2, false>(
524       x, source, line, dim, mask, "MINVAL");
525 }
526 CppTypeFor<TypeCategory::Integer, 4> RTNAME(MinvalInteger4)(const Descriptor &x,
527     const char *source, int line, int dim, const Descriptor *mask) {
528   return TotalNumericMaxOrMin<TypeCategory::Integer, 4, false>(
529       x, source, line, dim, mask, "MINVAL");
530 }
531 CppTypeFor<TypeCategory::Integer, 8> RTNAME(MinvalInteger8)(const Descriptor &x,
532     const char *source, int line, int dim, const Descriptor *mask) {
533   return TotalNumericMaxOrMin<TypeCategory::Integer, 8, false>(
534       x, source, line, dim, mask, "MINVAL");
535 }
536 #ifdef __SIZEOF_INT128__
537 CppTypeFor<TypeCategory::Integer, 16> RTNAME(MinvalInteger16)(
538     const Descriptor &x, const char *source, int line, int dim,
539     const Descriptor *mask) {
540   return TotalNumericMaxOrMin<TypeCategory::Integer, 16, false>(
541       x, source, line, dim, mask, "MINVAL");
542 }
543 #endif
544 
545 // TODO: REAL(2 & 3)
546 CppTypeFor<TypeCategory::Real, 4> RTNAME(MinvalReal4)(const Descriptor &x,
547     const char *source, int line, int dim, const Descriptor *mask) {
548   return TotalNumericMaxOrMin<TypeCategory::Real, 4, false>(
549       x, source, line, dim, mask, "MINVAL");
550 }
551 CppTypeFor<TypeCategory::Real, 8> RTNAME(MinvalReal8)(const Descriptor &x,
552     const char *source, int line, int dim, const Descriptor *mask) {
553   return TotalNumericMaxOrMin<TypeCategory::Real, 8, false>(
554       x, source, line, dim, mask, "MINVAL");
555 }
556 #if LONG_DOUBLE == 80
557 CppTypeFor<TypeCategory::Real, 10> RTNAME(MinvalReal10)(const Descriptor &x,
558     const char *source, int line, int dim, const Descriptor *mask) {
559   return TotalNumericMaxOrMin<TypeCategory::Real, 10, false>(
560       x, source, line, dim, mask, "MINVAL");
561 }
562 #elif LONG_DOUBLE == 128
563 CppTypeFor<TypeCategory::Real, 16> RTNAME(MinvalReal16)(const Descriptor &x,
564     const char *source, int line, int dim, const Descriptor *mask) {
565   return TotalNumericMaxOrMin<TypeCategory::Real, 16, false>(
566       x, source, line, dim, mask, "MINVAL");
567 }
568 #endif
569 
570 void RTNAME(MinvalCharacter)(Descriptor &result, const Descriptor &x,
571     const char *source, int line, const Descriptor *mask) {
572   CharacterMaxOrMin<false>(result, x, 0, source, line, mask, "MINVAL");
573 }
574 
575 void RTNAME(MaxvalDim)(Descriptor &result, const Descriptor &x, int dim,
576     const char *source, int line, const Descriptor *mask) {
577   if (x.type().IsCharacter()) {
578     CharacterMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL");
579   } else {
580     NumericMaxOrMin<true>(result, x, dim, source, line, mask, "MAXVAL");
581   }
582 }
583 void RTNAME(MinvalDim)(Descriptor &result, const Descriptor &x, int dim,
584     const char *source, int line, const Descriptor *mask) {
585   if (x.type().IsCharacter()) {
586     CharacterMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL");
587   } else {
588     NumericMaxOrMin<false>(result, x, dim, source, line, mask, "MINVAL");
589   }
590 }
591 } // extern "C"
592 } // namespace Fortran::runtime
593