Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implement a simple algorithm to find repetition

  • Loading branch information...
commit 408d4a05c40a70a7d49e40d6edc33af98e6816a3 1 parent be2a7c4
Kenta Murata authored
2  bignum.c
View
@@ -5488,7 +5488,7 @@ rb_big_uminus(VALUE x)
* sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
*/
-static VALUE
+VALUE
rb_big_neg(VALUE x)
{
VALUE z = rb_big_clone(x);
1  include/ruby/intern.h
View
@@ -115,6 +115,7 @@ VALUE rb_big_unpack(unsigned long *buf, long num_longs);
int rb_uv_to_utf8(char[6],unsigned long);
VALUE rb_dbl2big(double);
double rb_big2dbl(VALUE);
+VALUE rb_big_neg(VALUE);
VALUE rb_big_cmp(VALUE, VALUE);
VALUE rb_big_eq(VALUE, VALUE);
VALUE rb_big_eql(VALUE, VALUE);
133 rational.c
View
@@ -1633,6 +1633,113 @@ f_format(VALUE self, VALUE (*func)(VALUE))
return s;
}
+static inline VALUE
+num_n_times(VALUE num, int n)
+{
+ assert(RB_TYPE_P(num, T_FIXNUM) || RB_TYPE_P(num, T_BIGNUM));
+
+ switch (TYPE(num)) {
+ case T_FIXNUM:
+ return LONG2NUM(n*FIX2LONG(num));
+
+ case T_BIGNUM:
+ return rb_big_mul(num, INT2FIX(n));
+
+ default:
+ break;
+ }
+
+ UNREACHABLE;
+}
+
+static VALUE
+nurat_to_decimal_str(VALUE self)
+{
+ long n, negative, j, k;
+ VALUE num, den, qr, q, r, rr, s;
+
+ get_dat1(self);
+ num = dat->num;
+ den = dat->den;
+
+ switch (TYPE(num)) {
+ case T_FIXNUM:
+ n = FIX2LONG(num);
+ negative = n < 0;
+ if (negative) {
+ num = LONG2FIX(-n);
+ }
+ break;
+
+ case T_BIGNUM:
+ negative = RBIGNUM_NEGATIVE_P(num);
+ if (negative) {
+ num = rb_big_neg(num);
+ }
+ break;
+ }
+
+ qr = rb_funcallv(num, rb_intern("divmod"), 1, &den);
+ q = RARRAY_AREF(qr, 0);
+ r = RARRAY_AREF(qr, 1);
+
+ s = rb_obj_as_string(q);
+ if (negative) {
+ s = rb_str_concat(rb_str_new2("-"), s);
+ }
+
+ if (f_zero_p(r)) return s;
+
+ rb_str_cat2(s, ".");
+
+ j = k = RSTRING_LEN(s) - 1;
+ rr = r;
+ while (!f_zero_p(r)) {
+ char digs[2];
+ long n;
+
+ r = num_n_times(r, 100);
+ qr = rb_funcallv(r, rb_intern("divmod"), 1, &dat->den);
+ q = RARRAY_AREF(qr, 0);
+ r = RARRAY_AREF(qr, 1);
+
+ n = FIX2LONG(q);
+ digs[0] = '0' + (n/10);
+ digs[1] = '0' + (n%10);
+ rb_str_cat(s, digs, 2);
+
+ if (f_zero_p(r)) {
+ if (RSTRING_PTR(s)[RSTRING_LEN(s) - 1] == '0') {
+ rb_str_set_len(s, RSTRING_LEN(s) - 1);
+ }
+ break;
+ }
+
+ rr = num_n_times(rr, 10);
+ qr = rb_funcallv(rr, rb_intern("divmod"), 1, &dat->den);
+ rr = RARRAY_AREF(qr, 1);
+
+ k += 2;
+ ++j;
+
+ if (f_eqeq_p(r, rr)) {
+ int i;
+
+ while (RSTRING_PTR(s)[j - 1] == RSTRING_PTR(s)[k - 1]) {
+ --j;
+ --k;
+ }
+ rb_str_set_len(s, k + 2);
+ MEMMOVE(RSTRING_PTR(s) + j + 1, RSTRING_PTR(s) + j, char, k - j);
+ RSTRING_PTR(s)[j] = '(';
+ RSTRING_PTR(s)[k + 1] = ')';
+ break;
+ }
+ }
+
+ return s;
+}
+
/*
* call-seq:
* rat.to_s -> string
@@ -1644,9 +1751,29 @@ f_format(VALUE self, VALUE (*func)(VALUE))
* Rational('1/2').to_s #=> "1/2"
*/
static VALUE
-nurat_to_s(VALUE self)
+nurat_to_s(int argc, VALUE* argv, VALUE self)
{
- return f_format(self, f_to_s);
+ if (argc == 0) {
+ goto rational;
+ }
+
+ if (argc > 1) {
+ rb_raise(rb_eArgError, "too long arguments");
+ }
+
+ Check_Type(argv[0], T_SYMBOL);
+
+ if (rb_intern("rational") == SYM2ID(argv[0])) {
+ rational:
+ return f_format(self, f_to_s);
+ }
+
+ if (rb_intern("decimal") == SYM2ID(argv[0])) {
+ return nurat_to_decimal_str(self);
+ }
+
+ rb_raise(rb_eArgError, "invalid argument");
+ UNREACHABLE;
}
/*
@@ -2595,7 +2722,7 @@ Init_Rational(void)
rb_define_method(rb_cRational, "hash", nurat_hash, 0);
- rb_define_method(rb_cRational, "to_s", nurat_to_s, 0);
+ rb_define_method(rb_cRational, "to_s", nurat_to_s, -1);
rb_define_method(rb_cRational, "inspect", nurat_inspect, 0);
rb_define_private_method(rb_cRational, "marshal_dump", nurat_marshal_dump, 0);
21 test/ruby/test_rational.rb
View
@@ -803,7 +803,26 @@ def test_to_s
assert_equal('-1/2', Rational(-1,2).to_s)
assert_equal('1/2', Rational(-1,-2).to_s)
assert_equal('-1/2', Rational(1,-2).to_s)
- assert_equal('1/2', Rational(-1,-2).to_s)
+
+ assert_equal('1/2', Rational(-1,-2).to_s(:rational))
+
+ assert_equal('0.5', Rational(1,2).to_s(:decimal))
+ assert_equal('-0.5', Rational(-1,2).to_s(:decimal))
+ assert_equal('-0.5', Rational(1,-2).to_s(:decimal))
+ assert_equal('0.5', Rational(-1,-2).to_s(:decimal))
+
+ pi30 = Rational(3141592653589793238462643383279502884197,
+ 1000000000000000000000000000000000000000)
+ pi30s = '3.141592653589793238462643383279502884197'
+ assert_equal(pi30s, pi30.to_s(:decimal))
+
+ r = Rational(1371742101508805200123450343, 11111111111111111111000000)
+ s = '123.456789(13579246801234509876)'
+ assert_equal(s, r.to_s(:decimal))
+
+ assert_raise(TypeError) { Rational(1, 2).to_s(1) }
+ assert_raise(ArgumentError) { Rational(1, 2).to_s(:foobar) }
+ assert_raise(ArgumentError) { Rational(1, 2).to_s(:decimal, :rational) }
end
def test_inspect
Please sign in to comment.
Something went wrong with that request. Please try again.