-
-
Notifications
You must be signed in to change notification settings - Fork 373
/
Rational.pm
124 lines (111 loc) · 3.45 KB
/
Rational.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
my role Rational[::NuT, ::DeT] does Real {
has NuT $.numerator;
has DeT $.denominator;
multi method WHICH(Rational:D:) {
nqp::box_s(
nqp::concat_s(
nqp::concat_s(nqp::unbox_s(self.^name), '|'),
nqp::concat_s(
nqp::tostr_I($!numerator),
nqp::concat_s('/', nqp::tostr_I($!denominator))
)
),
ObjAt
);
}
method new(NuT \$nu = 0, DeT \$de = 1) {
my $new := nqp::create(self);
my $gcd := $nu gcd $de;
my $numerator = $nu div $gcd;
my $denominator = $de div $gcd;
if $denominator < 0 {
$numerator = -$numerator;
$denominator = -$denominator;
}
nqp::bindattr($new, self.WHAT, '$!numerator', nqp::p6decont($numerator));
nqp::bindattr($new, self.WHAT, '$!denominator', nqp::p6decont($denominator));
$new;
}
method nude() { $!numerator, $!denominator }
method Num() {
$!denominator == 0
?? ($!numerator < 0 ?? -$Inf !! $Inf)
!! nqp::p6box_n(nqp::div_In(
nqp::p6decont($!numerator),
nqp::p6decont($!denominator)
));
}
method floor(Rational:D:) returns Int:D {
$!denominator == 1
?? $!numerator
!! $!numerator < 0
?? ($!numerator div $!denominator - 1) # XXX because div for negati
!! $!numerator div $!denominator
}
method ceiling(Rational:D:) returns Int:D {
$!denominator == 1
?? $!numerator
!! $!numerator < 0
?? ($!numerator div $!denominator) # XXX should be +1, but div is buggy
!! ($!numerator div $!denominator + 1)
}
method Int() { $!numerator div $!denominator }
method Bridge() { self.Num }
multi method Str(::?CLASS:D:) {
my $s = $!numerator < 0 ?? '-' !! '';
my $r = self.abs;
my $i = $r.floor;
$r -= $i;
$s ~= $i;
if $r {
$s ~= '.';
my $want = $!denominator < 100_000
?? 6
!! $!denominator.Str.chars + 1;
my $f = '';
while $r and $f.chars < $want {
$r *= 10;
$i = $r.floor;
$f ~= $i;
$r -= $i;
}
$f++ if 2 * $r >= 1;
$s ~= $f;
}
$s;
}
method base($base) {
my $s = $!numerator < 0 ?? '-' !! '';
my $r = self.abs;
my $i = $r.floor;
$r -= $i;
$s ~= $i.base($base);
if $r {
my $want = $!denominator < $base**6 ?? 6 !! $!denominator.log($base).ceiling + 1;
my @f;
while $r and @f < $want {
$r *= $base;
$i = $r.floor;
push @f, $i;
$r -= $i;
}
if 2 * $r >= 1 {
for @f-1 ... 0 -> $x {
last if ++@f[$x] < $base;
@f[$x] = 0;
$s ~= ($i+1).base($base) if $x == 0; # never happens?
}
}
$s ~= '.';
$s ~= (0..9,'A'..'Z')[@f].join;
}
$s;
}
method succ {
self.new($!numerator + $!denominator, $!denominator);
}
method pred {
self.new($!numerator - $!denominator, $!denominator);
}
method norm() { self }
}