/*
 *  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 or strings, natural ordering is used.  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(argc, argv, range)
    int argc;
    VALUE *argv;
    VALUE range;
{
    VALUE b, e, step;
    long unit;

    b = rb_ivar_get(range, id_beg);
    e = rb_ivar_get(range, id_end);
    if (rb_scan_args(argc, argv, "01", &step) == 0) {
        step = INT2FIX(1);
    }

    unit = NUM2LONG(step);
    if (unit < 0) {
        rb_raise(rb_eArgError, "step can't be negative");
    } 
    if (FIXNUM_P(b) && FIXNUM_P(e)) { /* fixnums are special */
        long end = FIX2LONG(e);
        long i;

        if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
        if (!EXCL(range)) end += 1;
        i = FIX2LONG(b);       
        while (i < end) {
            rb_yield(LONG2NUM(i));
            if (i + unit < i) break;
            i += unit;
        }
    }
    else {
        VALUE tmp = rb_check_string_type(b);

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

            b = tmp;
            if (unit == 0) rb_raise(rb_eArgError, "step can't be 0");
            args[0] = b; args[1] = e; args[2] = range;
            iter[0] = 1; iter[1] = unit;
            rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i,
                        (VALUE)iter);
        }
        else if (rb_obj_is_kind_of(b, rb_cNumeric)) {
            ID c = rb_intern(EXCL(range) ? "<" : "<=");

            if (rb_equal(step, INT2FIX(0))) rb_raise(rb_eArgError, "step can't be 0");
            while (RTEST(rb_funcall(b, c, 1, e))) {
                rb_yield(b);
                b = rb_funcall(b, '+', 1, step);
            }
        }
        else {
            long args[2];

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