Imported Upstream version 1.46.0
[platform/upstream/nghttp2.git] / third-party / mruby / src / numeric.c
index 4128ea3..49d30c1 100644 (file)
@@ -44,6 +44,15 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
   }
   return mrb_float(val);
 }
+
+MRB_API mrb_value
+mrb_int_value(mrb_state *mrb, mrb_float f)
+{
+  if (FIXABLE_FLOAT(f)) {
+    return mrb_fixnum_value((mrb_int)f);
+  }
+  return mrb_float_value(mrb, f);
+}
 #endif
 
 /*
@@ -56,14 +65,13 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
  *    2.0**3      #=> 8.0
  */
 static mrb_value
-num_pow(mrb_state *mrb, mrb_value x)
+integral_pow(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 #ifndef MRB_WITHOUT_FLOAT
   mrb_float d;
 #endif
 
-  mrb_get_args(mrb, "o", &y);
   if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) {
     /* try ipow() */
     mrb_int base = mrb_fixnum(x);
@@ -103,6 +111,24 @@ num_pow(mrb_state *mrb, mrb_value x)
 #endif
 }
 
+static mrb_value
+integral_idiv(mrb_state *mrb, mrb_value x)
+{
+#ifdef MRB_WITHOUT_FLOAT
+  mrb_value y = mrb_get_arg1(mrb);
+
+  if (!mrb_fixnum_p(y)) {
+    mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+  }
+  return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+#else
+  mrb_float y;
+
+  mrb_get_args(mrb, "f", &y);
+  return mrb_int_value(mrb, mrb_to_flo(mrb, x) / y);
+#endif
+}
+
 /* 15.2.8.3.4  */
 /* 15.2.9.3.4  */
 /*
@@ -114,19 +140,6 @@ num_pow(mrb_state *mrb, mrb_value x)
  * result.
  */
 
-mrb_value
-mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
-{
-#ifdef MRB_WITHOUT_FLOAT
-  if (!mrb_fixnum_p(y)) {
-    mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
-  }
-  return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
-#else
-  return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
-#endif
-}
-
 /* 15.2.9.3.19(x) */
 /*
  *  call-seq:
@@ -136,12 +149,11 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
  */
 
 static mrb_value
-num_div(mrb_state *mrb, mrb_value x)
+integral_div(mrb_state *mrb, mrb_value x)
 {
 #ifdef MRB_WITHOUT_FLOAT
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   if (!mrb_fixnum_p(y)) {
     mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
   }
@@ -155,19 +167,19 @@ num_div(mrb_state *mrb, mrb_value x)
 }
 
 static mrb_value
-num_coerce_step_counter(mrb_state *mrb, mrb_value self)
+integral_coerce_step_counter(mrb_state *mrb, mrb_value self)
 {
-  mrb_value counter = self, num, step;
+  mrb_value num, step;
 
   mrb_get_args(mrb, "oo", &num, &step);
 
 #ifndef MRB_WITHOUT_FLOAT
   if (mrb_float_p(self) || mrb_float_p(num) || mrb_float_p(step)) {
-    counter = mrb_funcall(mrb, counter, "to_f", 0);
+    return mrb_Float(mrb, self);
   }
 #endif
 
-  return counter;
+  return self;
 }
 
 #ifndef MRB_WITHOUT_FLOAT
