# class Enumerator::ArithmeticSequence

`Enumerator::ArithmeticSequence` is a subclass of `Enumerator`, that is a representation of sequences of numbers with common difference. Instances of this class can be generated by the `Range#step` and `Numeric#step` methods.

The class can be used for slicing `Array` (see `Array#slice`) or custom collections.

### Public Instance Methods

aseq == obj → true or false click to toggle source

Returns `true` only if `obj` is an `Enumerator::ArithmeticSequence`, has equivalent begin, end, step, and exclude_end? settings.

    static VALUE
    arith_seq_eq(VALUE self, VALUE other)
    {
        if (!RTEST(rb_obj_is_kind_of(other, rb_cArithSeq))) {
            return Qfalse;
        }

        if (!rb_equal(arith_seq_begin(self), arith_seq_begin(other))) {
            return Qfalse;
        }

        if (!rb_equal(arith_seq_end(self), arith_seq_end(other))) {
            return Qfalse;
        }

        if (!rb_equal(arith_seq_step(self), arith_seq_step(other))) {
            return Qfalse;
        }

        if (arith_seq_exclude_end_p(self) != arith_seq_exclude_end_p(other)) {
            return Qfalse;
        }

        return Qtrue;
    }

Also aliased as: ===, eql?
===
Alias for: ==
begin → num or nil click to toggle source

Returns the number that defines the first element of this arithmetic sequence.

    static inline VALUE
    arith_seq_begin(VALUE self)
    {
        return rb_ivar_get(self, id_begin);
    }

each {|i| block } → aseq click to toggle source
each → aseq

    static VALUE
    arith_seq_each(VALUE self)
    {
        VALUE c, e, s, len_1, last;
        int x;

        if (!rb_block_given_p()) return self;

        c = arith_seq_begin(self);
        e = arith_seq_end(self);
        s = arith_seq_step(self);
        x = arith_seq_exclude_end_p(self);

        if (!RB_TYPE_P(s, T_COMPLEX) && ruby_float_step(c, e, s, x, TRUE)) {
            return self;
        }

        if (NIL_P(e)) {
            while (1) {
                rb_yield(c);
                c = rb_int_plus(c, s);
            }

            return self;
        }

        if (rb_equal(s, INT2FIX(0))) {
            while (1) {
                rb_yield(c);
            }

            return self;
        }

        len_1 = num_idiv(num_minus(e, c), s);
        last = num_plus(c, num_mul(s, len_1));
        if (x && rb_equal(last, e)) {
            last = num_minus(last, s);
        }

        if (rb_num_negative_int_p(s)) {
            while (NUM_GE(c, last)) {
                rb_yield(c);
                c = num_plus(c, s);
            }
        }
        else {
            while (NUM_GE(last, c)) {
                rb_yield(c);
                c = num_plus(c, s);
            }
        }

        return self;
    }

end → num or nil click to toggle source

Returns the number that defines the end of this arithmetic sequence.

    static inline VALUE
    arith_seq_end(VALUE self)
    {
        return rb_ivar_get(self, id_end);
    }

eql?
Alias for: ==
exclude_end? → true or false click to toggle source

Returns `true` if this arithmetic sequence excludes its end value.

    static inline VALUE
    arith_seq_exclude_end(VALUE self)
    {
        return rb_ivar_get(self, id_exclude_end);
    }

first → num or nil click to toggle source
first(n) → an_array

