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