@@ -195,26 +207,30 @@ static mrb_value
 flo_to_s(mrb_state *mrb, mrb_value flt)
 {
   mrb_float f = mrb_float(flt);
+  mrb_value str;
 
   if (isinf(f)) {
-    return f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
-                 : mrb_str_new_lit(mrb, "Infinity");
+    str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
+                : mrb_str_new_lit(mrb, "Infinity");
+    goto exit;
   }
   else if (isnan(f)) {
-    return mrb_str_new_lit(mrb, "NaN");
+    str = mrb_str_new_lit(mrb, "NaN");
+    goto exit;
   }
   else {
     char fmt[] = "%." MRB_STRINGIZE(FLO_TO_STR_PREC) "g";
-    mrb_value str = mrb_float_to_str(mrb, flt, fmt);
     mrb_int len;
-    char *begp;
+    char *begp, *p, *endp;
+
+    str = mrb_float_to_str(mrb, flt, fmt);
 
     insert_dot_zero:
     begp = RSTRING_PTR(str);
     len = RSTRING_LEN(str);
-    for (char *p = begp, *endp = p + len; p < endp; ++p) {
+    for (p = begp, endp = p + len; p < endp; ++p) {
       if (*p == '.') {
-        return str;
+        goto exit;
       }
       else if (*p == 'e') {
         ptrdiff_t e_pos = p - begp;
@@ -222,7 +238,7 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
         p = RSTRING_PTR(str) + e_pos;
         memmove(p + 2, p, len - e_pos);
         memcpy(p, ".0", 2);
-        return str;
+        goto exit;
       }
     }
 
@@ -231,12 +247,13 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
       str = mrb_float_to_str(mrb, flt, fmt);
       goto insert_dot_zero;
     }
-    else {
-      mrb_str_cat(mrb, str, ".0", 2);
-    }
 
-    return str;
+    goto exit;
   }
+
+  exit:
+  RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+  return str;
 }
 
 /* 15.2.9.3.2  */
@@ -251,9 +268,8 @@ flo_to_s(mrb_state *mrb, mrb_value flt)
 static mrb_value
 flo_minus(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
 }
 
@@ -269,9 +285,8 @@ flo_minus(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_mul(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
 }
 
@@ -305,6 +320,8 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
     div = (x - mod) / y;
     if (modp && divp) div = round(div);
   }