Returns the first number in this arithmetic sequence, or an array of the first `n` elements.

    static VALUE
    arith_seq_first(int argc, VALUE *argv, VALUE self)
    {
        VALUE b, e, s, ary;
        long n;
        int x;

        rb_check_arity(argc, 0, 1);

        b = arith_seq_begin(self);
        e = arith_seq_end(self);
        s = arith_seq_step(self);
        if (argc == 0) {
            if (NIL_P(b)) {
                return Qnil;
            }
            if (!NIL_P(e)) {
                VALUE zero = INT2FIX(0);
                int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero);
                if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) {
                    return Qnil;
                }
                if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) {
                    return Qnil;
                }
            }
            return b;
        }

        // TODO: the following code should be extracted as arith_seq_take

        n = NUM2LONG(argv[0]);
        if (n < 0) {
            rb_raise(rb_eArgError, "attempt to take negative size");
        }
        if (n == 0) {
            return rb_ary_new_capa(0);
        }

        x = arith_seq_exclude_end_p(self);

        if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(s)) {
            long i = FIX2LONG(b), unit = FIX2LONG(s);
            ary = rb_ary_new_capa(n);
            while (n > 0 && FIXABLE(i)) {
                rb_ary_push(ary, LONG2FIX(i));
                i += unit;  // FIXABLE + FIXABLE never overflow;
                --n;
            }
            if (n > 0) {
                b = LONG2NUM(i);
                while (n > 0) {
                    rb_ary_push(ary, b);
                    b = rb_big_plus(b, s);
                    --n;
                }
            }
            return ary;
        }
        else if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(s)) {
            long i = FIX2LONG(b);
            long end = FIX2LONG(e);
            long unit = FIX2LONG(s);
            long len;

            if (unit >= 0) {
                if (!x) end += 1;

                len = end - i;
                if (len < 0) len = 0;
                ary = rb_ary_new_capa((n < len) ? n : len);
                while (n > 0 && i < end) {
                    rb_ary_push(ary, LONG2FIX(i));
                    if (i + unit < i) break;
                    i += unit;
                    --n;
                }
            }
            else {
                if (!x) end -= 1;

                len = i - end;
                if (len < 0) len = 0;
                ary = rb_ary_new_capa((n < len) ? n : len);
                while (n > 0 && i > end) {
                    rb_ary_push(ary, LONG2FIX(i));
                    if (i + unit > i) break;
                    i += unit;
                    --n;
                }
            }
            return ary;
        }
        else if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
            /* generate values like ruby_float_step */

            double unit = NUM2DBL(s);
            double beg = NUM2DBL(b);
            double end = NIL_P(e) ? (unit < 0 ? -1 : 1)*HUGE_VAL : NUM2DBL(e);
            double len = ruby_float_step_size(beg, end, unit, x);
            long i;

            if (n > len)
                n = (long)len;

            if (isinf(unit)) {
                if (len > 0) {
                    ary = rb_ary_new_capa(1);
                    rb_ary_push(ary, DBL2NUM(beg));
                }
                else {
                    ary = rb_ary_new_capa(0);
                }
            }
            else if (unit == 0) {
                VALUE val = DBL2NUM(beg);
                ary = rb_ary_new_capa(n);
                for (i = 0; i < len; ++i) {
                    rb_ary_push(ary, val);
                }
            }
            else {
                ary = rb_ary_new_capa(n);
                for (i = 0; i < n; ++i) {
                    double d = i*unit+beg;
                    if (unit >= 0 ? end < d : d < end) d = end;
                    rb_ary_push(ary, DBL2NUM(d));
                }
            }

            return ary;
        }

        return rb_call_super(argc, argv);
    }

hash → integer click to toggle source

Compute a hash-value for this arithmetic sequence. Two arithmetic sequences with same begin, end, step, and exclude_end? values will generate the same hash-value.

See also `Object#hash`.

    static VALUE
    arith_seq_hash(VALUE self)
    {
        st_index_t hash;
        VALUE v;

        hash = rb_hash_start(arith_seq_exclude_end_p(self));
        v = rb_hash(arith_seq_begin(self));
        hash = rb_hash_uint(hash, NUM2LONG(v));
        v = rb_hash(arith_seq_end(self));
        hash = rb_hash_uint(hash, NUM2LONG(v));
        v = rb_hash(arith_seq_step(self));
        hash = rb_hash_uint(hash, NUM2LONG(v));
        hash = rb_hash_end(hash);

        return ST2FIX(hash);
    }

inspect → string click to toggle source

Convert this arithmetic sequence to a printable form.

    static VALUE
    arith_seq_inspect(VALUE self)
    {
        struct enumerator *e;
        VALUE eobj, str, eargs;
        int range_p;

        TypedData_Get_Struct(self, struct enumerator, &enumerator_data_type, e);

        eobj = rb_attr_get(self, id_receiver);
        if (NIL_P(eobj)) {
            eobj = e->obj;
        }

        range_p = RTEST(rb_obj_is_kind_of(eobj, rb_cRange));
        str = rb_sprintf("(%s%"PRIsVALUE"%s.", range_p ? "(" : "", eobj, range_p ? ")" : "");

        rb_str_buf_append(str, rb_id2str(e->meth));

        eargs = rb_attr_get(eobj, id_arguments);
        if (NIL_P(eargs)) {
            eargs = e->args;
        }
        if (eargs != Qfalse) {
            long argc = RARRAY_LEN(eargs);
            const VALUE *argv = RARRAY_CONST_PTR(eargs); /* WB: no new reference */

            if (argc > 0) {
                VALUE kwds = Qnil;

                rb_str_buf_cat2(str, "(");

                if (RB_TYPE_P(argv[argc-1], T_HASH)) {
                    int all_key = TRUE;
                    rb_hash_foreach(argv[argc-1], key_symbol_p, (VALUE)&all_key);
                    if (all_key) kwds = argv[--argc];
                }

                while (argc--) {
                    VALUE arg = *argv++;

                    rb_str_append(str, rb_inspect(arg));
                    rb_str_buf_cat2(str, ", ");
                }
                if (!NIL_P(kwds)) {
                    rb_hash_foreach(kwds, kwd_append, str);
                }
                rb_str_set_len(str, RSTRING_LEN(str)-2); /* drop the last ", " */
                rb_str_buf_cat2(str, ")");
            }
        }

        rb_str_buf_cat2(str, ")");

        return str;
    }

