#include "ruby/ruby.h"
#include "ruby/util.h"
#include "node.h"
#include "id.h"
#include "internal.h"
#define STATIC_ASSERT(name, expr) typedef int static_assert_##name##_check[1 - 2*!(expr)]
VALUE rb_mEnumerable;
static ID id_next;
static ID id_div;
static ID id_call;
static ID id_size;
#define id_each idEach
#define id_eqq idEqq
#define id_cmp idCmp
#define id_lshift idLTLT
VALUE
rb_enum_values_pack(int argc, VALUE *argv)
{
if (argc == 0) return Qnil;
if (argc == 1) return argv[0];
return rb_ary_new4(argc, argv);
}
#define ENUM_WANT_SVALUE() do { \
i = rb_enum_values_pack(argc, argv); \
} while (0)
#define enum_yield rb_yield_values2
static VALUE
grep_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
ENUM_WANT_SVALUE();
if (RTEST(rb_funcall(memo->u1.value, id_eqq, 1, i))) {
rb_ary_push(memo->u2.value, i);
}
return Qnil;
}
static VALUE
grep_iter_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
ENUM_WANT_SVALUE();
if (RTEST(rb_funcall(memo->u1.value, id_eqq, 1, i))) {
rb_ary_push(memo->u2.value, rb_yield(i));
}
return Qnil;
}
static VALUE
enum_grep(VALUE obj, VALUE pat)
{
VALUE ary = rb_ary_new();
NODE *memo = NEW_MEMO(pat, ary, 0);
rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)memo);
return ary;
}
static VALUE
count_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
NODE *memo = RNODE(memop);
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->u1.value)) {
memo->u3.cnt++;
}
return Qnil;
}
static VALUE
count_iter_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
NODE *memo = RNODE(memop);
if (RTEST(enum_yield(argc, argv))) {
memo->u3.cnt++;
}
return Qnil;
}
static VALUE
count_all_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
NODE *memo = RNODE(memop);
memo->u3.cnt++;
return Qnil;
}
static VALUE
enum_count(int argc, VALUE *argv, VALUE obj)
{
VALUE item = Qnil;
NODE *memo;
rb_block_call_func *func;
if (argc == 0) {
if (rb_block_given_p()) {
func = count_iter_i;
}
else {
func = count_all_i;
}
}
else {
rb_scan_args(argc, argv, "1", &item);
if (rb_block_given_p()) {
rb_warn("given block not used");
}
func = count_i;
}
memo = NEW_MEMO(item, 0, 0);
rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
return INT2NUM(memo->u3.cnt);
}
static VALUE
find_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
if (RTEST(rb_yield(i))) {
NODE *memo = RNODE(memop);
memo->u1.value = i;
memo->u3.cnt = 1;
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_find(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
VALUE if_none;
rb_scan_args(argc, argv, "01", &if_none);
RETURN_ENUMERATOR(obj, argc, argv);
memo = NEW_MEMO(Qundef, 0, 0);
rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
if (memo->u3.cnt) {
return memo->u1.value;
}
if (!NIL_P(if_none)) {
return rb_funcall(if_none, id_call, 0, 0);
}
return Qnil;
}
static VALUE
find_index_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
NODE *memo = RNODE(memop);
ENUM_WANT_SVALUE();
if (rb_equal(i, memo->u2.value)) {
memo->u1.value = UINT2NUM(memo->u3.cnt);
rb_iter_break();
}
memo->u3.cnt++;
return Qnil;
}
static VALUE
find_index_iter_i(VALUE i, VALUE memop, int argc, VALUE *argv)
{
NODE *memo = RNODE(memop);
if (RTEST(enum_yield(argc, argv))) {
memo->u1.value = UINT2NUM(memo->u3.cnt);
rb_iter_break();
}
memo->u3.cnt++;
return Qnil;
}
static VALUE
enum_find_index(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
VALUE condition_value = Qnil;
rb_block_call_func *func;
if (argc == 0) {
RETURN_ENUMERATOR(obj, 0, 0);
func = find_index_iter_i;
}
else {
rb_scan_args(argc, argv, "1", &condition_value);
if (rb_block_given_p()) {
rb_warn("given block not used");
}
func = find_index_i;
}
memo = NEW_MEMO(Qnil, condition_value, 0);
rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
return memo->u1.value;
}
static VALUE
find_all_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
if (RTEST(rb_yield(i))) {
rb_ary_push(ary, i);
}
return Qnil;
}
static VALUE
enum_size(VALUE self, VALUE args)
{
VALUE r;
r = rb_check_funcall(self, id_size, 0, 0);
return (r == Qundef) ? Qnil : r;
}
static VALUE
enum_find_all(VALUE obj)
{
VALUE ary;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, find_all_i, ary);
return ary;
}
static VALUE
reject_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
if (!RTEST(rb_yield(i))) {
rb_ary_push(ary, i);
}
return Qnil;
}
static VALUE
enum_reject(VALUE obj)
{
VALUE ary;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, reject_i, ary);
return ary;
}
static VALUE
collect_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
rb_ary_push(ary, enum_yield(argc, argv));
return Qnil;
}
static VALUE
collect_all(VALUE i, VALUE ary, int argc, VALUE *argv)
{
rb_thread_check_ints();
rb_ary_push(ary, rb_enum_values_pack(argc, argv));
return Qnil;
}
static VALUE
enum_collect(VALUE obj)
{
VALUE ary;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, collect_i, ary);
return ary;
}
static VALUE
flat_map_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
VALUE tmp;
i = enum_yield(argc, argv);
tmp = rb_check_array_type(i);
if (NIL_P(tmp)) {
rb_ary_push(ary, i);
}
else {
rb_ary_concat(ary, tmp);
}
return Qnil;
}
static VALUE
enum_flat_map(VALUE obj)
{
VALUE ary;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);
return ary;
}
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
VALUE ary = rb_ary_new();
rb_block_call(obj, id_each, argc, argv, collect_all, ary);
OBJ_INFECT(ary, obj);
return ary;
}
static VALUE
inject_i(VALUE i, VALUE p, int argc, VALUE *argv)
{
NODE *memo = RNODE(p);
ENUM_WANT_SVALUE();
if (memo->u2.argc == 0) {
memo->u2.argc = 1;
memo->u1.value = i;
}
else {
memo->u1.value = rb_yield_values(2, memo->u1.value, i);
}
return Qnil;
}
static VALUE
inject_op_i(VALUE i, VALUE p, int argc, VALUE *argv)
{
NODE *memo = RNODE(p);
ENUM_WANT_SVALUE();
if (memo->u2.argc == 0) {
memo->u2.argc = 1;
memo->u1.value = i;
}
else {
memo->u1.value = rb_funcall(memo->u1.value, memo->u3.id, 1, i);
}
return Qnil;
}
static VALUE
enum_inject(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
VALUE init, op;
VALUE (*iter)(VALUE, VALUE, int, VALUE*) = inject_i;
switch (rb_scan_args(argc, argv, "02", &init, &op)) {
case 0:
break;
case 1:
if (rb_block_given_p()) {
break;
}
op = (VALUE)rb_to_id(init);
argc = 0;
init = Qnil;
iter = inject_op_i;
break;
case 2:
if (rb_block_given_p()) {
rb_warning("given block not used");
}
op = (VALUE)rb_to_id(op);
iter = inject_op_i;
break;
}
memo = NEW_MEMO(init, argc, op);
rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
return memo->u1.value;
}
static VALUE
partition_i(VALUE i, VALUE arys, int argc, VALUE *argv)
{
NODE *memo = RNODE(arys);
VALUE ary;
ENUM_WANT_SVALUE();
if (RTEST(rb_yield(i))) {
ary = memo->u1.value;
}
else {
ary = memo->u2.value;
}
rb_ary_push(ary, i);
return Qnil;
}
static VALUE
enum_partition(VALUE obj)
{
NODE *memo;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo = NEW_MEMO(rb_ary_new(), rb_ary_new(), 0);
rb_block_call(obj, id_each, 0, 0, partition_i, (VALUE)memo);
return rb_assoc_new(memo->u1.value, memo->u2.value);
}
static VALUE
group_by_i(VALUE i, VALUE hash, int argc, VALUE *argv)
{
VALUE group;
VALUE values;
ENUM_WANT_SVALUE();
group = rb_yield(i);
values = rb_hash_aref(hash, group);
if (!RB_TYPE_P(values, T_ARRAY)) {
values = rb_ary_new3(1, i);
rb_hash_aset(hash, group, values);
}
else {
rb_ary_push(values, i);
}
return Qnil;
}
static VALUE
enum_group_by(VALUE obj)
{
VALUE hash;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
hash = rb_hash_new();
rb_block_call(obj, id_each, 0, 0, group_by_i, hash);
OBJ_INFECT(hash, obj);
return hash;
}
static VALUE
first_i(VALUE i, VALUE params, int argc, VALUE *argv)
{
NODE *memo = RNODE(params);
ENUM_WANT_SVALUE();
memo->u1.value = i;
rb_iter_break();
UNREACHABLE;
}
static VALUE enum_take(VALUE obj, VALUE n);
static VALUE
enum_first(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
rb_check_arity(argc, 0, 1);
if (argc > 0) {
return enum_take(obj, argv[0]);
}
else {
memo = NEW_MEMO(Qnil, 0, 0);
rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
return memo->u1.value;
}
}
static VALUE
enum_sort(VALUE obj)
{
return rb_ary_sort(enum_to_a(0, 0, obj));
}
#define SORT_BY_BUFSIZE 16
struct sort_by_data {
VALUE ary;
VALUE buf;
long n;
};
static VALUE
sort_by_i(VALUE i, VALUE _data, int argc, VALUE *argv)
{
struct sort_by_data *data = (struct sort_by_data *)&RNODE(_data)->u1;
VALUE ary = data->ary;
VALUE v;
ENUM_WANT_SVALUE();
v = rb_yield(i);
if (RBASIC(ary)->klass) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
}
if (RARRAY_LEN(data->buf) != SORT_BY_BUFSIZE*2) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
}
RARRAY_PTR(data->buf)[data->n*2] = v;
RARRAY_PTR(data->buf)[data->n*2+1] = i;
data->n++;
if (data->n == SORT_BY_BUFSIZE) {
rb_ary_concat(ary, data->buf);
data->n = 0;
}
return Qnil;
}
static int
sort_by_cmp(const void *ap, const void *bp, void *data)
{
VALUE a;
VALUE b;
VALUE ary = (VALUE)data;
if (RBASIC(ary)->klass) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
}
a = *(VALUE *)ap;
b = *(VALUE *)bp;
return rb_cmpint(rb_funcall(a, id_cmp, 1, b), a, b);
}
static VALUE
enum_sort_by(VALUE obj)
{
VALUE ary, buf;
NODE *memo;
long i;
struct sort_by_data *data;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
if (RB_TYPE_P(obj, T_ARRAY) && RARRAY_LEN(obj) <= LONG_MAX/2) {
ary = rb_ary_new2(RARRAY_LEN(obj)*2);
}
else {
ary = rb_ary_new();
}
RBASIC(ary)->klass = 0;
buf = rb_ary_tmp_new(SORT_BY_BUFSIZE*2);
rb_ary_store(buf, SORT_BY_BUFSIZE*2-1, Qnil);
memo = NEW_MEMO(0, 0, 0);
OBJ_INFECT(memo, obj);
data = (struct sort_by_data *)&memo->u1;
data->ary = ary;
data->buf = buf;
data->n = 0;
rb_block_call(obj, id_each, 0, 0, sort_by_i, (VALUE)memo);
ary = data->ary;
buf = data->buf;
if (data->n) {
rb_ary_resize(buf, data->n*2);
rb_ary_concat(ary, buf);
}
if (RARRAY_LEN(ary) > 2) {
ruby_qsort(RARRAY_PTR(ary), RARRAY_LEN(ary)/2, 2*sizeof(VALUE),
sort_by_cmp, (void *)ary);
}
if (RBASIC(ary)->klass) {
rb_raise(rb_eRuntimeError, "sort_by reentered");
}
for (i=1; i<RARRAY_LEN(ary); i+=2) {
RARRAY_PTR(ary)[i/2] = RARRAY_PTR(ary)[i];
}
rb_ary_resize(ary, RARRAY_LEN(ary)/2);
RBASIC(ary)->klass = rb_cArray;
OBJ_INFECT(ary, memo);
return ary;
}
#define ENUMFUNC(name) rb_block_given_p() ? name##_iter_i : name##_i
#define DEFINE_ENUMFUNCS(name) \
static VALUE enum_##name##_func(VALUE result, NODE *memo); \
\
static VALUE \
name##_i(VALUE i, VALUE memo, int argc, VALUE *argv) \
{ \
return enum_##name##_func(rb_enum_values_pack(argc, argv), RNODE(memo)); \
} \
\
static VALUE \
name##_iter_i(VALUE i, VALUE memo, int argc, VALUE *argv) \
{ \
return enum_##name##_func(enum_yield(argc, argv), RNODE(memo)); \
} \
\
static VALUE \
enum_##name##_func(VALUE result, NODE *memo)
DEFINE_ENUMFUNCS(all)
{
if (!RTEST(result)) {
memo->u1.value = Qfalse;
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_all(VALUE obj)
{
NODE *memo = NEW_MEMO(Qtrue, 0, 0);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
return memo->u1.value;
}
DEFINE_ENUMFUNCS(any)
{
if (RTEST(result)) {
memo->u1.value = Qtrue;
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_any(VALUE obj)
{
NODE *memo = NEW_MEMO(Qfalse, 0, 0);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
return memo->u1.value;
}
DEFINE_ENUMFUNCS(one)
{
if (RTEST(result)) {
if (memo->u1.value == Qundef) {
memo->u1.value = Qtrue;
}
else if (memo->u1.value == Qtrue) {
memo->u1.value = Qfalse;
rb_iter_break();
}
}
return Qnil;
}
static VALUE
enum_one(VALUE obj)
{
NODE *memo = NEW_MEMO(Qundef, 0, 0);
VALUE result;
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
result = memo->u1.value;
if (result == Qundef) return Qfalse;
return result;
}
DEFINE_ENUMFUNCS(none)
{
if (RTEST(result)) {
memo->u1.value = Qfalse;
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_none(VALUE obj)
{
NODE *memo = NEW_MEMO(Qtrue, 0, 0);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
return memo->u1.value;
}
static VALUE
min_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
VALUE cmp;
NODE *memo = RNODE(args);
ENUM_WANT_SVALUE();
if (memo->u1.value == Qundef) {
memo->u1.value = i;
}
else {
cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
memo->u1.value = i;
}
}
return Qnil;
}
static VALUE
min_ii(VALUE i, VALUE args, int argc, VALUE *argv)
{
VALUE cmp;
NODE *memo = RNODE(args);
ENUM_WANT_SVALUE();
if (memo->u1.value == Qundef) {
memo->u1.value = i;
}
else {
cmp = rb_yield_values(2, i, memo->u1.value);
if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
memo->u1.value = i;
}
}
return Qnil;
}
static VALUE
enum_min(VALUE obj)
{
NODE *memo = NEW_MEMO(Qundef, 0, 0);
VALUE result;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, min_ii, (VALUE)memo);
}
else {
rb_block_call(obj, id_each, 0, 0, min_i, (VALUE)memo);
}
result = memo->u1.value;
if (result == Qundef) return Qnil;
return result;
}
static VALUE
max_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
VALUE cmp;
ENUM_WANT_SVALUE();
if (memo->u1.value == Qundef) {
memo->u1.value = i;
}
else {
cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
memo->u1.value = i;
}
}
return Qnil;
}
static VALUE
max_ii(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
VALUE cmp;
ENUM_WANT_SVALUE();
if (memo->u1.value == Qundef) {
memo->u1.value = i;
}
else {
cmp = rb_yield_values(2, i, memo->u1.value);
if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
memo->u1.value = i;
}
}
return Qnil;
}
static VALUE
enum_max(VALUE obj)
{
NODE *memo = NEW_MEMO(Qundef, 0, 0);
VALUE result;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
}
else {
rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
}
result = memo->u1.value;
if (result == Qundef) return Qnil;
return result;
}
struct minmax_t {
VALUE min;
VALUE max;
VALUE last;
};
STATIC_ASSERT(minmax_t, sizeof(struct minmax_t) <= sizeof(NODE) - offsetof(NODE, u1));
static void
minmax_i_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
if (memo->min == Qundef) {
memo->min = i;
memo->max = j;
}
else {
n = rb_cmpint(rb_funcall(i, id_cmp, 1, memo->min), i, memo->min);
if (n < 0) {
memo->min = i;
}
n = rb_cmpint(rb_funcall(j, id_cmp, 1, memo->max), j, memo->max);
if (n > 0) {
memo->max = j;
}
}
}
static VALUE
minmax_i(VALUE i, VALUE _memo, int argc, VALUE *argv)
{
struct minmax_t *memo = (struct minmax_t *)&RNODE(_memo)->u1.value;
int n;
VALUE j;
ENUM_WANT_SVALUE();
if (memo->last == Qundef) {
memo->last = i;
return Qnil;
}
j = memo->last;
memo->last = Qundef;
n = rb_cmpint(rb_funcall(j, id_cmp, 1, i), j, i);
if (n == 0)
i = j;
else if (n < 0) {
VALUE tmp;
tmp = i;
i = j;
j = tmp;
}
minmax_i_update(i, j, memo);
return Qnil;
}
static void
minmax_ii_update(VALUE i, VALUE j, struct minmax_t *memo)
{
int n;
if (memo->min == Qundef) {
memo->min = i;
memo->max = j;
}
else {
n = rb_cmpint(rb_yield_values(2, i, memo->min), i, memo->min);
if (n < 0) {
memo->min = i;
}
n = rb_cmpint(rb_yield_values(2, j, memo->max), j, memo->max);
if (n > 0) {
memo->max = j;
}
}
}
static VALUE
minmax_ii(VALUE i, VALUE _memo, int argc, VALUE *argv)
{
struct minmax_t *memo = (struct minmax_t *)&RNODE(_memo)->u1.value;
int n;
VALUE j;
ENUM_WANT_SVALUE();
if (memo->last == Qundef) {
memo->last = i;
return Qnil;
}
j = memo->last;
memo->last = Qundef;
n = rb_cmpint(rb_yield_values(2, j, i), j, i);
if (n == 0)
i = j;
else if (n < 0) {
VALUE tmp;
tmp = i;
i = j;
j = tmp;
}
minmax_ii_update(i, j, memo);
return Qnil;
}
static VALUE
enum_minmax(VALUE obj)
{
NODE *memo = NEW_MEMO(Qundef, Qundef, Qundef);
struct minmax_t *m = (struct minmax_t *)&memo->u1.value;
VALUE ary = rb_ary_new3(2, Qnil, Qnil);
m->min = Qundef;
m->last = Qundef;
if (rb_block_given_p()) {
rb_block_call(obj, id_each, 0, 0, minmax_ii, (VALUE)memo);
if (m->last != Qundef)
minmax_ii_update(m->last, m->last, m);
}
else {
rb_block_call(obj, id_each, 0, 0, minmax_i, (VALUE)memo);
if (m->last != Qundef)
minmax_i_update(m->last, m->last, m);
}
if (m->min != Qundef) {
rb_ary_store(ary, 0, m->min);
rb_ary_store(ary, 1, m->max);
}
return ary;
}
static VALUE
min_by_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
VALUE v;
ENUM_WANT_SVALUE();
v = rb_yield(i);
if (memo->u1.value == Qundef) {
memo->u1.value = v;
memo->u2.value = i;
}
else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->u1.value), v, memo->u1.value) < 0) {
memo->u1.value = v;
memo->u2.value = i;
}
return Qnil;
}
static VALUE
enum_min_by(VALUE obj)
{
NODE *memo;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo = NEW_MEMO(Qundef, Qnil, 0);
rb_block_call(obj, id_each, 0, 0, min_by_i, (VALUE)memo);
return memo->u2.value;
}
static VALUE
max_by_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
VALUE v;
ENUM_WANT_SVALUE();
v = rb_yield(i);
if (memo->u1.value == Qundef) {
memo->u1.value = v;
memo->u2.value = i;
}
else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->u1.value), v, memo->u1.value) > 0) {
memo->u1.value = v;
memo->u2.value = i;
}
return Qnil;
}
static VALUE
enum_max_by(VALUE obj)
{
NODE *memo;
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
memo = NEW_MEMO(Qundef, Qnil, 0);
rb_block_call(obj, id_each, 0, 0, max_by_i, (VALUE)memo);
return memo->u2.value;
}
struct minmax_by_t {
VALUE min_bv;
VALUE max_bv;
VALUE min;
VALUE max;
VALUE last_bv;
VALUE last;
};
static void
minmax_by_i_update(VALUE v1, VALUE v2, VALUE i1, VALUE i2, struct minmax_by_t *memo)
{
if (memo->min_bv == Qundef) {
memo->min_bv = v1;
memo->max_bv = v2;
memo->min = i1;
memo->max = i2;
}
else {
if (rb_cmpint(rb_funcall(v1, id_cmp, 1, memo->min_bv), v1, memo->min_bv) < 0) {
memo->min_bv = v1;
memo->min = i1;
}
if (rb_cmpint(rb_funcall(v2, id_cmp, 1, memo->max_bv), v2, memo->max_bv) > 0) {
memo->max_bv = v2;
memo->max = i2;
}
}
}
static VALUE
minmax_by_i(VALUE i, VALUE _memo, int argc, VALUE *argv)
{
struct minmax_by_t *memo = MEMO_FOR(struct minmax_by_t, _memo);
VALUE vi, vj, j;
int n;
ENUM_WANT_SVALUE();
vi = rb_yield(i);
if (memo->last_bv == Qundef) {
memo->last_bv = vi;
memo->last = i;
return Qnil;
}
vj = memo->last_bv;
j = memo->last;
memo->last_bv = Qundef;
n = rb_cmpint(rb_funcall(vj, id_cmp, 1, vi), vj, vi);
if (n == 0) {
i = j;
vi = vj;
}
else if (n < 0) {
VALUE tmp;
tmp = i;
i = j;
j = tmp;
tmp = vi;
vi = vj;
vj = tmp;
}
minmax_by_i_update(vi, vj, i, j, memo);
return Qnil;
}
static VALUE
enum_minmax_by(VALUE obj)
{
VALUE memo;
struct minmax_by_t *m = NEW_MEMO_FOR(struct minmax_by_t, memo);
RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
m->min_bv = Qundef;
m->max_bv = Qundef;
m->min = Qnil;
m->max = Qnil;
m->last_bv = Qundef;
m->last = Qundef;
rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo);
if (m->last_bv != Qundef)
minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m);
m = MEMO_FOR(struct minmax_by_t, memo);
return rb_assoc_new(m->min, m->max);
}
static VALUE
member_i(VALUE iter, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
if (rb_equal(rb_enum_values_pack(argc, argv), memo->u1.value)) {
memo->u2.value = Qtrue;
rb_iter_break();
}
return Qnil;
}
static VALUE
enum_member(VALUE obj, VALUE val)
{
NODE *memo = NEW_MEMO(val, Qfalse, 0);
rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
return memo->u2.value;
}
static VALUE
each_with_index_i(VALUE i, VALUE memo, int argc, VALUE *argv)
{
long n = RNODE(memo)->u3.cnt++;
return rb_yield_values(2, rb_enum_values_pack(argc, argv), INT2NUM(n));
}
static VALUE
enum_each_with_index(int argc, VALUE *argv, VALUE obj)
{
NODE *memo;
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
memo = NEW_MEMO(0, 0, 0);
rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)memo);
return obj;
}
static VALUE
enum_reverse_each(int argc, VALUE *argv, VALUE obj)
{
VALUE ary;
long i;
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
ary = enum_to_a(argc, argv, obj);
for (i = RARRAY_LEN(ary); --i >= 0; ) {
rb_yield(RARRAY_PTR(ary)[i]);
}
return obj;
}
static VALUE
each_val_i(VALUE i, VALUE p, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
rb_yield(i);
return Qnil;
}
static VALUE
enum_each_entry(int argc, VALUE *argv, VALUE obj)
{
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
rb_block_call(obj, id_each, argc, argv, each_val_i, 0);
return obj;
}
static VALUE
each_slice_i(VALUE i, VALUE m, int argc, VALUE *argv)
{
NODE *memo = RNODE(m);
VALUE ary = memo->u1.value;
VALUE v = Qnil;
long size = memo->u3.cnt;
ENUM_WANT_SVALUE();
rb_ary_push(ary, i);
if (RARRAY_LEN(ary) == size) {
v = rb_yield(ary);
memo->u1.value = rb_ary_new2(size);
}
return v;
}
static VALUE
enum_each_slice_size(VALUE obj, VALUE args)
{
VALUE n, size;
long slice_size = NUM2LONG(RARRAY_PTR(args)[0]);
if (slice_size <= 0) rb_raise(rb_eArgError, "invalid slice size");
size = enum_size(obj, 0);
if (size == Qnil) return Qnil;
n = rb_funcall(size, '+', 1, LONG2NUM(slice_size-1));
return rb_funcall(n, id_div, 1, LONG2FIX(slice_size));
}
static VALUE
enum_each_slice(VALUE obj, VALUE n)
{
long size = NUM2LONG(n);
VALUE ary;
NODE *memo;
if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_slice_size);
ary = rb_ary_new2(size);
memo = NEW_MEMO(ary, 0, size);
rb_block_call(obj, id_each, 0, 0, each_slice_i, (VALUE)memo);
ary = memo->u1.value;
if (RARRAY_LEN(ary) > 0) rb_yield(ary);
return Qnil;
}
static VALUE
each_cons_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
VALUE ary = memo->u1.value;
VALUE v = Qnil;
long size = memo->u3.cnt;
ENUM_WANT_SVALUE();
if (RARRAY_LEN(ary) == size) {
rb_ary_shift(ary);
}
rb_ary_push(ary, i);
if (RARRAY_LEN(ary) == size) {
v = rb_yield(rb_ary_dup(ary));
}
return v;
}
static VALUE
enum_each_cons_size(VALUE obj, VALUE args)
{
VALUE n, size;
long cons_size = NUM2LONG(RARRAY_PTR(args)[0]);
if (cons_size <= 0) rb_raise(rb_eArgError, "invalid size");
size = enum_size(obj, 0);
if (size == Qnil) return Qnil;
n = rb_funcall(size, '+', 1, LONG2NUM(1 - cons_size));
return (rb_cmpint(rb_funcall(n, id_cmp, 1, LONG2FIX(0)), n, LONG2FIX(0)) == -1) ? LONG2FIX(0) : n;
}
static VALUE
enum_each_cons(VALUE obj, VALUE n)
{
long size = NUM2LONG(n);
NODE *memo;
if (size <= 0) rb_raise(rb_eArgError, "invalid size");
RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_cons_size);
memo = NEW_MEMO(rb_ary_new2(size), 0, size);
rb_block_call(obj, id_each, 0, 0, each_cons_i, (VALUE)memo);
return Qnil;
}
static VALUE
each_with_object_i(VALUE i, VALUE memo, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
return rb_yield_values(2, i, memo);
}
static VALUE
enum_each_with_object(VALUE obj, VALUE memo)
{
RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enum_size);
rb_block_call(obj, id_each, 0, 0, each_with_object_i, memo);
return memo;
}
static VALUE
zip_ary(VALUE val, NODE *memo, int argc, VALUE *argv)
{
volatile VALUE result = memo->u1.value;
volatile VALUE args = memo->u2.value;
long n = memo->u3.cnt++;
volatile VALUE tmp;
int i;
tmp = rb_ary_new2(RARRAY_LEN(args) + 1);
rb_ary_store(tmp, 0, rb_enum_values_pack(argc, argv));
for (i=0; i<RARRAY_LEN(args); i++) {
VALUE e = RARRAY_PTR(args)[i];
if (RARRAY_LEN(e) <= n) {
rb_ary_push(tmp, Qnil);
}
else {
rb_ary_push(tmp, RARRAY_PTR(e)[n]);
}
}
if (NIL_P(result)) {
rb_yield(tmp);
}
else {
rb_ary_push(result, tmp);
}
return Qnil;
}
static VALUE
call_next(VALUE *v)
{
return v[0] = rb_funcall(v[1], id_next, 0, 0);
}
static VALUE
call_stop(VALUE *v)
{
return v[0] = Qundef;
}
static VALUE
zip_i(VALUE val, NODE *memo, int argc, VALUE *argv)
{
volatile VALUE result = memo->u1.value;
volatile VALUE args = memo->u2.value;
volatile VALUE tmp;
int i;
tmp = rb_ary_new2(RARRAY_LEN(args) + 1);
rb_ary_store(tmp, 0, rb_enum_values_pack(argc, argv));
for (i=0; i<RARRAY_LEN(args); i++) {
if (NIL_P(RARRAY_PTR(args)[i])) {
rb_ary_push(tmp, Qnil);
}
else {
VALUE v[2];
v[1] = RARRAY_PTR(args)[i];
rb_rescue2(call_next, (VALUE)v, call_stop, (VALUE)v, rb_eStopIteration, (VALUE)0);
if (v[0] == Qundef) {
RARRAY_PTR(args)[i] = Qnil;
v[0] = Qnil;
}
rb_ary_push(tmp, v[0]);
}
}
if (NIL_P(result)) {
rb_yield(tmp);
}
else {
rb_ary_push(result, tmp);
}
return Qnil;
}
static VALUE
enum_zip(int argc, VALUE *argv, VALUE obj)
{
int i;
ID conv;
NODE *memo;
VALUE result = Qnil;
VALUE args = rb_ary_new4(argc, argv);
int allary = TRUE;
argv = RARRAY_PTR(args);
for (i=0; i<argc; i++) {
VALUE ary = rb_check_array_type(argv[i]);
if (NIL_P(ary)) {
allary = FALSE;
break;
}
argv[i] = ary;
}
if (!allary) {
CONST_ID(conv, "to_enum");
for (i=0; i<argc; i++) {
if (!rb_respond_to(argv[i], id_each)) {
rb_raise(rb_eTypeError, "wrong argument type %s (must respond to :each)",
rb_obj_classname(argv[i]));
}
argv[i] = rb_funcall(argv[i], conv, 1, ID2SYM(id_each));
}
}
if (!rb_block_given_p()) {
result = rb_ary_new();
}
memo = rb_node_newnode(NODE_DOT2, result, args, 0);
rb_block_call(obj, id_each, 0, 0, allary ? zip_ary : zip_i, (VALUE)memo);
return result;
}
static VALUE
take_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
rb_ary_push(memo->u1.value, rb_enum_values_pack(argc, argv));
if (--memo->u3.cnt == 0) rb_iter_break();
return Qnil;
}
static VALUE
enum_take(VALUE obj, VALUE n)
{
NODE *memo;
VALUE result;
long len = NUM2LONG(n);
if (len < 0) {
rb_raise(rb_eArgError, "attempt to take negative size");
}
if (len == 0) return rb_ary_new2(0);
result = rb_ary_new2(len);
memo = NEW_MEMO(result, 0, len);
rb_block_call(obj, id_each, 0, 0, take_i, (VALUE)memo);
return result;
}
static VALUE
take_while_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
if (!RTEST(enum_yield(argc, argv))) rb_iter_break();
rb_ary_push(ary, rb_enum_values_pack(argc, argv));
return Qnil;
}
static VALUE
enum_take_while(VALUE obj)
{
VALUE ary;
RETURN_ENUMERATOR(obj, 0, 0);
ary = rb_ary_new();
rb_block_call(obj, id_each, 0, 0, take_while_i, ary);
return ary;
}
static VALUE
drop_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
if (memo->u3.cnt == 0) {
rb_ary_push(memo->u1.value, rb_enum_values_pack(argc, argv));
}
else {
memo->u3.cnt--;
}
return Qnil;
}
static VALUE
enum_drop(VALUE obj, VALUE n)
{
VALUE result;
NODE *memo;
long len = NUM2LONG(n);
if (len < 0) {
rb_raise(rb_eArgError, "attempt to drop negative size");
}
result = rb_ary_new();
memo = NEW_MEMO(result, 0, len);
rb_block_call(obj, id_each, 0, 0, drop_i, (VALUE)memo);
return result;
}
static VALUE
drop_while_i(VALUE i, VALUE args, int argc, VALUE *argv)
{
NODE *memo = RNODE(args);
ENUM_WANT_SVALUE();
if (!memo->u3.state && !RTEST(rb_yield(i))) {
memo->u3.state = TRUE;
}
if (memo->u3.state) {
rb_ary_push(memo->u1.value, i);
}
return Qnil;
}
static VALUE
enum_drop_while(VALUE obj)
{
VALUE result;
NODE *memo;
RETURN_ENUMERATOR(obj, 0, 0);
result = rb_ary_new();
memo = NEW_MEMO(result, 0, FALSE);
rb_block_call(obj, id_each, 0, 0, drop_while_i, (VALUE)memo);
return result;
}
static VALUE
cycle_i(VALUE i, VALUE ary, int argc, VALUE *argv)
{
ENUM_WANT_SVALUE();
rb_ary_push(ary, i);
rb_yield(i);
return Qnil;
}
static VALUE
enum_cycle_size(VALUE self, VALUE args)
{
long mul;
VALUE n = Qnil;
VALUE size = enum_size(self, args);
if (size == Qnil) return Qnil;
if (args && (RARRAY_LEN(args) > 0)) {
n = RARRAY_PTR(args)[0];
}
if (n == Qnil) return DBL2NUM(INFINITY);
mul = NUM2LONG(n);
if (mul <= 0) return INT2FIX(0);
return rb_funcall(size, '*', 1, LONG2FIX(mul));
}
static VALUE
enum_cycle(int argc, VALUE *argv, VALUE obj)
{
VALUE ary;
VALUE nv = Qnil;
long n, i, len;
rb_scan_args(argc, argv, "01", &nv);
RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size);
if (NIL_P(nv)) {
n = -1;
}
else {
n = NUM2LONG(nv);
if (n <= 0) return Qnil;
}
ary = rb_ary_new();
RBASIC(ary)->klass = 0;
rb_block_call(obj, id_each, 0, 0, cycle_i, ary);
len = RARRAY_LEN(ary);
if (len == 0) return Qnil;
while (n < 0 || 0 < --n) {
for (i=0; i<len; i++) {
rb_yield(RARRAY_PTR(ary)[i]);
}
}
return Qnil;
}
struct chunk_arg {
VALUE categorize;
VALUE state;
VALUE prev_value;
VALUE prev_elts;
VALUE yielder;
};
static VALUE
chunk_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
{
struct chunk_arg *argp = MEMO_FOR(struct chunk_arg, _argp);
VALUE v;
VALUE alone = ID2SYM(rb_intern("_alone"));
VALUE separator = ID2SYM(rb_intern("_separator"));
ENUM_WANT_SVALUE();
if (NIL_P(argp->state))
v = rb_funcall(argp->categorize, id_call, 1, i);
else
v = rb_funcall(argp->categorize, id_call, 2, i, argp->state);
if (v == alone) {
if (!NIL_P(argp->prev_value)) {
rb_funcall(argp->yielder, id_lshift, 1, rb_assoc_new(argp->prev_value, argp->prev_elts));
argp->prev_value = argp->prev_elts = Qnil;
}
rb_funcall(argp->yielder, id_lshift, 1, rb_assoc_new(v, rb_ary_new3(1, i)));
}
else if (NIL_P(v) || v == separator) {
if (!NIL_P(argp->prev_value)) {
rb_funcall(argp->yielder, id_lshift, 1, rb_assoc_new(argp->prev_value, argp->prev_elts));
argp->prev_value = argp->prev_elts = Qnil;
}
}
else if (SYMBOL_P(v) && rb_id2name(SYM2ID(v))[0] == '_') {
rb_raise(rb_eRuntimeError, "symbol begins with an underscore is reserved");
}
else {
if (NIL_P(argp->prev_value)) {
argp->prev_value = v;
argp->prev_elts = rb_ary_new3(1, i);
}
else {
if (rb_equal(argp->prev_value, v)) {
rb_ary_push(argp->prev_elts, i);
}
else {
rb_funcall(argp->yielder, id_lshift, 1, rb_assoc_new(argp->prev_value, argp->prev_elts));
argp->prev_value = v;
argp->prev_elts = rb_ary_new3(1, i);
}
}
}
return Qnil;
}
static VALUE
chunk_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
{
VALUE enumerable;
VALUE arg;
struct chunk_arg *memo = NEW_MEMO_FOR(struct chunk_arg, arg);
enumerable = rb_ivar_get(enumerator, rb_intern("chunk_enumerable"));
memo->categorize = rb_ivar_get(enumerator, rb_intern("chunk_categorize"));
memo->state = rb_ivar_get(enumerator, rb_intern("chunk_initial_state"));
memo->prev_value = Qnil;
memo->prev_elts = Qnil;
memo->yielder = yielder;
if (!NIL_P(memo->state))
memo->state = rb_obj_dup(memo->state);
rb_block_call(enumerable, id_each, 0, 0, chunk_ii, arg);
memo = MEMO_FOR(struct chunk_arg, arg);
if (!NIL_P(memo->prev_elts))
rb_funcall(memo->yielder, id_lshift, 1, rb_assoc_new(memo->prev_value, memo->prev_elts));
return Qnil;
}
static VALUE
enum_chunk(int argc, VALUE *argv, VALUE enumerable)
{
VALUE initial_state;
VALUE enumerator;
if (!rb_block_given_p())
rb_raise(rb_eArgError, "no block given");
rb_scan_args(argc, argv, "01", &initial_state);
enumerator = rb_obj_alloc(rb_cEnumerator);
rb_ivar_set(enumerator, rb_intern("chunk_enumerable"), enumerable);
rb_ivar_set(enumerator, rb_intern("chunk_categorize"), rb_block_proc());
rb_ivar_set(enumerator, rb_intern("chunk_initial_state"), initial_state);
rb_block_call(enumerator, idInitialize, 0, 0, chunk_i, enumerator);
return enumerator;
}
struct slicebefore_arg {
VALUE sep_pred;
VALUE sep_pat;
VALUE state;
VALUE prev_elts;
VALUE yielder;
};
static VALUE
slicebefore_ii(VALUE i, VALUE _argp, int argc, VALUE *argv)
{
struct slicebefore_arg *argp = MEMO_FOR(struct slicebefore_arg, _argp);
VALUE header_p;
ENUM_WANT_SVALUE();
if (!NIL_P(argp->sep_pat))
header_p = rb_funcall(argp->sep_pat, id_eqq, 1, i);
else if (NIL_P(argp->state))
header_p = rb_funcall(argp->sep_pred, id_call, 1, i);
else
header_p = rb_funcall(argp->sep_pred, id_call, 2, i, argp->state);
if (RTEST(header_p)) {
if (!NIL_P(argp->prev_elts))
rb_funcall(argp->yielder, id_lshift, 1, argp->prev_elts);
argp->prev_elts = rb_ary_new3(1, i);
}
else {
if (NIL_P(argp->prev_elts))
argp->prev_elts = rb_ary_new3(1, i);
else
rb_ary_push(argp->prev_elts, i);
}
return Qnil;
}
static VALUE
slicebefore_i(VALUE yielder, VALUE enumerator, int argc, VALUE *argv)
{
VALUE enumerable;
VALUE arg;
struct slicebefore_arg *memo = NEW_MEMO_FOR(struct slicebefore_arg, arg);
enumerable = rb_ivar_get(enumerator, rb_intern("slicebefore_enumerable"));
memo->sep_pred = rb_attr_get(enumerator, rb_intern("slicebefore_sep_pred"));
memo->sep_pat = NIL_P(memo->sep_pred) ? rb_ivar_get(enumerator, rb_intern("slicebefore_sep_pat")) : Qnil;
memo->state = rb_attr_get(enumerator, rb_intern("slicebefore_initial_state"));
memo->prev_elts = Qnil;
memo->yielder = yielder;
if (!NIL_P(memo->state))
memo->state = rb_obj_dup(memo->state);
rb_block_call(enumerable, id_each, 0, 0, slicebefore_ii, arg);
memo = MEMO_FOR(struct slicebefore_arg, arg);
if (!NIL_P(memo->prev_elts))
rb_funcall(memo->yielder, id_lshift, 1, memo->prev_elts);
return Qnil;
}
static VALUE
enum_slice_before(int argc, VALUE *argv, VALUE enumerable)
{
VALUE enumerator;
if (rb_block_given_p()) {
VALUE initial_state;
rb_scan_args(argc, argv, "01", &initial_state);
enumerator = rb_obj_alloc(rb_cEnumerator);
rb_ivar_set(enumerator, rb_intern("slicebefore_sep_pred"), rb_block_proc());
rb_ivar_set(enumerator, rb_intern("slicebefore_initial_state"), initial_state);
}
else {
VALUE sep_pat;
rb_scan_args(argc, argv, "1", &sep_pat);
enumerator = rb_obj_alloc(rb_cEnumerator);
rb_ivar_set(enumerator, rb_intern("slicebefore_sep_pat"), sep_pat);
}
rb_ivar_set(enumerator, rb_intern("slicebefore_enumerable"), enumerable);
rb_block_call(enumerator, idInitialize, 0, 0, slicebefore_i, enumerator);
return enumerator;
}
void
Init_Enumerable(void)
{
#undef rb_intern
#define rb_intern(str) rb_intern_const(str)
rb_mEnumerable = rb_define_module("Enumerable");
rb_define_method(rb_mEnumerable, "to_a", enum_to_a, -1);
rb_define_method(rb_mEnumerable, "entries", enum_to_a, -1);
rb_define_method(rb_mEnumerable, "sort", enum_sort, 0);
rb_define_method(rb_mEnumerable, "sort_by", enum_sort_by, 0);
rb_define_method(rb_mEnumerable, "grep", enum_grep, 1);
rb_define_method(rb_mEnumerable, "count", enum_count, -1);
rb_define_method(rb_mEnumerable, "find", enum_find, -1);
rb_define_method(rb_mEnumerable, "detect", enum_find, -1);
rb_define_method(rb_mEnumerable, "find_index", enum_find_index, -1);
rb_define_method(rb_mEnumerable, "find_all", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "select", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "reject", enum_reject, 0);
rb_define_method(rb_mEnumerable, "collect", enum_collect, 0);
rb_define_method(rb_mEnumerable, "map", enum_collect, 0);
rb_define_method(rb_mEnumerable, "flat_map", enum_flat_map, 0);
rb_define_method(rb_mEnumerable, "collect_concat", enum_flat_map, 0);
rb_define_method(rb_mEnumerable, "inject", enum_inject, -1);
rb_define_method(rb_mEnumerable, "reduce", enum_inject, -1);
rb_define_method(rb_mEnumerable, "partition", enum_partition, 0);
rb_define_method(rb_mEnumerable, "group_by", enum_group_by, 0);
rb_define_method(rb_mEnumerable, "first", enum_first, -1);
rb_define_method(rb_mEnumerable, "all?", enum_all, 0);
rb_define_method(rb_mEnumerable, "any?", enum_any, 0);
rb_define_method(rb_mEnumerable, "one?", enum_one, 0);
rb_define_method(rb_mEnumerable, "none?", enum_none, 0);
rb_define_method(rb_mEnumerable, "min", enum_min, 0);
rb_define_method(rb_mEnumerable, "max", enum_max, 0);
rb_define_method(rb_mEnumerable, "minmax", enum_minmax, 0);
rb_define_method(rb_mEnumerable, "min_by", enum_min_by, 0);
rb_define_method(rb_mEnumerable, "max_by", enum_max_by, 0);
rb_define_method(rb_mEnumerable, "minmax_by", enum_minmax_by, 0);
rb_define_method(rb_mEnumerable, "member?", enum_member, 1);
rb_define_method(rb_mEnumerable, "include?", enum_member, 1);
rb_define_method(rb_mEnumerable, "each_with_index", enum_each_with_index, -1);
rb_define_method(rb_mEnumerable, "reverse_each", enum_reverse_each, -1);
rb_define_method(rb_mEnumerable, "each_entry", enum_each_entry, -1);
rb_define_method(rb_mEnumerable, "each_slice", enum_each_slice, 1);
rb_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1);
rb_define_method(rb_mEnumerable, "each_with_object", enum_each_with_object, 1);
rb_define_method(rb_mEnumerable, "zip", enum_zip, -1);
rb_define_method(rb_mEnumerable, "take", enum_take, 1);
rb_define_method(rb_mEnumerable, "take_while", enum_take_while, 0);
rb_define_method(rb_mEnumerable, "drop", enum_drop, 1);
rb_define_method(rb_mEnumerable, "drop_while", enum_drop_while, 0);
rb_define_method(rb_mEnumerable, "cycle", enum_cycle, -1);
rb_define_method(rb_mEnumerable, "chunk", enum_chunk, -1);
rb_define_method(rb_mEnumerable, "slice_before", enum_slice_before, -1);
id_next = rb_intern("next");
id_call = rb_intern("call");
id_size = rb_intern("size");
id_div = rb_intern("div");
}