+  if (div == 0) div = 0.0;
+  if (mod == 0) mod = 0.0;
   if (y*mod < 0) {
     mod += y;
     div -= 1.0;
@@ -329,11 +346,9 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
 static mrb_value
 flo_mod(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
   mrb_float mod;
 
-  mrb_get_args(mrb, "o", &y);
-
   flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
   return mrb_float_value(mrb, mod);
 }
@@ -354,9 +369,8 @@ flo_mod(mrb_state *mrb, mrb_value x)
 static mrb_value
 fix_eql(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   if (!mrb_fixnum_p(y)) return mrb_false_value();
   return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
 }
@@ -365,9 +379,8 @@ fix_eql(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_eql(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   if (!mrb_float_p(y)) return mrb_false_value();
   return mrb_bool_value(mrb_float(x) == mrb_float(y));
 }
@@ -388,8 +401,7 @@ flo_eql(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_eq(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
-  mrb_get_args(mrb, "o", &y);
+  mrb_value y = mrb_get_arg1(mrb);
 
   switch (mrb_type(y)) {
   case MRB_TT_FIXNUM:
@@ -407,7 +419,6 @@ value_int64(mrb_state *mrb, mrb_value x)
   switch (mrb_type(x)) {
   case MRB_TT_FIXNUM:
     return (int64_t)mrb_fixnum(x);
-    break;
   case MRB_TT_FLOAT:
     return (int64_t)mrb_float(x);
   default:
@@ -421,7 +432,7 @@ value_int64(mrb_state *mrb, mrb_value x)
 static mrb_value
 int64_value(mrb_state *mrb, int64_t v)
 {
-  if (FIXABLE(v)) {
+  if (TYPED_FIXABLE(v,int64_t)) {
     return mrb_fixnum_value((mrb_int)v);
   }
   return mrb_float_value(mrb, (mrb_float)v);
@@ -431,7 +442,6 @@ static mrb_value
 flo_rev(mrb_state *mrb, mrb_value x)
 {
   int64_t v1;
-  mrb_get_args(mrb, "");
   v1 = (int64_t)mrb_float(x);
   return int64_value(mrb, ~v1);
 }
@@ -439,9 +449,8 @@ flo_rev(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_and(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
   int64_t v1, v2;
-  mrb_get_args(mrb, "o", &y);
 
   v1 = (int64_t)mrb_float(x);
   v2 = value_int64(mrb, y);
@@ -451,9 +460,8 @@ flo_and(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_or(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
   int64_t v1, v2;
-  mrb_get_args(mrb, "o", &y);
 
   v1 = (int64_t)mrb_float(x);
   v2 = value_int64(mrb, y);
@@ -463,9 +471,8 @@ flo_or(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_xor(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
   int64_t v1, v2;
-  mrb_get_args(mrb, "o", &y);
 
   v1 = (int64_t)mrb_float(x);
   v2 = value_int64(mrb, y);
@@ -484,6 +491,10 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
   if (width < 0) {
     while (width++) {
       val /= 2;
+      if (val < 1.0) {
+        val = 0;
+        break;
+      }
     }
 #if defined(_ISOC99_SOURCE)
     val = trunc(val);
@@ -503,14 +514,11 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
       val *= 2;
     }
   }
-  if (FIXABLE_FLOAT(val)) {
-    return mrb_fixnum_value((mrb_int)val);
-  }
-  return mrb_float_value(mrb, val);
+  return mrb_int_value(mrb, val);
 }
 
 static mrb_value
-flo_lshift(mrb_state *mrb, mrb_value x)
+flo_rshift(mrb_state *mrb, mrb_value x)
 {
   mrb_int width;
 
@@ -519,7 +527,7 @@ flo_lshift(mrb_state *mrb, mrb_value x)
 }
 
 static mrb_value
-flo_rshift(mrb_state *mrb, mrb_value x)
+flo_lshift(mrb_state *mrb, mrb_value x)
 {
   mrb_int width;
 
@@ -612,10 +620,7 @@ flo_floor(mrb_state *mrb, mrb_value num)
   mrb_float f = floor(mrb_float(num));
 
   mrb_check_num_exact(mrb, f);
-  if (!FIXABLE_FLOAT(f)) {
-    return mrb_float_value(mrb, f);
-  }
-  return mrb_fixnum_value((mrb_int)f);
+  return mrb_int_value(mrb, f);
 }
 
 /* 15.2.9.3.8  */
@@ -638,10 +643,7 @@ flo_ceil(mrb_state *mrb, mrb_value num)
   mrb_float f = ceil(mrb_float(num));
 
   mrb_check_num_exact(mrb, f);
-  if (!FIXABLE_FLOAT(f)) {
-    return mrb_float_value(mrb, f);
-  }
-  return mrb_fixnum_value((mrb_int)f);
+  return mrb_int_value(mrb, f);
 }
 
 /* 15.2.9.3.12 */
@@ -692,6 +694,7 @@ flo_round(mrb_state *mrb, mrb_value num)
 
   f = 1.0;
   i = ndigits >= 0 ? ndigits : -ndigits;
+  if (ndigits > DBL_DIG+2) return num;
   while  (--i >= 0)
     f = f*10.0;
 
@@ -722,7 +725,7 @@ flo_round(mrb_state *mrb, mrb_value num)
     if (!isfinite(number)) return num;
     return mrb_float_value(mrb, number);
   }
-  return mrb_fixnum_value((mrb_int)number);
+  return mrb_int_value(mrb, number);
 }
 
 /* 15.2.9.3.14 */
@@ -744,10 +747,7 @@ flo_truncate(mrb_state *mrb, mrb_value num)
   if (f < 0.0) f = ceil(f);
 
   mrb_check_num_exact(mrb, f);
-  if (!FIXABLE_FLOAT(f)) {
-    return mrb_float_value(mrb, f);
-  }
-  return mrb_fixnum_value((mrb_int)f);
+  return mrb_int_value(mrb, f);
 }
 
 static mrb_value
@@ -780,8 +780,8 @@ int_to_i(mrb_state *mrb, mrb_value num)
   return num;
 }
 
-mrb_value
-mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
 {
   mrb_int a;
 
@@ -805,6 +805,21 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
 #endif
 }
 
+MRB_API mrb_value
+mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+  if (mrb_fixnum_p(x)) {
+    return fixnum_mul(mrb, x, y);
+  }
+#ifndef MRB_WITHOUT_FLOAT
+  if (mrb_float_p(x)) {
+    return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
+  }
+#endif
+  mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
+  return mrb_nil_value();       /* not reached */
+}
+
 /* 15.2.8.3.3  */
 /*
  * call-seq:
@@ -818,10 +833,9 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
 static mrb_value
 fix_mul(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
-  return mrb_fixnum_mul(mrb, x, y);
+  return fixnum_mul(mrb, x, y);
 }
 
 static void
@@ -865,23 +879,24 @@ fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
 static mrb_value
 fix_mod(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
-  mrb_int a;
+  mrb_value y = mrb_get_arg1(mrb);
+  mrb_int a, b;
 
-  mrb_get_args(mrb, "o", &y);
   a = mrb_fixnum(x);
-  if (mrb_fixnum_p(y)) {
-    mrb_int b, mod;
+   if (mrb_fixnum_p(y) && a != MRB_INT_MIN && (b=mrb_fixnum(y)) != MRB_INT_MIN) {
+    mrb_int mod;
 
-    if ((b=mrb_fixnum(y)) == 0) {
+    if (b == 0) {
 #ifdef MRB_WITHOUT_FLOAT
       /* ZeroDivisionError */
       return mrb_fixnum_value(0);
 #else
+      if (a > 0) return mrb_float_value(mrb, INFINITY);
+      if (a < 0) return mrb_float_value(mrb, INFINITY);
       return mrb_float_value(mrb, NAN);
 #endif
     }
-    fixdivmod(mrb, a, b, 0, &mod);
+    fixdivmod(mrb, a, b, NULL, &mod);
     return mrb_fixnum_value(mod);
   }
 #ifdef MRB_WITHOUT_FLOAT
@@ -890,7 +905,7 @@ fix_mod(mrb_state *mrb, mrb_value x)
   else {
     mrb_float mod;
 
-    flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod);
+    flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), NULL, &mod);
     return mrb_float_value(mrb, mod);
   }
 #endif
@@ -905,9 +920,7 @@ fix_mod(mrb_state *mrb, mrb_value x)
 static mrb_value
 fix_divmod(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
-
-  mrb_get_args(mrb, "o", &y);
+  mrb_value y = mrb_get_arg1(mrb);
 
   if (mrb_fixnum_p(y)) {
     mrb_int div, mod;
@@ -933,7 +946,7 @@ fix_divmod(mrb_state *mrb, mrb_value x)
     mrb_value a, b;
 
     flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
-    a = mrb_float_value(mrb, div);
+    a = mrb_int_value(mrb, div);
     b = mrb_float_value(mrb, mod);
     return mrb_assoc_new(mrb, a, b);
   }
@@ -944,14 +957,12 @@ fix_divmod(mrb_state *mrb, mrb_value x)
 static mrb_value
 flo_divmod(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
   mrb_float div, mod;
   mrb_value a, b;
 
-  mrb_get_args(mrb, "o", &y);
-
   flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
-  a = mrb_float_value(mrb, div);
+  a = mrb_int_value(mrb, div);
   b = mrb_float_value(mrb, mod);
   return mrb_assoc_new(mrb, a, b);
 }
@@ -972,9 +983,8 @@ flo_divmod(mrb_state *mrb, mrb_value x)
 static mrb_value
 fix_equal(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   switch (mrb_type(y)) {
   case MRB_TT_FIXNUM:
     return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y));
@@ -1031,9 +1041,8 @@ static mrb_value flo_xor(mrb_state *mrb, mrb_value x);
 static mrb_value
 fix_and(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   bit_op(x, y, and, &);
 }
 
@@ -1048,9 +1057,8 @@ fix_and(mrb_state *mrb, mrb_value x)
 static mrb_value
 fix_or(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   bit_op(x, y, or, |);
 }
 
@@ -1065,9 +1073,8 @@ fix_or(mrb_state *mrb, mrb_value x)
 static mrb_value
 fix_xor(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   bit_op(x, y, or, ^);
 }
 
@@ -1096,7 +1103,7 @@ lshift(mrb_state *mrb, mrb_int val, mrb_int width)
   }
   else {
     if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
-        (val   < (MRB_INT_MIN >> width))) {
+        (val   <= (MRB_INT_MIN >> width))) {
 #ifdef MRB_WITHOUT_FLOAT
       return mrb_fixnum_value(0);
 #else
@@ -1230,15 +1237,15 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
       z = (mrb_int)d;
     }
     else {
-      mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
+      mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x);
     }
   }
   return mrb_fixnum_value(z);
 }
 #endif
 