last → num or nil click to toggle source
last(n) → an_array

Returns the last number in this arithmetic sequence, or an array of the last `n` elements.

    static VALUE
    arith_seq_last(int argc, VALUE *argv, VALUE self)
    {
        VALUE b, e, s, len_1, len, last, nv, ary;
        int last_is_adjusted;
        long n;

        e = arith_seq_end(self);
        if (NIL_P(e)) {
            rb_raise(rb_eRangeError,
                     "cannot get the last element of endless arithmetic sequence");
        }

        b = arith_seq_begin(self);
        s = arith_seq_step(self);

        len_1 = num_idiv(num_minus(e, b), s);
        if (rb_num_negative_int_p(len_1)) {
            if (argc == 0) {
                return Qnil;
            }
            return rb_ary_new_capa(0);
        }

        last = num_plus(b, num_mul(s, len_1));
        if ((last_is_adjusted = arith_seq_exclude_end_p(self) && rb_equal(last, e))) {
            last = num_minus(last, s);
        }

        if (argc == 0) {
            return last;
        }

        if (last_is_adjusted) {
            len = len_1;
        }
        else {
            len = rb_int_plus(len_1, INT2FIX(1));
        }

        rb_scan_args(argc, argv, "1", &nv);
        if (!RB_INTEGER_TYPE_P(nv)) {
            nv = rb_to_int(nv);
        }
        if (RTEST(rb_int_gt(nv, len))) {
            nv = len;
        }
        n = NUM2LONG(nv);
        if (n < 0) {
            rb_raise(rb_eArgError, "negative array size");
        }

        ary = rb_ary_new_capa(n);
        b = rb_int_minus(last, rb_int_mul(s, nv));
        while (n) {
            b = rb_int_plus(b, s);
            rb_ary_push(ary, b);
            --n;
        }

        return ary;
    }

size → num or nil click to toggle source

Returns the number of elements in this arithmetic sequence if it is a finite sequence. Otherwise, returns `nil`.

    static VALUE
    arith_seq_size(VALUE self)
    {
        VALUE b, e, s, len_1, len, last;
        int x;

        b = arith_seq_begin(self);
        e = arith_seq_end(self);
        s = arith_seq_step(self);
        x = arith_seq_exclude_end_p(self);

        if (RB_FLOAT_TYPE_P(b) || RB_FLOAT_TYPE_P(e) || RB_FLOAT_TYPE_P(s)) {
            double ee, n;

            if (NIL_P(e)) {
                if (rb_num_negative_int_p(s)) {
                    ee = -HUGE_VAL;
                }
                else {
                    ee = HUGE_VAL;
                }
            }
            else {
                ee = NUM2DBL(e);
            }

            n = ruby_float_step_size(NUM2DBL(b), ee, NUM2DBL(s), x);
            if (isinf(n)) return DBL2NUM(n);
            if (POSFIXABLE(n)) return LONG2FIX((long)n);
            return rb_dbl2big(n);
        }

        if (NIL_P(e)) {
            return DBL2NUM(HUGE_VAL);
        }

        if (!rb_obj_is_kind_of(s, rb_cNumeric)) {
            s = rb_to_int(s);
        }

        if (rb_equal(s, INT2FIX(0))) {
            return DBL2NUM(HUGE_VAL);
        }

        len_1 = rb_int_idiv(rb_int_minus(e, b), s);
        if (rb_num_negative_int_p(len_1)) {
            return INT2FIX(0);
        }

        last = rb_int_plus(b, rb_int_mul(s, len_1));
        if (x && rb_equal(last, e)) {
            len = len_1;
        }
        else {
            len = rb_int_plus(len_1, INT2FIX(1));
        }

        return len;
    }

step → num click to toggle source

Returns the number that defines the common difference between two adjacent elements in this arithmetic sequence.

    static inline VALUE
    arith_seq_step(VALUE self)
    {
        return rb_ivar_get(self, id_step);
    }