/*
 *  call-seq:
 *     rng.step(n=1) {| obj | block }    => rng
 *  
 *  Iterates over <i>rng</i>, passing each <i>n</i>th element to the block. If
 *  the range contains numbers, <i>n</i> is added for each iteration.  Otherwise
 *  <code>step</code> invokes <code>succ</code> to iterate through range
 *  elements. The following code uses class <code>Xs</code>, which is defined
 *  in the class-level documentation.
 *     
 *     range = Xs.new(1)..Xs.new(10)
 *     range.step(2) {|x| puts x}
 *     range.step(3) {|x| puts x}
 *     
 *  <em>produces:</em>
 *     
 *      1 x
 *      3 xxx
 *      5 xxxxx
 *      7 xxxxxxx
 *      9 xxxxxxxxx
 *      1 x
 *      4 xxxx
 *      7 xxxxxxx
 *     10 xxxxxxxxxx
 */


static VALUE
range_step(int argc, VALUE *argv, VALUE range)
{
    VALUE b, e, step, tmp;

    RETURN_ENUMERATOR(range, argc, argv);

    b = RANGE_BEG(range);
    e = RANGE_END(range);
    if (argc == 0) {
        step = INT2FIX(1);
    }
    else {
        rb_scan_args(argc, argv, "01", &step);
        if (!rb_obj_is_kind_of(step, rb_cNumeric)) {
            step = rb_to_int(step);
        }
        if (rb_funcall(step, '<', 1, INT2FIX(0))) {
            rb_raise(rb_eArgError, "step can't be negative");
        }
        else if (!rb_funcall(step, '>', 1, INT2FIX(0))) {
            rb_raise(rb_eArgError, "step can't be 0");
        }
    }

    if (FIXNUM_P(b) && FIXNUM_P(e) && FIXNUM_P(step)) { /* fixnums are special */
        long end = FIX2LONG(e);
        long i, unit = FIX2LONG(step);

        if (!EXCL(range))
            end += 1;
        i = FIX2LONG(b);       
        while (i < end) {
            rb_yield(LONG2NUM(i));
            if (i + unit < i) break;
            i += unit;
        }

    }
    else if (rb_obj_is_kind_of(b, rb_cNumeric) ||
             !NIL_P(rb_check_to_integer(b, "to_int")) ||
             !NIL_P(rb_check_to_integer(e, "to_int"))) {
        ID op = EXCL(range) ? '<' : rb_intern("<=");

        while (RTEST(rb_funcall(b, op, 1, e))) {
            rb_yield(b);
            b = rb_funcall(b, '+', 1, step);
        }
    }
    else {
        tmp = rb_check_string_type(b);

        if (!NIL_P(tmp)) {
            VALUE args[2], iter[2];

            b = tmp;
            args[0] = e;
            args[1] = EXCL(range) ? Qtrue : Qfalse;
            iter[0] = INT2FIX(1);
            iter[1] = step;
            rb_block_call(b, rb_intern("upto"), 2, args, step_i, (VALUE)iter);
        }
        else {
            VALUE args[2];

            if (!rb_respond_to(b, id_succ)) {
                rb_raise(rb_eTypeError, "can't iterate from %s",
                         rb_obj_classname(b));
            }
            args[0] = INT2FIX(1);
            args[1] = step;
            range_each_func(range, step_i, args);
        }
    }
    return range;
}