-mrb_value
-mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
 {
   mrb_int a;
 
@@ -1262,6 +1269,21 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
 #endif
 }
 
+MRB_API mrb_value
+mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+  if (mrb_fixnum_p(x)) {
+    return fixnum_plus(mrb, x, y);
+  }
+#ifndef MRB_WITHOUT_FLOAT
+  if (mrb_float_p(x)) {
+    return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
+  }
+#endif
+  mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
+  return mrb_nil_value();       /* not reached */
+}
+
 /* 15.2.8.3.1  */
 /*
  * call-seq:
@@ -1274,14 +1296,13 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
 static mrb_value
 fix_plus(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &other);
-  return mrb_fixnum_plus(mrb, self, other);
+  return fixnum_plus(mrb, self, other);
 }
 
-mrb_value
-mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
 {
   mrb_int a;
 
@@ -1304,6 +1325,21 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
 #endif
 }
 
+MRB_API mrb_value
+mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+  if (mrb_fixnum_p(x)) {
+    return fixnum_minus(mrb, x, y);
+  }
+#ifndef MRB_WITHOUT_FLOAT
+  if (mrb_float_p(x)) {
+    return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
+  }
+#endif
+  mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction");
+  return mrb_nil_value();       /* not reached */
+}
+
 /* 15.2.8.3.2  */
 /* 15.2.8.3.16 */
 /*
@@ -1317,10 +1353,9 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
 static mrb_value
 fix_minus(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &other);
-  return mrb_fixnum_minus(mrb, self, other);
+  return fixnum_minus(mrb, self, other);
 }
 
 
@@ -1330,9 +1365,10 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
   char buf[MRB_INT_BIT+1];
   char *b = buf + sizeof buf;
   mrb_int val = mrb_fixnum(x);
+  mrb_value str;
 
   if (base < 2 || 36 < base) {
-    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
+    mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
   }
 
   if (val == 0) {
@@ -1350,7 +1386,9 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
     } while (val /= base);
   }
 
-  return mrb_str_new(mrb, b, buf + sizeof(buf) - b);
+  str = mrb_str_new(mrb, b, buf + sizeof(buf) - b);
+  RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+  return str;
 }
 
 /* 15.2.8.3.25 */
