KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyNumeric


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
15  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
17  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
18  * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
19  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
20  * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
21  * Copyright (C) 2006 Antti Karanta <Antti.Karanta@napa.fi>
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either of the GNU General Public License Version 2 or later (the "GPL"),
25  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the CPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the CPL, the GPL or the LGPL.
34  ***** END LICENSE BLOCK *****/

35 package org.jruby;
36
37 import java.math.BigInteger JavaDoc;
38 import java.util.List JavaDoc;
39
40 import org.jruby.exceptions.RaiseException;
41 import org.jruby.runtime.Block;
42 import org.jruby.runtime.CallbackFactory;
43 import org.jruby.runtime.ObjectAllocator;
44 import org.jruby.runtime.ThreadContext;
45 import org.jruby.runtime.builtin.IRubyObject;
46 import org.jruby.util.ByteList;
47 import org.jruby.util.Convert;
48
49 /**
50  * Base class for all numerical types in ruby.
51  */

52 // TODO: Numeric.new works in Ruby and it does here too. However trying to use
53
// that instance in a numeric operation should generate an ArgumentError. Doing
54
// this seems so pathological I do not see the need to fix this now.
55
public class RubyNumeric extends RubyObject {
56     
57     public static RubyClass createNumericClass(Ruby runtime) {
58         RubyClass numeric = runtime.defineClass("Numeric", runtime.getObject(), NUMERIC_ALLOCATOR);
59         CallbackFactory callbackFactory = runtime.callbackFactory(RubyNumeric.class);
60         numeric.defineFastMethod("singleton_method_added", callbackFactory.getFastMethod("sadded",
61                 RubyKernel.IRUBY_OBJECT));
62
63         numeric.includeModule(runtime.getModule("Comparable"));
64
65         numeric.defineFastMethod("initialize_copy", callbackFactory.getFastMethod("init_copy", RubyKernel.IRUBY_OBJECT));
66         numeric.defineFastMethod("coerce", callbackFactory.getFastMethod("coerce", RubyKernel.IRUBY_OBJECT));
67
68         numeric.defineFastMethod("+@", callbackFactory.getFastMethod("uplus"));
69         numeric.defineFastMethod("-@", callbackFactory.getFastMethod("uminus"));
70         numeric.defineFastMethod("<=>", callbackFactory.getFastMethod("cmp", RubyKernel.IRUBY_OBJECT));
71         numeric.defineFastMethod("quo", callbackFactory.getFastMethod("quo", RubyKernel.IRUBY_OBJECT));
72         numeric.defineFastMethod("eql?", callbackFactory.getFastMethod("eql_p", RubyKernel.IRUBY_OBJECT));
73         numeric.defineFastMethod("div", callbackFactory.getFastMethod("div", RubyKernel.IRUBY_OBJECT));
74         numeric.defineFastMethod("divmod", callbackFactory.getFastMethod("divmod", RubyKernel.IRUBY_OBJECT));
75         numeric.defineFastMethod("modulo", callbackFactory.getFastMethod("modulo", RubyKernel.IRUBY_OBJECT));
76         numeric.defineFastMethod("remainder", callbackFactory.getFastMethod("remainder", RubyKernel.IRUBY_OBJECT));
77         numeric.defineFastMethod("abs", callbackFactory.getFastMethod("abs"));
78         numeric.defineFastMethod("to_int", callbackFactory.getFastMethod("to_int"));
79         numeric.defineFastMethod("integer?", callbackFactory.getFastMethod("int_p"));
80         numeric.defineFastMethod("zero?", callbackFactory.getFastMethod("zero_p"));
81         numeric.defineFastMethod("nonzero?", callbackFactory.getFastMethod("nonzero_p"));
82         numeric.defineFastMethod("floor", callbackFactory.getFastMethod("floor"));
83         numeric.defineFastMethod("ceil", callbackFactory.getFastMethod("ceil"));
84         numeric.defineFastMethod("round", callbackFactory.getFastMethod("round"));
85         numeric.defineFastMethod("truncate", callbackFactory.getFastMethod("truncate"));
86         numeric.defineMethod("step", callbackFactory.getOptMethod("step"));
87
88         return numeric;
89     }
90
91     protected static ObjectAllocator NUMERIC_ALLOCATOR = new ObjectAllocator() {
92         public IRubyObject allocate(Ruby runtime, RubyClass klass) {
93             return new RubyNumeric(runtime, klass);
94         }
95     };
96
97     public static double DBL_EPSILON=2.2204460492503131e-16;
98     
99     public RubyNumeric(Ruby runtime, RubyClass metaClass) {
100         super(runtime, metaClass);
101     }
102
103     // The implementations of these are all bonus (see TODO above) I was going
104
// to throw an error from these, but it appears to be the wrong place to
105
// do it.
106
public double getDoubleValue() {
107         return 0;
108     }
109
110     public long getLongValue() {
111         return 0;
112     }
113     
114     public static RubyNumeric newNumeric(Ruby runtime) {
115         return new RubyNumeric(runtime, runtime.getClass("Numeric"));
116     }
117
118     /* ================
119      * Utility Methods
120      * ================
121      */

122
123     /** rb_num2int, NUM2INT
124      *
125      */

126     public static int num2int(IRubyObject arg) {
127         long num = num2long(arg);
128
129         checkInt(arg, num);
130         return (int)num;
131         }
132     
133     /** check_int
134      *
135      */

136     public static void checkInt(IRubyObject arg, long num){
137         String JavaDoc s;
138         if (num < Integer.MIN_VALUE) {
139             s = "small";
140         } else if (num > Integer.MAX_VALUE) {
141             s = "big";
142         } else {
143             return;
144         }
145         throw arg.getRuntime().newRangeError("integer " + num + " too " + s + " to convert to `int'");
146     }
147
148     // TODO: Find all consumers and convert to correct conversion protocol <- done
149
/** rb_num2long and FIX2LONG (numeric.c)
150      *
151      */

152     public static long num2long(IRubyObject arg) {
153         if (arg instanceof RubyFixnum) {
154             return ((RubyFixnum) arg).getLongValue();
155         }
156         if (arg.isNil()) {
157             throw arg.getRuntime().newTypeError("no implicit conversion from nil to integer");
158     }
159
160         if (arg instanceof RubyFloat) {
161             double aFloat = ((RubyFloat) arg).getDoubleValue();
162             if (aFloat <= (double) Long.MAX_VALUE && aFloat >= (double) Long.MIN_VALUE) {
163                 return (long) aFloat;
164             } else {
165                 // TODO: number formatting here, MRI uses "%-.10g", 1.4 API is a must?
166
throw arg.getRuntime().newTypeError("float " + aFloat + "out of range of integer");
167             }
168         } else if (arg instanceof RubyBignum) {
169             return RubyBignum.big2long((RubyBignum) arg);
170         }
171         return arg.convertToInteger().getLongValue();
172     }
173
174     /** rb_dbl2big + LONG2FIX at once (numeric.c)
175      *
176      */

177     public static IRubyObject dbl2num(Ruby runtime, double val) {
178         if (Double.isInfinite(val)) {
179             throw runtime.newFloatDomainError(val < 0 ? "-Infinity" : "Infinity");
180         }
181         if (Double.isNaN(val)) {
182             throw runtime.newFloatDomainError("NaN");
183         }
184
185         if (val > (double) RubyFixnum.MAX || val < (double) RubyFixnum.MIN) {
186             return RubyBignum.newBignum(runtime, val);
187         }
188         return RubyFixnum.newFixnum(runtime, (long) val);
189     }
190
191     /** rb_num2dbl and NUM2DBL
192      *
193      */

194     public static double num2dbl(IRubyObject arg) {
195         if (arg instanceof RubyFloat) {
196             return ((RubyFloat) arg).getDoubleValue();
197         } else if (arg instanceof RubyString) {
198             throw arg.getRuntime().newTypeError("no implicit conversion to float from string");
199         } else if (arg == arg.getRuntime().getNil()) {
200             throw arg.getRuntime().newTypeError("no implicit conversion to float from nil");
201         }
202         return arg.convertToFloat().getDoubleValue();
203     }
204
205     /** rb_dbl_cmp (numeric.c)
206      *
207      */

208     public static IRubyObject dbl_cmp(Ruby runtime, double a, double b) {
209         if (Double.isNaN(a) || Double.isNaN(b)) {
210             return runtime.getNil();
211         }
212         if (a > b) {
213             return RubyFixnum.one(runtime);
214         }
215         if (a < b) {
216             return RubyFixnum.minus_one(runtime);
217         }
218         return RubyFixnum.zero(runtime);
219     }
220
221     public static long fix2long(IRubyObject arg) {
222             return ((RubyFixnum) arg).getLongValue();
223         }
224
225     public static int fix2int(IRubyObject arg) {
226         long num = arg instanceof RubyFixnum ? fix2long(arg) : num2long(arg);
227
228         checkInt(arg, num);
229         return (int) num;
230         }
231
232     public static RubyInteger str2inum(Ruby runtime, RubyString str, int base) {
233         return str2inum(runtime,str,base,false);
234     }
235
236     public static RubyNumeric int2fix(Ruby runtime, long val) {
237         return RubyFixnum.newFixnum(runtime,val);
238     }
239
240     /** rb_num2fix
241      *
242      */

243     public static IRubyObject num2fix(IRubyObject val) {
244         if (val instanceof RubyFixnum) {
245             return val;
246         }
247         if (val instanceof RubyBignum) {
248             // any BigInteger is bigger than Fixnum and we don't have FIXABLE
249
throw val.getRuntime().newRangeError("integer " + val + " out of range of fixnum");
250         }
251         return RubyFixnum.newFixnum(val.getRuntime(), num2long(val));
252     }
253
254     /**
255      * Converts a string representation of an integer to the integer value.
256      * Parsing starts at the beginning of the string (after leading and
257      * trailing whitespace have been removed), and stops at the end or at the
258      * first character that can't be part of an integer. Leading signs are
259      * allowed. If <code>base</code> is zero, strings that begin with '0[xX]',
260      * '0[bB]', or '0' (optionally preceded by a sign) will be treated as hex,
261      * binary, or octal numbers, respectively. If a non-zero base is given,
262      * only the prefix (if any) that is appropriate to that base will be
263      * parsed correctly. For example, if the base is zero or 16, the string
264      * "0xff" will be converted to 256, but if the base is 10, it will come out
265      * as zero, since 'x' is not a valid decimal digit. If the string fails
266      * to parse as a number, zero is returned.
267      *
268      * @param runtime the ruby runtime
269      * @param str the string to be converted
270      * @param base the expected base of the number (for example, 2, 8, 10, 16),
271      * or 0 if the method should determine the base automatically
272      * (defaults to 10). Values 0 and 2-36 are permitted. Any other
273      * value will result in an ArgumentError.
274      * @param strict if true, enforce the strict criteria for String encoding of
275      * numeric values, as required by Integer('n'), and raise an
276      * exception when those criteria are not met. Otherwise, allow
277      * lax expression of values, as permitted by String#to_i, and
278      * return a value in almost all cases (excepting illegal radix).
279      * TODO: describe the rules/criteria
280      * @return a RubyFixnum or (if necessary) a RubyBignum representing
281      * the result of the conversion, which will be zero if the
282      * conversion failed.
283      */

284     public static RubyInteger str2inum(Ruby runtime, RubyString str, int base, boolean strict) {
285         if (base != 0 && (base < 2 || base > 36)) {
286             throw runtime.newArgumentError("illegal radix " + base);
287             }
288         ByteList bytes = str.getByteList();
289         try {
290             return runtime.newFixnum(Convert.byteListToLong(bytes,base,strict));
291
292         } catch (InvalidIntegerException e) {
293             if (strict) {
294                 throw runtime.newArgumentError("invalid value for Integer: "
295                         + str.callMethod(runtime.getCurrentContext(), "inspect").toString());
296             }
297             return RubyFixnum.zero(runtime);
298         } catch (NumberTooLargeException e) {
299         try {
300                 BigInteger JavaDoc bi = Convert.byteListToBigInteger(bytes,base,strict);
301                 return new RubyBignum(runtime,bi);
302             } catch (InvalidIntegerException e2) {
303                 if(strict) {
304                     throw runtime.newArgumentError("invalid value for Integer: "
305                             + str.callMethod(runtime.getCurrentContext(), "inspect").toString());
306                 }
307                 return RubyFixnum.zero(runtime);
308             }
309         }
310     }
311
312     public static RubyFloat str2fnum(Ruby runtime, RubyString arg) {
313         return str2fnum(runtime,arg,false);
314     }
315
316     /**
317      * Converts a string representation of a floating-point number to the
318      * numeric value. Parsing starts at the beginning of the string (after
319      * leading and trailing whitespace have been removed), and stops at the
320      * end or at the first character that can't be part of a number. If
321      * the string fails to parse as a number, 0.0 is returned.
322      *
323      * @param runtime the ruby runtime
324      * @param arg the string to be converted
325      * @param raise if the string is not a valid float, raise error, otherwise return 0.0
326      * @return a RubyFloat representing the result of the conversion, which
327      * will be 0.0 if the conversion failed.
328      */

329     public static RubyFloat str2fnum(Ruby runtime, RubyString arg, boolean raise) {
330         String JavaDoc str = arg.toString().trim();
331         double d = 0.0;
332         int pos = str.length();
333         for (int i = 0; i < pos; i++) {
334             if ("0123456789eE+-.".indexOf(str.charAt(i)) == -1) {
335                 if(raise) {
336                     throw runtime.newArgumentError("invalid value for Float(): "
337                             + arg.callMethod(runtime.getCurrentContext(), "inspect").toString());
338                 }
339                 pos = i + 1;
340                 break;
341             }
342         }
343         for (; pos > 0; pos--) {
344             try {
345                 d = Double.parseDouble(str.substring(0, pos));
346             } catch (NumberFormatException JavaDoc ex) {
347                 if(raise) {
348                     throw runtime.newArgumentError("invalid value for Float(): "
349                             + arg.callMethod(runtime.getCurrentContext(), "inspect").toString());
350                 }
351                 continue;
352             }
353             break;
354         }
355         return new RubyFloat(runtime, d);
356     }
357
358     /** Numeric methods. (num_*)
359      *
360      */

361     
362     protected IRubyObject[] getCoerced(IRubyObject other, boolean error) {
363         IRubyObject result;
364         
365         try {
366             result = other.callMethod(getRuntime().getCurrentContext(), "coerce", this);
367         } catch (RaiseException e) {
368             if (error) {
369                 throw getRuntime().newTypeError(
370                         other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
371             }
372              
373             return null;
374         }
375         
376         if (!(result instanceof RubyArray) || ((RubyArray)result).getLength() != 2) {
377             throw getRuntime().newTypeError("coerce must return [x, y]");
378         }
379         
380         return ((RubyArray)result).toJavaArray();
381     }
382
383     protected IRubyObject callCoerced(String JavaDoc method, IRubyObject other, boolean err) {
384         IRubyObject[] args = getCoerced(other, err);
385         return args[0].callMethod(getRuntime().getCurrentContext(), method, args[1]);
386     }
387
388     protected IRubyObject callCoerced(String JavaDoc method, IRubyObject other) {
389         IRubyObject[] args = getCoerced(other, true);
390         return args[0].callMethod(getRuntime().getCurrentContext(), method, args[1]);
391     }
392     
393     // beneath are rewritten coercions that reflect MRI logic, the aboves are used only by RubyBigDecimal
394

395     /** coerce_body
396      *
397      */

398     protected final IRubyObject coerceBody(IRubyObject other) {
399         return other.callMethod(getRuntime().getCurrentContext(), "coerce", this);
400         }
401
402     /** do_coerce
403      *
404      */

405     protected final List JavaDoc doCoerce(IRubyObject other, boolean err) {
406         IRubyObject result;
407         try {
408             result = coerceBody(other);
409         } catch (RaiseException e) {
410             if (err) {
411                 throw getRuntime().newTypeError(
412                         other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
413     }
414             return null;
415         }
416     
417         if (!(result instanceof RubyArray) || ((RubyArray) result).getLength() != 2) {
418             throw getRuntime().newTypeError("coerce must return [x, y]");
419     }
420         return ((RubyArray) result).getList();
421     }
422
423     /** rb_num_coerce_bin
424      * coercion taking two arguments
425      */

426     protected final IRubyObject coerceBin(String JavaDoc method, IRubyObject other) {
427         List JavaDoc list = doCoerce(other, true);
428         return ((RubyObject) list.get(0))
429                 .callMethod(getRuntime().getCurrentContext(), method, (RubyObject) list.get(1));
430     }
431     
432     /** rb_num_coerce_cmp
433      * coercion used for comparisons
434      */

435     protected final IRubyObject coerceCmp(String JavaDoc method, IRubyObject other) {
436         List JavaDoc list = doCoerce(other, false);
437         if (list == null) {
438             return getRuntime().getNil(); // MRI does it!
439
}
440         return ((RubyObject) list.get(0))
441                 .callMethod(getRuntime().getCurrentContext(), method, (RubyObject) list.get(1));
442     }
443         
444     /** rb_num_coerce_relop
445      * coercion used for relative operators
446      */

447     protected final IRubyObject coerceRelOp(String JavaDoc method, IRubyObject other) {
448         List JavaDoc list = doCoerce(other, false);
449         if (list != null) {
450             IRubyObject result = ((RubyObject) list.get(0)).callMethod(getRuntime().getCurrentContext(), method,
451                     (RubyObject) list.get(1));
452             if (!result.isNil()) {
453                 return result;
454     }
455         }
456     
457         RubyComparable.cmperr(this, other); // MRI also does it that way
458
return null; // not reachd as in MRI
459
}
460         
461     public RubyNumeric asNumeric() {
462         return this;
463     }
464
465     /* ================
466      * Instance Methods
467      * ================
468      */

469
470     /** num_sadded
471      *
472      */

473     public IRubyObject sadded(IRubyObject name) {
474         throw getRuntime().newTypeError("can't define singleton method " + name + " for " + getType().getName());
475         }
476         
477     /** num_init_copy
478      *
479      */

480     public IRubyObject init_copy(IRubyObject arg) {
481         throw getRuntime().newTypeError("can't copy " + getType().getName());
482     }
483     
484     /** num_coerce
485      *
486      */

487     public IRubyObject coerce(IRubyObject other) {
488         if (getMetaClass() == other.getMetaClass()) {
489             return getRuntime().newArray(other, this);
490         }
491         
492         return getRuntime().newArray(other.convertToFloat(), convertToFloat());
493     }
494
495     /** num_uplus
496      *
497      */

498     public IRubyObject uplus() {
499         return this;
500     }
501
502     /** num_uminus
503      *
504      */

505     public IRubyObject uminus() {
506         RubyFixnum zero = RubyFixnum.zero(getRuntime());
507         List JavaDoc list = zero.doCoerce(this, true);
508         return ((RubyObject) list.get(0)).callMethod(getRuntime().getCurrentContext(), "-", (RubyObject) list.get(1));
509     }
510     
511     /** num_cmp
512      *
513      */

514     public IRubyObject cmp(IRubyObject other) {
515         if (this == other) { // won't hurt fixnums
516
return RubyFixnum.zero(getRuntime());
517         }
518         return getRuntime().getNil();
519     }
520
521     /** num_eql
522      *
523      */

524     public IRubyObject eql_p(IRubyObject other) {
525         if (getMetaClass() != other.getMetaClass()) {
526             return getRuntime().getFalse();
527                 }
528         return super.equal(other);
529             }
530             
531     /** num_quo
532      *
533      */

534     public IRubyObject quo(IRubyObject other) {
535         return callMethod(getRuntime().getCurrentContext(), "/", other);
536         }
537
538     /** num_div
539      *
540      */

541     public IRubyObject div(IRubyObject other) {
542         return callMethod(getRuntime().getCurrentContext(), "/", other).convertToFloat().floor();
543     }
544
545     /** num_divmod
546      *
547      */

548     public IRubyObject divmod(IRubyObject other) {
549         IRubyObject cdr = callMethod(getRuntime().getCurrentContext(), "%", other);
550         IRubyObject car = div(other);
551         return RubyArray.newArray(getRuntime(), car, cdr);
552
553     }
554
555     /** num_modulo
556      *
557      */

558     public IRubyObject modulo(IRubyObject other) {
559         return callMethod(getRuntime().getCurrentContext(), "%", other);
560         }
561         
562     /** num_remainder
563      *
564      */

565     public IRubyObject remainder(IRubyObject y) {
566             ThreadContext context = getRuntime().getCurrentContext();
567         IRubyObject z = callMethod(context, "%", y);
568         IRubyObject x = this;
569         RubyFixnum zero = RubyFixnum.zero(getRuntime());
570
571         if (!(((RubyNumeric)z).equal(zero).isTrue())
572             && ((x.callMethod(context, "<", zero)).isTrue()
573                 && (y.callMethod(context, ">", zero)).isTrue())
574             || ((x.callMethod(context, ">", zero)).isTrue()
575                 && (y.callMethod(context, "<", zero)).isTrue())) {
576             return z.callMethod(context, "-",y);
577         }
578
579         return z;
580             }
581
582     /** num_abs
583      *
584      */

585     public IRubyObject abs() {
586         ThreadContext context = getRuntime().getCurrentContext();
587         if (callMethod(context, "<", RubyFixnum.zero(getRuntime())).isTrue()) {
588             return (RubyNumeric) callMethod(context, "-@");
589         }
590         return this;
591     }
592         
593     /** num_to_int
594      *
595      */

596     public IRubyObject to_int() {
597         return callMethod(getRuntime().getCurrentContext(), "to_i", IRubyObject.NULL_ARRAY);
598     }
599
600     /** num_int_p
601      *
602      */

603     public IRubyObject int_p() {
604         return getRuntime().getFalse();
605     }
606     
607     /** num_zero_p
608      *
609      */

610     public IRubyObject zero_p() {
611         // Will always return a boolean
612
return equal(RubyFixnum.zero(getRuntime())).isTrue() ? getRuntime().getTrue() : getRuntime().getFalse();
613     }
614     
615     /** num_nonzero_p
616      *
617      */

618     public IRubyObject nonzero_p() {
619         if (callMethod(getRuntime().getCurrentContext(), "zero?").isTrue()) {
620             return getRuntime().getNil();
621         }
622         return this;
623     }
624
625     /** num_floor
626      *
627      */

628     public IRubyObject floor() {
629         return convertToFloat().floor();
630         }
631         
632     /** num_ceil
633      *
634      */

635     public IRubyObject ceil() {
636         return convertToFloat().ceil();
637     }
638
639     /** num_round
640      *
641      */

642     public IRubyObject round() {
643         return convertToFloat().round();
644         }
645
646     /** num_truncate
647      *
648      */

649     public IRubyObject truncate() {
650         return convertToFloat().truncate();
651     }
652     
653     public IRubyObject step(IRubyObject[] args, Block block) {
654         IRubyObject to;
655         IRubyObject step;
656         
657         if(args.length == 1){
658             to = args[0];
659             step = RubyFixnum.one(getRuntime());
660         } else if (args.length == 2) {
661             to = args[0];
662             step = args[1];
663         }else{
664             throw getRuntime().newTypeError("wrong number of arguments");
665         }
666         
667         ThreadContext context = getRuntime().getCurrentContext();
668         if (this instanceof RubyFixnum && to instanceof RubyFixnum && step instanceof RubyFixnum) {
669             long value = getLongValue();
670             long end = ((RubyFixnum) to).getLongValue();
671             long diff = ((RubyFixnum) step).getLongValue();
672
673             if (diff == 0) {
674                 throw getRuntime().newArgumentError("step cannot be 0");
675             }
676             if (diff > 0) {
677                 for (long i = value; i <= end; i += diff) {
678                     context.yield(RubyFixnum.newFixnum(getRuntime(), i), block);
679                 }
680             } else {
681                 for (long i = value; i >= end; i += diff) {
682                     context.yield(RubyFixnum.newFixnum(getRuntime(), i), block);
683                 }
684             }
685         } else if (this instanceof RubyFloat || to instanceof RubyFloat || step instanceof RubyFloat) {
686             double beg = num2dbl(this);
687             double end = num2dbl(to);
688             double unit = num2dbl(step);
689
690             if (unit == 0) {
691                 throw getRuntime().newArgumentError("step cannot be 0");
692             }
693             
694             double n = (end - beg)/unit;
695             double err = (Math.abs(beg) + Math.abs(end) + Math.abs(end - beg)) / Math.abs(unit) * DBL_EPSILON;
696             
697             if (err>0.5) {
698                 err=0.5;
699             }
700             n = Math.floor(n + err) + 1;
701             
702             for(double i = 0; i < n; i++){
703                 context.yield(RubyFloat.newFloat(getRuntime(), i * unit + beg), block);
704             }
705
706         } else {
707             RubyNumeric i = this;
708             
709             String JavaDoc cmp;
710             if (((RubyBoolean) step.callMethod(context, ">", RubyFixnum.zero(getRuntime()))).isTrue()) {
711                 cmp = ">";
712             } else {
713                 cmp = "<";
714                 }
715
716             while (true) {
717                 if (i.callMethod(context, cmp, to).isTrue()) {
718                     break;
719                 }
720                 context.yield(i, block);
721                 i = (RubyNumeric) i.callMethod(context, "+", step);
722             }
723         }
724         return this;
725     }
726     
727     // /** num_equal
728
// *
729
// */
730
// public RubyBoolean veryEqual(IRubyObject other) {
731
// IRubyObject truth = super.equal(other);
732
//
733
// return truth == getRuntime().getNil() ? getRuntime().getFalse() : (RubyBoolean) truth;
734
// }
735
//
736
/** num_equal
737      *
738      */

739     public IRubyObject equal(IRubyObject other) {
740         if (this == other) { // it won't hurt fixnums
741
return getRuntime().getTrue();
742     }
743
744         return other.callMethod(getRuntime().getCurrentContext(), "==", this);
745     }
746
747     public static class InvalidIntegerException extends NumberFormatException JavaDoc {
748         private static final long serialVersionUID = 55019452543252148L;
749         
750         public InvalidIntegerException() {
751             super();
752         }
753         public InvalidIntegerException(String JavaDoc message) {
754             super(message);
755         }
756     }
757     
758     public static class NumberTooLargeException extends NumberFormatException JavaDoc {
759         private static final long serialVersionUID = -1835120694982699449L;
760         public NumberTooLargeException() {
761             super();
762         }
763         public NumberTooLargeException(String JavaDoc message) {
764             super(message);
765         }
766         
767     }
768     
769 }
770
Popular Tags