From f56cec1eb397068dccb3faef1cb7f5ce8675d4f8 Mon Sep 17 00:00:00 2001
From: Yusuke Endoh <mame@ruby-lang.org>
Date: Mon, 14 Mar 2016 21:38:09 +0900
Subject: [PATCH 2/3] introduce Array#max and Array#min

* array.c (rb_ary_max, rb_ary_min): added.

* internal.h (OPTIMIZED_CMP): moved from enum.c.
---
 array.c    | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 enum.c     |  7 -----
 internal.h |  7 +++++
 3 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/array.c b/array.c
index 120c2f8..f59b4b9 100644
--- a/array.c
+++ b/array.c
@@ -4178,6 +4178,96 @@ rb_ary_or(VALUE ary1, VALUE ary2)
     return ary3;
 }
 
+/*
+ *  call-seq:
+ *     ary.max                   -> obj
+ *     ary.max { |a, b| block }  -> obj
+ *     ary.max(n)                -> array
+ *     ary.max(n) {|a,b| block } -> array
+ *
+ *  Returns the object in _ary_ with the maximum value. The
+ *  first form assumes all objects implement <code>Comparable</code>;
+ *  the second uses the block to return <em>a <=> b</em>.
+ *
+ *     a = %w(albatross dog horse)
+ *     a.max                                   #=> "horse"
+ *     a.max { |a, b| a.length <=> b.length }  #=> "albatross"
+ *
+ *  If the +n+ argument is given, maximum +n+ elements are returned
+ *  as an array.
+ *
+ *     a = %w[albatross dog horse]
+ *     a.max(2)                                  #=> ["horse", "dog"]
+ *     a.max(2) {|a, b| a.length <=> b.length }  #=> ["albatross", "horse"]
+ */
+static VALUE
+rb_ary_max(int argc, VALUE *argv, VALUE ary)
+{
+    struct cmp_opt_data cmp_opt = { 0, 0 };
+    VALUE result = Qundef, v;
+    VALUE num;
+    long i;
+
+    rb_scan_args(argc, argv, "01", &num);
+
+    if (!NIL_P(num) || rb_block_given_p())
+       return rb_call_super(argc, argv); /* XXX: should redefine? */
+
+    for (i = 0; i < RARRAY_LEN(ary); i++) {
+       v = RARRAY_AREF(ary, i);
+       if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) > 0) {
+           result = v;
+       }
+    }
+    if (result == Qundef) return Qnil;
+    return result;
+}
+
+/*
+ *  call-seq:
+ *     ary.min                     -> obj
+ *     ary.min {| a,b | block }    -> obj
+ *     ary.min(n)                  -> array
+ *     ary.min(n) {| a,b | block } -> array
+ *
+ *  Returns the object in _ary_ with the minimum value. The
+ *  first form assumes all objects implement <code>Comparable</code>;
+ *  the second uses the block to return <em>a <=> b</em>.
+ *
+ *     a = %w(albatross dog horse)
+ *     a.min                                   #=> "albatross"
+ *     a.min { |a, b| a.length <=> b.length }  #=> "dog"
+ *
+ *  If the +n+ argument is given, minimum +n+ elements are returned
+ *  as an array.
+ *
+ *     a = %w[albatross dog horse]
+ *     a.min(2)                                  #=> ["albatross", "dog"]
+ *     a.min(2) {|a, b| a.length <=> b.length }  #=> ["dog", "horse"]
+ */
+static VALUE
+rb_ary_min(int argc, VALUE *argv, VALUE ary)
+{
+    struct cmp_opt_data cmp_opt = { 0, 0 };
+    VALUE result = Qundef, v;
+    VALUE num;
+    long i;
+
+    rb_scan_args(argc, argv, "01", &num);
+
+    if (!NIL_P(num) || rb_block_given_p())
+       return rb_call_super(argc, argv); /* XXX: should redefine? */
+
+    for (i = 0; i < RARRAY_LEN(ary); i++) {
+       v = RARRAY_AREF(ary, i);
+       if (result == Qundef || OPTIMIZED_CMP(v, result, cmp_opt) < 0) {
+           result = v;
+       }
+    }
+    if (result == Qundef) return Qnil;
+    return result;
+}
+
 static int
 push_value(st_data_t key, st_data_t val, st_data_t ary)
 {
@@ -5867,6 +5957,9 @@ Init_Array(void)
     rb_define_method(rb_cArray, "&", rb_ary_and, 1);
     rb_define_method(rb_cArray, "|", rb_ary_or, 1);
 
+    rb_define_method(rb_cArray, "max", rb_ary_max, -1);
+    rb_define_method(rb_cArray, "min", rb_ary_min, -1);
+
     rb_define_method(rb_cArray, "uniq", rb_ary_uniq, 0);
     rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0);
     rb_define_method(rb_cArray, "compact", rb_ary_compact, 0);
diff --git a/enum.c b/enum.c
index 1541f61..85bc76b 100644
--- a/enum.c
+++ b/enum.c
@@ -1412,13 +1412,6 @@ enum_none(VALUE obj)
     return memo->v1;
 }
 
-#define OPTIMIZED_CMP(a, b, data) \
-    ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data, Fixnum)) ? \
-     (((long)a > (long)b) ? 1 : ((long)a < (long)b) ? -1 : 0) : \
-     (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data, String)) ? \
-     rb_str_cmp(a, b) : \
-     rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b))
-
 struct min_t {
     VALUE min;
     struct cmp_opt_data cmp_opt;
diff --git a/internal.h b/internal.h
index aab4734..227fe88 100644
--- a/internal.h
+++ b/internal.h
@@ -698,6 +698,13 @@ struct cmp_opt_data {
       rb_method_basic_definition_p(TOKEN_PASTE(rb_c,type), id_cmp) && \
       ((data).opt_methods |= CMP_OPTIMIZABLE_BIT(type))))
 
+#define OPTIMIZED_CMP(a, b, data) \
+    ((FIXNUM_P(a) && FIXNUM_P(b) && CMP_OPTIMIZABLE(data, Fixnum)) ? \
+     (((long)a > (long)b) ? 1 : ((long)a < (long)b) ? -1 : 0) : \
+     (STRING_P(a) && STRING_P(b) && CMP_OPTIMIZABLE(data, String)) ? \
+     rb_str_cmp(a, b) : \
+     rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b))
+
 /* ment is in method.h */
 
 /* global variable */
-- 
2.5.3