@@ -1421,41 +1459,38 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
 /* 15.2.9.3.6  */
 /*
  * call-seq:
- *     self.f <=> other.f    => -1, 0, +1
+ *     self.f <=> other.f    => -1, 0, +1, or nil
  *             <  => -1
  *             =  =>  0
  *             >  => +1
  *  Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is
  *  less than, equal to, or greater than <i>numeric</i>. This is the
- *  basis for the tests in <code>Comparable</code>.
+ *  basis for the tests in <code>Comparable</code>. When the operands are
+ *  not comparable, it returns nil instead of raising an exception.
  */
 static mrb_value
-num_cmp(mrb_state *mrb, mrb_value self)
+integral_cmp(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
   mrb_int n;
 
-  mrb_get_args(mrb, "o", &other);
   n = cmpnum(mrb, self, other);
   if (n == -2) return mrb_nil_value();
   return mrb_fixnum_value(n);
 }
 
-static void
+static mrb_noreturn void
 cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
 {
-  mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %S with %S failed",
-             mrb_obj_value(mrb_class(mrb, v1)),
-             mrb_obj_value(mrb_class(mrb, v2)));
+  mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2);
 }
 
 static mrb_value
-num_lt(mrb_state *mrb, mrb_value self)
+integral_lt(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
   mrb_int n;
 
-  mrb_get_args(mrb, "o", &other);
   n = cmpnum(mrb, self, other);
   if (n == -2) cmperr(mrb, self, other);
   if (n < 0) return mrb_true_value();
@@ -1463,12 +1498,11 @@ num_lt(mrb_state *mrb, mrb_value self)
 }
 
 static mrb_value
