@@ -604,7 +604,7 @@ julia> prod(1:5; init = 1.0)
604604"""
605605prod (a; kw... ) = mapreduce (identity, mul_prod, a; kw... )
606606
607- # # maximum & minimum
607+ # # maximum, minimum, & extrema
608608_fast (:: typeof (min),x,y) = min (x,y)
609609_fast (:: typeof (max),x,y) = max (x,y)
610610function _fast (:: typeof (max), x:: AbstractFloat , y:: AbstractFloat )
@@ -634,11 +634,6 @@ function mapreduce_impl(f, op::Union{typeof(max), typeof(min)},
634634 start = first + 1
635635 simdstop = start + chunk_len - 4
636636 while simdstop <= last - 3
637- # short circuit in case of NaN or missing
638- (v1 == v1) === true || return v1
639- (v2 == v2) === true || return v2
640- (v3 == v3) === true || return v3
641- (v4 == v4) === true || return v4
642637 @inbounds for i in start: 4 : simdstop
643638 v1 = _fast (op, v1, f (A[i+ 0 ]))
644639 v2 = _fast (op, v2, f (A[i+ 1 ]))
785780"""
786781minimum (a; kw... ) = mapreduce (identity, min, a; kw... )
787782
783+ """
784+ extrema(itr; [init]) -> (mn, mx)
785+
786+ Compute both the minimum `mn` and maximum `mx` element in a single pass, and return them
787+ as a 2-tuple.
788+
789+ The value returned for empty `itr` can be specified by `init`. It must be a 2-tuple whose
790+ first and second elements are neutral elements for `min` and `max` respectively
791+ (i.e. which are greater/less than or equal to any other element). As a consequence, when
792+ `itr` is empty the returned `(mn, mx)` tuple will satisfy `mn ≥ mx`. When `init` is
793+ specified it may be used even for non-empty `itr`.
794+
795+ !!! compat "Julia 1.8"
796+ Keyword argument `init` requires Julia 1.8 or later.
797+
798+ # Examples
799+ ```jldoctest
800+ julia> extrema(2:10)
801+ (2, 10)
802+
803+ julia> extrema([9,pi,4.5])
804+ (3.141592653589793, 9.0)
805+
806+ julia> extrema([]; init = (Inf, -Inf))
807+ (Inf, -Inf)
808+ ```
809+ """
810+ extrema (itr; kw... ) = extrema (identity, itr; kw... )
811+
812+ """
813+ extrema(f, itr; [init]) -> (mn, mx)
814+
815+ Compute both the minimum `mn` and maximum `mx` of `f` applied to each element in `itr` and
816+ return them as a 2-tuple. Only one pass is made over `itr`.
817+
818+ The value returned for empty `itr` can be specified by `init`. It must be a 2-tuple whose
819+ first and second elements are neutral elements for `min` and `max` respectively
820+ (i.e. which are greater/less than or equal to any other element). It is used for non-empty
821+ collections. Note: it implies that, for empty `itr`, the returned value `(mn, mx)` satisfies
822+ `mn ≥ mx` even though for non-empty `itr` it satisfies `mn ≤ mx`. This is a "paradoxical"
823+ but yet expected result.
824+
825+ !!! compat "Julia 1.2"
826+ This method requires Julia 1.2 or later.
827+
828+ !!! compat "Julia 1.8"
829+ Keyword argument `init` requires Julia 1.8 or later.
830+
831+ # Examples
832+ ```jldoctest
833+ julia> extrema(sin, 0:π)
834+ (0.0, 0.9092974268256817)
835+
836+ julia> extrema(sin, Real[]; init = (1.0, -1.0)) # good, since -1 ≤ sin(::Real) ≤ 1
837+ (1.0, -1.0)
838+ ```
839+ """
840+ extrema (f, itr; kw... ) = mapreduce (ExtremaMap (f), _extrema_rf, itr; kw... )
841+
842+ # Not using closure since `extrema(type, itr)` is a very likely use-case and it's better
843+ # to avoid type-instability (#23618).
844+ struct ExtremaMap{F} <: Function
845+ f:: F
846+ end
847+ ExtremaMap (:: Type{T} ) where {T} = ExtremaMap {Type{T}} (T)
848+ @inline (f:: ExtremaMap )(x) = (y = f. f (x); (y, y))
849+
850+ # TODO : optimize for inputs <: AbstractFloat
851+ @inline _extrema_rf ((min1, max1), (min2, max2)) = (min (min1, min2), max (max1, max2))
852+
788853# # findmax, findmin, argmax & argmin
789854
790855"""
0 commit comments