-num_le(mrb_state *mrb, mrb_value self)
+integral_le(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
   mrb_int n;
 
-  mrb_get_args(mrb, "o", &other);
   n = cmpnum(mrb, self, other);
   if (n == -2) cmperr(mrb, self, other);
   if (n <= 0) return mrb_true_value();
@@ -1476,12 +1510,11 @@ num_le(mrb_state *mrb, mrb_value self)
 }
 
 static mrb_value
-num_gt(mrb_state *mrb, mrb_value self)
+integral_gt(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
   mrb_int n;
 
-  mrb_get_args(mrb, "o", &other);
   n = cmpnum(mrb, self, other);
   if (n == -2) cmperr(mrb, self, other);
   if (n > 0) return mrb_true_value();
@@ -1489,29 +1522,47 @@ num_gt(mrb_state *mrb, mrb_value self)
 }
 
 static mrb_value
-num_ge(mrb_state *mrb, mrb_value self)
+integral_ge(mrb_state *mrb, mrb_value self)
 {
-  mrb_value other;
+  mrb_value other = mrb_get_arg1(mrb);
   mrb_int n;
 
-  mrb_get_args(mrb, "o", &other);
   n = cmpnum(mrb, self, other);
   if (n == -2) cmperr(mrb, self, other);
   if (n >= 0) return mrb_true_value();
   return mrb_false_value();
 }
 
+MRB_API mrb_int
+mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
+{
+  mrb_value v;
+
+  switch (mrb_type(obj1)) {
+  case MRB_TT_FIXNUM:
+  case MRB_TT_FLOAT:
+    return cmpnum(mrb, obj1, obj2);
+  case MRB_TT_STRING:
+    if (!mrb_string_p(obj2))
+      return -2;
+    return mrb_str_cmp(mrb, obj1, obj2);
+  default:
+    v = mrb_funcall(mrb, obj1, "<=>", 1, obj2);
+    if (mrb_nil_p(v) || !mrb_fixnum_p(v))
+      return -2;
+    return mrb_fixnum(v);
+  }
+}
+
 static mrb_value
 num_finite_p(mrb_state *mrb, mrb_value self)
 {
-  mrb_get_args(mrb, "");
   return mrb_true_value();
 }
 
 static mrb_value
 num_infinite_p(mrb_state *mrb, mrb_value self)
 {
-  mrb_get_args(mrb, "");
   return mrb_false_value();
 }
 
@@ -1527,9 +1578,8 @@ num_infinite_p(mrb_state *mrb, mrb_value self)
 static mrb_value
 flo_plus(mrb_state *mrb, mrb_value x)
 {
-  mrb_value y;
+  mrb_value y = mrb_get_arg1(mrb);
 
-  mrb_get_args(mrb, "o", &y);
   return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
 }
 #endif
@@ -1544,21 +1594,21 @@ mrb_init_numeric(mrb_state *mrb)
 #endif
 
   integral = mrb_define_module(mrb, "Integral");
+  mrb_define_method(mrb, integral,"**",       integral_pow,    MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,"/",        integral_div,    MRB_ARGS_REQ(1)); /* 15.2.{8,9}.3.6  */
+  mrb_define_method(mrb, integral,"quo",      integral_div,    MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+  mrb_define_method(mrb, integral,"div",      integral_idiv,   MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,"<=>",      integral_cmp,    MRB_ARGS_REQ(1)); /* 15.2.{8,9}.3.1  */
+  mrb_define_method(mrb, integral,"<",        integral_lt,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,"<=",       integral_le,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,">",        integral_gt,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,">=",       integral_ge,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, integral,"__coerce_step_counter", integral_coerce_step_counter, MRB_ARGS_REQ(2));
 
   /* Numeric Class */
   numeric = mrb_define_class(mrb, "Numeric",  mrb->object_class);                /* 15.2.7 */
-
-  mrb_define_method(mrb, numeric, "**",       num_pow,         MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, numeric, "/",        num_div,         MRB_ARGS_REQ(1)); /* 15.2.8.3.4  */
-  mrb_define_method(mrb, numeric, "quo",      num_div,         MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
-  mrb_define_method(mrb, numeric, "<=>",      num_cmp,         MRB_ARGS_REQ(1)); /* 15.2.9.3.6  */
-  mrb_define_method(mrb, numeric, "<",        num_lt,          MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, numeric, "<=",       num_le,          MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, numeric, ">",        num_gt,          MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, numeric, ">=",       num_ge,          MRB_ARGS_REQ(1));
   mrb_define_method(mrb, numeric, "finite?",  num_finite_p,    MRB_ARGS_NONE());
   mrb_define_method(mrb, numeric, "infinite?",num_infinite_p,  MRB_ARGS_NONE());
-  mrb_define_method(mrb, numeric, "__coerce_step_counter", num_coerce_step_counter,  MRB_ARGS_REQ(2));
 
   /* Integer Class */
   integer = mrb_define_class(mrb, "Integer",  numeric);                          /* 15.2.8 */
@@ -1567,10 +1617,10 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_define_method(mrb, integer, "to_i",     int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.24 */
   mrb_define_method(mrb, integer, "to_int",   int_to_i,        MRB_ARGS_NONE());
 #ifndef MRB_WITHOUT_FLOAT
-  mrb_define_method(mrb, integer, "ceil",     int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */
-  mrb_define_method(mrb, integer, "floor",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */
-  mrb_define_method(mrb, integer, "round",    int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */
-  mrb_define_method(mrb, integer, "truncate", int_to_i,        MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */
+  mrb_define_method(mrb, integer, "ceil",     int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.8 (x) */
+  mrb_define_method(mrb, integer, "floor",    int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.10 (x) */
+  mrb_define_method(mrb, integer, "round",    int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.12 (x) */
+  mrb_define_method(mrb, integer, "truncate", int_to_i,        MRB_ARGS_NONE()); /* 15.2.8.3.15 (x) */
 #endif
 
   /* Fixnum Class */
@@ -1590,8 +1640,8 @@ mrb_init_numeric(mrb_state *mrb)
 #ifndef MRB_WITHOUT_FLOAT
   mrb_define_method(mrb, fixnum,  "to_f",     fix_to_f,        MRB_ARGS_NONE()); /* 15.2.8.3.23 */
 #endif
-  mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,        MRB_ARGS_NONE()); /* 15.2.8.3.25 */
-  mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,        MRB_ARGS_NONE());
+  mrb_define_method(mrb, fixnum,  "to_s",     fix_to_s,        MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */
+  mrb_define_method(mrb, fixnum,  "inspect",  fix_to_s,        MRB_ARGS_OPT(1));
   mrb_define_method(mrb, fixnum,  "divmod",   fix_divmod,      MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
 
 #ifndef MRB_WITHOUT_FLOAT
@@ -1608,8 +1658,8 @@ mrb_init_numeric(mrb_state *mrb)
   mrb_define_method(mrb, fl,      "&",         flo_and,        MRB_ARGS_REQ(1));
   mrb_define_method(mrb, fl,      "|",         flo_or,         MRB_ARGS_REQ(1));
   mrb_define_method(mrb, fl,      "^",         flo_xor,        MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, fl,      ">>",        flo_lshift,     MRB_ARGS_REQ(1));
-  mrb_define_method(mrb, fl,      "<<",        flo_rshift,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      ">>",        flo_rshift,     MRB_ARGS_REQ(1));
+  mrb_define_method(mrb, fl,      "<<",        flo_lshift,     MRB_ARGS_REQ(1));
   mrb_define_method(mrb, fl,      "ceil",      flo_ceil,       MRB_ARGS_NONE()); /* 15.2.9.3.8  */
   mrb_define_method(mrb, fl,      "finite?",   flo_finite_p,   MRB_ARGS_NONE()); /* 15.2.9.3.9  */
   mrb_define_method(mrb, fl,      "floor",     flo_floor,      MRB_ARGS_NONE()); /* 15.2.9.3.10 */